From 1e0ecc6c67fb4fa928b0d57da0342915e2c26532 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 14 Sep 2011 07:06:05 +0000 Subject: [PATCH 001/444] Creating branch 3.4 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1170456 13f79535-47bb-0310-9956-ffa450edef68 From f91e30d39d4a7772df5101ddcb374f1c8fed7a85 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Tue, 27 Sep 2011 01:07:35 +0000 Subject: [PATCH 002/444] ZOOKEEPER-1189. For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. (Rakesh R via mahadev) - Merging r1176144 from trunk git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1176153 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/server/persistence/Util.java | 10 ++++----- .../zookeeper/server/ZooKeeperServerTest.java | 22 ++++++++++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e391415ddc9..bd13498c657 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -325,6 +325,9 @@ BUGFIXES: ZOOKEEPER-1136. NEW_LEADER should be queued not sent to match the Zab 1.0 protocol on the twiki (breed via mahadev) + ZOOKEEPER-1189. For an invalid snapshot file(less than 10bytes size) RandomAccessFile + stream is leaking. (Rakesh R via mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/server/persistence/Util.java b/src/java/main/org/apache/zookeeper/server/persistence/Util.java index 6443434f4e1..7ef7f9c4144 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/Util.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/Util.java @@ -164,12 +164,12 @@ public static boolean isValidSnapshot(File f) throws IOException { // Check for a valid snapshot RandomAccessFile raf = new RandomAccessFile(f, "r"); - // including the header and the last / bytes - // the snapshot should be atleast 10 bytes - if (raf.length() < 10) { - return false; - } try { + // including the header and the last / bytes + // the snapshot should be atleast 10 bytes + if (raf.length() < 10) { + return false; + } raf.seek(raf.length() - 5); byte bytes[] = new byte[5]; int readlen = 0; diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerTest.java index 5a0759a9f6d..2d7aad17f7b 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerTest.java @@ -19,14 +19,15 @@ package org.apache.zookeeper.server; import java.io.File; +import java.io.IOException; import java.util.List; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.Util; +import org.apache.zookeeper.test.ClientBase; import org.junit.Assert; import org.junit.Test; -import org.junit.Assert; public class ZooKeeperServerTest extends ZKTestCase { @Test @@ -114,4 +115,23 @@ public void testForceSyncDefaultDisabled() { } } + @Test + public void testInvalidSnapshot() { + File f = null; + File tmpFileDir = null; + try { + tmpFileDir = ClientBase.createTmpDir(); + f = new File(tmpFileDir, "snapshot.0"); + if (!f.exists()) { + f.createNewFile(); + } + Assert.assertFalse("Snapshot file size is greater than 9 bytes", Util.isValidSnapshot(f)); + Assert.assertTrue("Can't delete file", f.delete()); + } catch (IOException e) { + } finally { + if (null != tmpFileDir) { + ClientBase.recursiveDelete(tmpFileDir); + } + } + } } From 3e0f530825ec613d4d527afb311f43e1d164298c Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Tue, 27 Sep 2011 02:09:13 +0000 Subject: [PATCH 003/444] ZOOKEEPER-1185. Send AuthFailed event to client if SASL authentication fails. (Eugene Kuntz via mahadev) - Merging r1176159 from trunk git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1176160 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/ClientCnxn.java | 12 ++++++++++++ .../zookeeper/client/ZooKeeperSaslClient.java | 13 ++++++++++--- .../org/apache/zookeeper/test/SaslAuthFailTest.java | 1 + .../org/apache/zookeeper/test/SaslAuthTest.java | 8 -------- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bd13498c657..0d47186ff8f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -328,6 +328,9 @@ BUGFIXES: ZOOKEEPER-1189. For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. (Rakesh R via mahadev) + ZOOKEEPER-1185. Send AuthFailed event to client if SASL authentication fails. + (Eugene Kuntz via mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 723efa1a1e8..3d03153651b 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -558,6 +558,12 @@ private void processEvent(Object event) { SetSASLResponse rsp = (SetSASLResponse) p.response; // TODO : check rc (== 0, etc) as with other packet types. cb.processResult(rc,null,p.ctx,rsp.getToken(),null); + ClientCnxn clientCnxn = (ClientCnxn)p.ctx; + if ((clientCnxn == null) || (clientCnxn.zooKeeperSaslClient == null) || + (clientCnxn.zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.FAILED)) { + queueEvent(new WatchedEvent(EventType.None, + KeeperState.AuthFailed, null)); + } } else if (p.response instanceof GetDataResponse) { DataCallback cb = (DataCallback) p.cb; GetDataResponse rsp = (GetDataResponse) p.response; @@ -938,6 +944,9 @@ private void startConnect() throws IOException { + "configuration file: '" + System.getProperty("java.security.auth.login.config") + "'. Will continue connection to Zookeeper server without SASL authentication, if Zookeeper " + "server allows it."); + eventThread.queueEvent(new WatchedEvent( + Watcher.Event.EventType.None, + KeeperState.AuthFailed, null)); } } clientCnxnSocket.connect(addr); @@ -979,6 +988,9 @@ public void run() { catch (SaslException e) { LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e); state = States.AUTH_FAILED; + eventThread.queueEvent(new WatchedEvent( + Watcher.Event.EventType.None, + KeeperState.AuthFailed,null)); } if (zooKeeperSaslClient.readyToSendSaslAuthEvent()) { eventThread.queueEvent(new WatchedEvent( diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 43382c8ddfd..722538ea3bf 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -59,12 +59,16 @@ public class ZooKeeperSaslClient { private byte[] saslToken = new byte[0]; private ClientCnxn cnxn; - private enum SaslState { - INITIAL,INTERMEDIATE,COMPLETE + public enum SaslState { + INITIAL,INTERMEDIATE,COMPLETE,FAILED } private SaslState saslState = SaslState.INITIAL; + public SaslState getSaslState() { + return saslState; + } + public ZooKeeperSaslClient(ClientCnxn cnxn, String serverPrincipal) throws LoginException { this.cnxn = cnxn; this.saslClient = createSaslClient(serverPrincipal); @@ -176,8 +180,8 @@ private void prepareSaslResponseToServer(byte[] serverToken) { queueSaslPacket(saslToken); } } catch (SaslException e) { - // TODO sendThread should set state to AUTH_FAILED; but currently only sendThread modifies state. LOG.error("SASL authentication failed."); + saslState = SaslState.FAILED; } } } @@ -265,6 +269,9 @@ public boolean readyToSendSaslAuthEvent() { } public void initialize() throws SaslException { + if (saslClient == null) { + throw new SaslException("saslClient failed to initialize properly: it's null."); + } if (saslState == SaslState.INITIAL) { if (saslClient.hasInitialResponse()) { queueSaslPacket(); diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java index 8de7c2a120d..79f0b5035c3 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java @@ -102,6 +102,7 @@ public synchronized void process(WatchedEvent event) { public void testBadSaslAuthNotifiesWatch() throws Exception { ZooKeeper zk = createClient(); Thread.sleep(1000); + Assert.assertEquals(authFailed.get(),1); zk.close(); } diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java index fd2034652c4..39955862d31 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java @@ -102,14 +102,6 @@ public synchronized void process(WatchedEvent event) { } } - @Test - public void testBadSaslAuthNotifiesWatch() throws Exception { - ZooKeeper zk = createClient(); - Thread.sleep(1000); - zk.close(); - } - - @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); From a787c99b69d19c1959378a3e03e36773584af53c Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Tue, 27 Sep 2011 15:06:41 +0000 Subject: [PATCH 004/444] ZOOKEEPER-1174. FD leak when network unreachable (Ted Dunning via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1176425 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../main/org/apache/zookeeper/ClientCnxn.java | 2 +- .../apache/zookeeper/ClientCnxnSocketNIO.java | 17 +++++++++++++---- .../org/apache/zookeeper/server/DataTree.java | 8 +++----- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0d47186ff8f..651948d95b2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -330,6 +330,8 @@ BUGFIXES: ZOOKEEPER-1185. Send AuthFailed event to client if SASL authentication fails. (Eugene Kuntz via mahadev) + + ZOOKEEPER-1174. FD leak when network unreachable (Ted Dunning via camille) IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 3d03153651b..1df1c9e2ccb 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -1040,7 +1040,7 @@ public void run() { clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue); - } catch (Exception e) { + } catch (Throwable e) { if (closing) { if (LOG.isDebugEnabled()) { // closing so this is expected diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java index 626da04e613..a73fdf11b62 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java @@ -34,7 +34,6 @@ import org.apache.zookeeper.ClientCnxn.EndOfStreamException; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.ZooDefs.OpCode; -import org.apache.zookeeper.ZooKeeper.States; public class ClientCnxnSocketNIO extends ClientCnxnSocket { private static final Logger LOG = LoggerFactory @@ -185,9 +184,15 @@ void connect(InetSocketAddress addr) throws IOException { sock.configureBlocking(false); sock.socket().setSoLinger(false, -1); sock.socket().setTcpNoDelay(true); - sockKey = sock.register(selector, SelectionKey.OP_CONNECT); - if (sock.connect(addr)) { - sendThread.primeConnection(); + try { + boolean immediateConnect = sock.connect(addr); + sockKey = sock.register(selector, SelectionKey.OP_CONNECT); + if (immediateConnect) { + sendThread.primeConnection(); + } + } catch (IOException e) { + LOG.error("Unable to open socket to " + addr); + sock.close(); } initialized = false; @@ -309,4 +314,8 @@ synchronized private void enableRead() { synchronized void enableReadWriteOnly() { sockKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } + + Selector getSelector() { + return selector; + } } diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index 3987c541e32..facfb388c86 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -209,7 +209,7 @@ public synchronized Long convertAcls(List acls) { /** * converts a list of longs to a list of acls. * - * @param longs + * @param longVal * the list of longs * @return a list of ACLs that map to longs */ @@ -630,7 +630,7 @@ public Stat setData(String path, byte data[], int version, long zxid, /** * If there is a quota set, return the appropriate prefix for that quota * Else return null - * @param The ZK path to check for quota + * @param path The ZK path to check for quota * @return Max quota prefix, or null if none */ public String getMaxPrefixWithQuota(String path) { @@ -971,9 +971,7 @@ private static class Counts { * * @param path * the path to be used - * @param bytes - * the long bytes - * @param count + * @param counts * the int count */ private void getCounts(String path, Counts counts) { From 76a17b6c360b1cccc7eea597d7b53afb171d90eb Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 27 Sep 2011 19:50:05 +0000 Subject: [PATCH 005/444] ZOOKEEPER-1203. Zookeeper systest is missing Junit Classes (Prashant Gokhale via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1176573 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/fatjar/build.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 651948d95b2..0b703525b87 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -333,6 +333,9 @@ BUGFIXES: ZOOKEEPER-1174. FD leak when network unreachable (Ted Dunning via camille) + ZOOKEEPER-1203. Zookeeper systest is missing Junit Classes + (Prashant Gokhale via phunt) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/contrib/fatjar/build.xml b/src/contrib/fatjar/build.xml index cd56844a799..8935f3940fe 100644 --- a/src/contrib/fatjar/build.xml +++ b/src/contrib/fatjar/build.xml @@ -46,6 +46,7 @@ + From a3d8b27678e27e8fec3172e5b5538807e0c191e5 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Wed, 28 Sep 2011 20:17:49 +0000 Subject: [PATCH 006/444] ZOOKEEPER-1174. FD leak when network unreachable (Ted Dunning via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1177043 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java index a73fdf11b62..b8ebe2ca5ad 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java @@ -185,8 +185,8 @@ void connect(InetSocketAddress addr) throws IOException { sock.socket().setSoLinger(false, -1); sock.socket().setTcpNoDelay(true); try { - boolean immediateConnect = sock.connect(addr); sockKey = sock.register(selector, SelectionKey.OP_CONNECT); + boolean immediateConnect = sock.connect(addr); if (immediateConnect) { sendThread.primeConnection(); } From e14e50ff4736224212bfc8f1ba038b595fc5759a Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Thu, 29 Sep 2011 07:41:34 +0000 Subject: [PATCH 007/444] ZOOKEEPER-1201. Clean SaslServerCallbackHandler.java. (Thomas Koch via mahadev) - Merging r1177191 from trunk git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1177192 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 +- .../auth/SaslServerCallbackHandler.java | 186 ++++++++---------- 2 files changed, 90 insertions(+), 100 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0b703525b87..363dc3eea57 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -443,7 +443,9 @@ IMPROVEMENTS: code (Thomas Koch via phunt) ZOOKEEPER-1171. fix build for java 7 (phunt via mahadev) - + + ZOOKEEPER-1201. Clean SaslServerCallbackHandler.java. (Thomas Koch + via mahadev) NEW FEATURES: ZOOKEEPER-729. Java client API to recursively delete a subtree. diff --git a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java index b3faa798954..fda0ed2f9f3 100644 --- a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java +++ b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import org.slf4j.Logger; @@ -36,125 +35,114 @@ import javax.security.sasl.RealmCallback; public class SaslServerCallbackHandler implements CallbackHandler { - private String userName = null; - private Map credentials = new HashMap(); - Logger LOG = LoggerFactory.getLogger(SaslServerCallbackHandler.class); + private static final String USER_PREFIX = "user_"; + private static final Logger LOG = LoggerFactory.getLogger(SaslServerCallbackHandler.class); + private static final String SYSPROP_SUPER_PASSWORD = "zookeeper.SASLAuthenticationProvider.superPassword"; + private static final String SYSPROP_REMOVE_HOST = "zookeeper.kerberos.removeHostFromPrincipal"; + private static final String SYSPROP_REMOVE_REALM = "zookeeper.kerberos.removeRealmFromPrincipal"; + + private String userName; + private final Map credentials = new HashMap(); public SaslServerCallbackHandler(Configuration configuration) throws IOException { AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry("Server"); if (configurationEntries == null) { - String errorMessage = "could not find a 'Server' entry in this configuration: server cannot start."; + String errorMessage = "Could not find a 'Server' entry in this configuration: Server cannot start."; LOG.error(errorMessage); - throw(new IOException(errorMessage)); + throw new IOException(errorMessage); } credentials.clear(); for(AppConfigurationEntry entry: configurationEntries) { Map options = entry.getOptions(); // Populate DIGEST-MD5 user -> password map with JAAS configuration entries from the "Server" section. // Usernames are distinguished from other options by prefixing the username with a "user_" prefix. - Iterator it = options.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - String key = (String)pair.getKey(); - if (key.substring(0,5).equals("user_")) { - String userName = key.substring(5); - credentials.put(userName,(String)pair.getValue()); - } + for(Map.Entry pair : options.entrySet()) { + String key = pair.getKey(); + if (key.startsWith(USER_PREFIX)) { + String userName = key.substring(USER_PREFIX.length()); + credentials.put(userName,(String)pair.getValue()); } } + } + } + + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + handleNameCallback((NameCallback) callback); + } else if (callback instanceof PasswordCallback) { + handlePasswordCallback((PasswordCallback) callback); + } else if (callback instanceof RealmCallback) { + handleRealmCallback((RealmCallback) callback); + } else if (callback instanceof AuthorizeCallback) { + handleAuthorizeCallback((AuthorizeCallback) callback); + } + } + } + + private void handleNameCallback(NameCallback nc) { + // check to see if this user is in the user password database. + if (credentials.get(nc.getDefaultName()) == null) { + LOG.warn("User '" + nc.getDefaultName() + "' not found in list of DIGEST-MD5 authenticateable users."); return; } + nc.setName(nc.getDefaultName()); + userName = nc.getDefaultName(); + } - public void handle(Callback[] callbacks) throws - UnsupportedCallbackException { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback) { - NameCallback nc = (NameCallback) callback; - // check to see if this user is in the user password database. - if (credentials.get(nc.getDefaultName()) != null) { - nc.setName(nc.getDefaultName()); - this.userName = nc.getDefaultName(); - } - else { // no such user. - LOG.warn("User '" + nc.getDefaultName() + "' not found in list of DIGEST-MD5 authenticateable users."); - } - } - else { - if (callback instanceof PasswordCallback) { - PasswordCallback pc = (PasswordCallback) callback; - - if ((this.userName.equals("super") - && - (System.getProperty("zookeeper.SASLAuthenticationProvider.superPassword") != null))) { - // superuser: use Java system property for password, if available. - pc.setPassword(System.getProperty("zookeeper.SASLAuthenticationProvider.superPassword").toCharArray()); - } - else { - if (this.credentials.get(this.userName) != null) { - pc.setPassword(this.credentials.get(this.userName).toCharArray()); - } - else { - LOG.warn("No password found for user: " + this.userName); - } - } - } - else { - if (callback instanceof RealmCallback) { - RealmCallback rc = (RealmCallback) callback; - LOG.debug("client supplied realm: " + rc.getDefaultText()); - rc.setText(rc.getDefaultText()); - } - else { - if (callback instanceof AuthorizeCallback) { - AuthorizeCallback ac = (AuthorizeCallback) callback; - - String authenticationID = ac.getAuthenticationID(); - String authorizationID = ac.getAuthorizationID(); - - LOG.info("Successfully authenticated client: authenticationID=" + authenticationID + "; authorizationID=" + authorizationID + "."); - if (authenticationID.equals(authorizationID)) { - LOG.debug("setAuthorized(true) since " + authenticationID + "==" + authorizationID); - ac.setAuthorized(true); - } else { - LOG.debug("setAuthorized(true), even though " + authenticationID + "!=" + authorizationID + "."); - ac.setAuthorized(true); - } - if (ac.isAuthorized()) { - LOG.debug("isAuthorized() since ac.isAuthorized() == true"); - // canonicalize authorization id according to system properties: - // kerberos.removeRealmFromPrincipal(={true,false}) - // kerberos.removeHostFromPrincipal(={true,false}) - KerberosName kerberosName = new KerberosName(authenticationID); - try { - String userName = kerberosName.getShortName(); - if (!removeHost() && (kerberosName.getHostName() != null)) { - userName += "/" + kerberosName.getServiceName(); - } - if (!removeRealm() && (kerberosName.getRealm() != null)) { - userName += "@" + kerberosName.getRealm(); - } - LOG.info("Setting authorizedID: " + userName); - ac.setAuthorizedID(userName); - } - catch (IOException e) { - LOG.error("Failed to set name based on Kerberos authentication rules."); - } - } - } - } - } - } + private void handlePasswordCallback(PasswordCallback pc) { + if ("super".equals(this.userName) && System.getProperty(SYSPROP_SUPER_PASSWORD) != null) { + // superuser: use Java system property for password, if available. + pc.setPassword(System.getProperty(SYSPROP_SUPER_PASSWORD).toCharArray()); + } else if (credentials.containsKey(userName) ) { + pc.setPassword(credentials.get(userName).toCharArray()); + } else { + LOG.warn("No password found for user: " + userName); + } + } + + private void handleRealmCallback(RealmCallback rc) { + LOG.debug("client supplied realm: " + rc.getDefaultText()); + rc.setText(rc.getDefaultText()); + } + + private void handleAuthorizeCallback(AuthorizeCallback ac) { + String authenticationID = ac.getAuthenticationID(); + String authorizationID = ac.getAuthorizationID(); + + LOG.info("Successfully authenticated client: authenticationID=" + authenticationID + + "; authorizationID=" + authorizationID + "."); + ac.setAuthorized(true); + + // canonicalize authorization id according to system properties: + // zookeeper.kerberos.removeRealmFromPrincipal(={true,false}) + // zookeeper.kerberos.removeHostFromPrincipal(={true,false}) + KerberosName kerberosName = new KerberosName(authenticationID); + try { + StringBuilder userNameBuilder = new StringBuilder(kerberosName.getShortName()); + if (shouldAppendHost(kerberosName)) { + userNameBuilder.append("/").append(kerberosName.getHostName()); + } + if (shouldAppendRealm(kerberosName)) { + userNameBuilder.append("@").append(kerberosName.getRealm()); } + LOG.info("Setting authorizedID: " + userNameBuilder); + ac.setAuthorizedID(userNameBuilder.toString()); + } catch (IOException e) { + LOG.error("Failed to set name based on Kerberos authentication rules."); } + } + + private boolean shouldAppendRealm(KerberosName kerberosName) { + return !isSystemPropertyTrue(SYSPROP_REMOVE_REALM) && kerberosName.getRealm() != null; + } - private boolean removeRealm() { - return ((System.getProperty("zookeeper.kerberos.removeRealmFromPrincipal") != null) && - (System.getProperty("zookeeper.kerberos.removeRealmFromPrincipal").equals("true"))); + private boolean shouldAppendHost(KerberosName kerberosName) { + return !isSystemPropertyTrue(SYSPROP_REMOVE_HOST) && kerberosName.getHostName() != null; } - private boolean removeHost() { - return ((System.getProperty("zookeeper.kerberos.removeHostFromPrincipal") != null) && - (System.getProperty("zookeeper.kerberos.removeHostFromPrincipal").equals("true"))); + private boolean isSystemPropertyTrue(String propertyName) { + return "true".equals(System.getProperty(propertyName)); } } From 13fbec9cedefbdcb6e9f9dfeefd164308bf97dc0 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 29 Sep 2011 21:34:29 +0000 Subject: [PATCH 008/444] ZOOKEEPER-1206. Sequential node creation does not use always use digits in node name given certain Locales. (Mark Miller via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1177434 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/server/PrepRequestProcessor.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 363dc3eea57..8dc86614cac 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -336,6 +336,9 @@ BUGFIXES: ZOOKEEPER-1203. Zookeeper systest is missing Junit Classes (Prashant Gokhale via phunt) + ZOOKEEPER-1206. Sequential node creation does not use always use + digits in node name given certain Locales. (Mark Miller via phunt) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 6d19f5e8cd5..077cab6c95a 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -27,6 +27,7 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Locale; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; @@ -318,7 +319,7 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record) CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags()); if (createMode.isSequential()) { - path = path + String.format("%010d", parentCVersion); + path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion); } try { PathUtils.validatePath(path); From ac5a3202ca0f77c91a25fe4283d228f041a6a089 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 5 Oct 2011 17:27:59 +0000 Subject: [PATCH 009/444] =?UTF-8?q?ZOOKEEPER-1212.=20zkServer.sh=20stop=20?= =?UTF-8?q?action=20is=20not=20conformat=20with=20LSB=20para=2020.2=C2=A0I?= =?UTF-8?q?nit=C2=A0Script=C2=A0Actions=20(Roman=20Shaposhnik=20via=20phun?= =?UTF-8?q?t)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1179337 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ bin/zkServer.sh | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8dc86614cac..c6b6df8fdbf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -339,6 +339,9 @@ BUGFIXES: ZOOKEEPER-1206. Sequential node creation does not use always use digits in node name given certain Locales. (Mark Miller via phunt) + ZOOKEEPER-1212. zkServer.sh stop action is not conformat with LSB + para 20.2 Init Script Actions (Roman Shaposhnik via phunt) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/bin/zkServer.sh b/bin/zkServer.sh index d9858d140ce..ac426ff3bb9 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -130,14 +130,13 @@ stop) echo -n "Stopping zookeeper ... " if [ ! -f "$ZOOPIDFILE" ] then - echo "error: could not find file $ZOOPIDFILE" - exit 1 + echo "no zookeeper to stop (could not find file $ZOOPIDFILE)" else $KILL -9 $(cat "$ZOOPIDFILE") rm "$ZOOPIDFILE" echo STOPPED - exit 0 fi + exit 0 ;; upgrade) shift From d8bfaf1c72521364df6cd2eb95b7cd793fb8be0c Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 5 Oct 2011 18:20:50 +0000 Subject: [PATCH 010/444] ZOOKEEPER-1190. ant package is not including many of the bin scripts in the package (zkServer.sh for example) (Eric Yang via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1179362 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ bin/zkEnv.sh | 9 +++- build.xml | 115 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 122 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c6b6df8fdbf..a2117ded8ee 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -342,6 +342,9 @@ BUGFIXES: ZOOKEEPER-1212. zkServer.sh stop action is not conformat with LSB para 20.2 Init Script Actions (Roman Shaposhnik via phunt) + ZOOKEEPER-1190. ant package is not including many of the bin scripts + in the package (zkServer.sh for example) (Eric Yang via phunt) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh index c074212a34c..c6c5d21c94a 100755 --- a/bin/zkEnv.sh +++ b/bin/zkEnv.sh @@ -75,10 +75,15 @@ do CLASSPATH="$i:$CLASSPATH" done -#make it work in the release -if [ -d ${ZOOKEEPER_PREFIX}/share/zookeeper ]; then +#make it work in the binary package +if [ -e ${ZOOKEEPER_PREFIX}/share/zookeeper/zookeeper-*.jar ]; then LIBPATH="${ZOOKEEPER_PREFIX}"/share/zookeeper/*.jar else + #release tarball format + for i in "$ZOOBINDIR"/../zookeeper-*.jar + do + CLASSPATH="$i:$CLASSPATH" + done LIBPATH="${ZOOBINDIR}"/../lib/*.jar fi diff --git a/build.xml b/build.xml index 219ce54f576..4a7eb2d0eb8 100644 --- a/build.xml +++ b/build.xml @@ -632,11 +632,120 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -811,7 +920,7 @@ - @@ -832,7 +941,7 @@ - + From 6e7b73e04ee965c2692ea325251b90be4139621a Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 7 Oct 2011 20:47:42 +0000 Subject: [PATCH 011/444] ZOOKEEPER-1190. ant package is not including many of the bin scripts in the package (zkServer.sh for example) (Eric Yang via phunt). Part 2. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1180223 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 ++ src/contrib/build-contrib.xml | 4 ++-- src/recipes/build-recipes.xml | 4 ++-- src/recipes/lock/build.xml | 10 +++++----- src/recipes/queue/build.xml | 10 +++++----- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/build.xml b/build.xml index 4a7eb2d0eb8..5c2097430b5 100644 --- a/build.xml +++ b/build.xml @@ -656,6 +656,7 @@ distribution directory so contribs know where to install to.--> + @@ -770,6 +771,7 @@ distribution directory so contribs know where to install to.--> + diff --git a/src/contrib/build-contrib.xml b/src/contrib/build-contrib.xml index 96c4dc8e17b..0e57d087af3 100644 --- a/src/contrib/build-contrib.xml +++ b/src/contrib/build-contrib.xml @@ -144,8 +144,8 @@ - - + diff --git a/src/recipes/build-recipes.xml b/src/recipes/build-recipes.xml index bafaf63891f..dff659df9d2 100644 --- a/src/recipes/build-recipes.xml +++ b/src/recipes/build-recipes.xml @@ -122,8 +122,8 @@ - - + diff --git a/src/recipes/lock/build.xml b/src/recipes/lock/build.xml index fd187497360..841cc018145 100644 --- a/src/recipes/lock/build.xml +++ b/src/recipes/lock/build.xml @@ -112,14 +112,14 @@ - + - - + + - - + + diff --git a/src/recipes/queue/build.xml b/src/recipes/queue/build.xml index 0c2e8755f5a..0f3505ab9ad 100644 --- a/src/recipes/queue/build.xml +++ b/src/recipes/queue/build.xml @@ -112,14 +112,14 @@ - + - - + + - - + + From 13dade11ad4b31b88c526bd7ea17d9cdb4bee48e Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 19 Oct 2011 06:54:36 +0000 Subject: [PATCH 012/444] ZOOKEEPER-1086. zookeeper test jar has non mavenised dependency. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1185995 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/libtest/accessive.jar | Bin 20879 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/java/libtest/accessive.jar diff --git a/src/java/libtest/accessive.jar b/src/java/libtest/accessive.jar deleted file mode 100644 index c7fdd87decc3f84ab5a9679e0b6fa6238ae93290..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20879 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 Date: Mon, 24 Oct 2011 06:48:53 +0000 Subject: [PATCH 013/444] ZOOKEEPER-1181. Fix problems with Kerberos TGT renewal. (Eugene Koontz via mahadev) - Merging r1188033 from trunk git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1188035 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + src/java/main/org/apache/zookeeper/Login.java | 402 +++++++++++------- 2 files changed, 245 insertions(+), 160 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a2117ded8ee..f81877ee7de 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -345,6 +345,9 @@ BUGFIXES: ZOOKEEPER-1190. ant package is not including many of the bin scripts in the package (zkServer.sh for example) (Eric Yang via phunt) + ZOOKEEPER-1181. Fix problems with Kerberos TGT renewal. + (Eugene Koontz via mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index de64d0df385..294801ad846 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -36,8 +36,8 @@ import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.Subject; -import java.io.IOException; import java.util.Date; +import java.util.Random; import java.util.Set; public class Login { @@ -49,6 +49,11 @@ public class Login { // and try to renew the ticket. private static final float TICKET_RENEW_WINDOW = 0.80f; + /** + * Percentage of random jitter added to the renewal time + */ + private static final float TICKET_RENEW_JITTER = 0.05f; + // Regardless of TICKET_RENEW_WINDOW setting above and the ticket expiry time, // thread will not sleep between refresh attempts any less than 1 minute (60*1000 milliseconds = 1 minute). // Change the '1' to e.g. 5, to change this to 5 minutes. @@ -58,6 +63,18 @@ public class Login { private Thread t = null; private boolean isKrbTicket = false; private boolean isUsingTicketCache = false; + private boolean isUsingKeytab = false; + + /** Random number generator */ + private static Random rng = new Random(); + + private LoginContext login = null; + private String loginContextName = null; + private String keytabFile = null; + private String principal = null; + + private long lastLogin = 0; + /** * LoginThread constructor. The constructor starts the thread used * to periodically re-login to the Kerberos Ticket Granting Server. @@ -71,129 +88,170 @@ public class Login { * Thrown if authentication fails. */ public Login(final String loginContextName, CallbackHandler callbackHandler) - throws LoginException { + throws LoginException { this.callbackHandler = callbackHandler; - final LoginContext loginContext = login(loginContextName); - subject = loginContext.getSubject(); + login = login(loginContextName); + this.loginContextName = loginContextName; + subject = login.getSubject(); isKrbTicket = !subject.getPrivateCredentials(KerberosTicket.class).isEmpty(); AppConfigurationEntry entries[] = Configuration.getConfiguration().getAppConfigurationEntry(loginContextName); for (AppConfigurationEntry entry: entries) { + // there will only be a single entry, so this for() loop will only be iterated through once. if (entry.getOptions().get("useTicketCache") != null) { String val = (String)entry.getOptions().get("useTicketCache"); if (val.equals("true")) { isUsingTicketCache = true; } - break; } + if (entry.getOptions().get("keyTab") != null) { + keytabFile = (String)entry.getOptions().get("keyTab"); + isUsingKeytab = true; + } + if (entry.getOptions().get("principal") != null) { + principal = (String)entry.getOptions().get("principal"); + } + break; } - if (isKrbTicket && isUsingTicketCache) { - // Refresh the Ticket Granting Ticket (TGT) cache periodically. How often to refresh is determined by the - // TGT's existing expiry date and the configured MIN_TIME_BEFORE_RELOGIN. For testing and development, - // you can decrease the interval of expiration of tickets (for example, to 3 minutes) by running : - // "modprinc -maxlife 3mins " in kadmin. - t = new Thread(new Runnable() { - public void run() { - LOG.info("TGT refresh thread started."); - while (true) { // renewal thread's main loop. if it exits from here, thread will exit. - KerberosTicket tgt = getTGT(); - long now = System.currentTimeMillis(); - long nextRefresh; - Date nextRefreshDate; - if (tgt == null) { - nextRefresh = now + MIN_TIME_BEFORE_RELOGIN; - nextRefreshDate = new Date(nextRefresh); - LOG.warn("No TGT found: will try again at " + nextRefreshDate); - } - else { - // determine how long to sleep from looking at ticket's expiry. - // We must not allow the ticket to expire, but we should take into consideration - // MIN_TIME_BEFORE_RELOGIN. Will not sleep less than MIN_TIME_BEFORE_RELOGIN, except when - // unless it would cause ticket expiration. - nextRefresh = getRefreshTime(tgt); - long expiry = tgt.getEndTime().getTime(); - if ((nextRefresh > expiry) || - ((now + MIN_TIME_BEFORE_RELOGIN) > expiry)) { - // expiry is before next scheduled refresh). - LOG.info("refreshing now because expiry is before next scheduled refresh time."); - nextRefresh = now; - } - else { - if (nextRefresh < (now + MIN_TIME_BEFORE_RELOGIN)) { - // next scheduled refresh is sooner than (now + MIN_TIME_BEFORE_LOGIN). - Date until = new Date(nextRefresh); - Date newuntil = new Date(now + MIN_TIME_BEFORE_RELOGIN); - LOG.warn("TGT refresh thread time adjusted from : " + until + " to : " + newuntil + " since " - + "the former is sooner than the minimum refresh interval (" - + MIN_TIME_BEFORE_RELOGIN / 1000 + " seconds) from now."); - } - nextRefresh = Math.max(nextRefresh, now + MIN_TIME_BEFORE_RELOGIN); - } - nextRefreshDate = new Date(nextRefresh); - if (nextRefresh > expiry) { - Date expiryDate = new Date(expiry); - LOG.error("next refresh: " + nextRefreshDate + " is later than expiry " + expiryDate - + ". This may indicated a clock skew problem. Check that this host and the KDC's " - + "hosts' clocks are in sync."); - return; - } - } + if (!isKrbTicket) { + // if no TGT, do not bother with ticket management. + return; + } - if (now < nextRefresh) { - Date until = new Date(nextRefresh); - LOG.info("TGT refresh thread sleeping until: " + until.toString()); - try { - Thread.sleep(nextRefresh - now); - } - catch (InterruptedException ie) { - LOG.warn("TGT renewal thread has been interrupted and will exit."); - break; + // Refresh the Ticket Granting Ticket (TGT) periodically. How often to refresh is determined by the + // TGT's existing expiry date and the configured MIN_TIME_BEFORE_RELOGIN. For testing and development, + // you can decrease the interval of expiration of tickets (for example, to 3 minutes) by running : + // "modprinc -maxlife 3mins " in kadmin. + t = new Thread(new Runnable() { + public void run() { + LOG.info("TGT refresh thread started."); + while (true) { // renewal thread's main loop. if it exits from here, thread will exit. + KerberosTicket tgt = getTGT(); + long now = System.currentTimeMillis(); + long nextRefresh; + Date nextRefreshDate; + if (tgt == null) { + nextRefresh = now + MIN_TIME_BEFORE_RELOGIN; + nextRefreshDate = new Date(nextRefresh); + LOG.warn("No TGT found: will try again at " + nextRefreshDate); + } else { + nextRefresh = getRefreshTime(tgt); + long expiry = tgt.getEndTime().getTime(); + Date expiryDate = new Date(expiry); + if ((isUsingTicketCache) && (tgt.getEndTime().equals(tgt.getRenewTill()))) { + LOG.error("The TGT cannot be renewed beyond the next expiry date: " + expiryDate + "." + + "This process will not be able to authenticate new SASL connections after that " + + "time (for example, it will not be authenticate a new connection with a Zookeeper " + + "Quorum member). Ask your system administrator to either increase the " + + "'renew until' time by doing : 'modprinc -maxrenewlife " + principal + "' within " + + "kadmin, or instead, to generate a keytab for " + principal + ". Because the TGT's " + + "expiry cannot be further extended by refreshing, exiting refresh thread now."); + return; + } + // determine how long to sleep from looking at ticket's expiry. + // We should not allow the ticket to expire, but we should take into consideration + // MIN_TIME_BEFORE_RELOGIN. Will not sleep less than MIN_TIME_BEFORE_RELOGIN, unless doing so + // would cause ticket expiration. + if ((nextRefresh > expiry) || + ((now + MIN_TIME_BEFORE_RELOGIN) > expiry)) { + // expiry is before next scheduled refresh). + LOG.info("refreshing now because expiry is before next scheduled refresh time."); + nextRefresh = now; + } else { + if (nextRefresh < (now + MIN_TIME_BEFORE_RELOGIN)) { + // next scheduled refresh is sooner than (now + MIN_TIME_BEFORE_LOGIN). + Date until = new Date(nextRefresh); + Date newuntil = new Date(now + MIN_TIME_BEFORE_RELOGIN); + LOG.warn("TGT refresh thread time adjusted from : " + until + " to : " + newuntil + " since " + + "the former is sooner than the minimum refresh interval (" + + MIN_TIME_BEFORE_RELOGIN / 1000 + " seconds) from now."); } + nextRefresh = Math.max(nextRefresh, now + MIN_TIME_BEFORE_RELOGIN); } - else { - LOG.error("nextRefresh:" + nextRefreshDate + " is in the past: exiting refresh thread. Check" - + " clock sync between this host and KDC - (KDC's clock is likely ahead of this host)." - + " Manual intervention will be required for this client to successfully authenticate."); - // TODO: if we have a keytab, we can use that to re-initialize and avoid the need for - // manual intervention. + nextRefreshDate = new Date(nextRefresh); + if (nextRefresh > expiry) { + LOG.error("next refresh: " + nextRefreshDate + " is later than expiry " + expiryDate + + ". This may indicate a clock skew problem. Check that this host and the KDC's " + + "hosts' clocks are in sync. Exiting refresh thread."); return; } - + } + if (now < nextRefresh) { + Date until = new Date(nextRefresh); + LOG.info("TGT refresh sleeping until: " + until.toString()); + try { + Thread.sleep(nextRefresh - now); + } catch (InterruptedException ie) { + LOG.warn("TGT renewal thread has been interrupted and will exit."); + break; + } + } + else { + LOG.error("nextRefresh:" + nextRefreshDate + " is in the past: exiting refresh thread. Check" + + " clock sync between this host and KDC - (KDC's clock is likely ahead of this host)." + + " Manual intervention will be required for this client to successfully authenticate." + + " Exiting refresh thread."); + return; + } + if (isUsingTicketCache) { String cmd = "/usr/bin/kinit"; if (System.getProperty("zookeeper.kinit") != null) { cmd = System.getProperty("zookeeper.kinit"); } String kinitArgs = "-R"; - try { - Shell.execCommand(cmd,kinitArgs); - } - catch (Shell.ExitCodeException e) { - LOG.error("Could not renew TGT due to problem running shell command: '" + cmd - + " " + kinitArgs + "'" + "; exception was:" + e + ". Will try shell command again at: " - + nextRefreshDate); - } - catch (IOException e) { - LOG.error("Could not renew TGT due to problem running shell command: '" + cmd - + " " + kinitArgs + "'; exception was:" + e + ". Will try shell command again at: " - + nextRefreshDate); - } - try { - reloginFromTicketCache(loginContextName, loginContext); - LOG.debug("renewed TGT successfully."); + int retry = 1; + while (retry >= 0) { + try { + LOG.debug("running ticket cache refresh command: " + cmd + " " + kinitArgs); + Shell.execCommand(cmd, kinitArgs); + break; + } catch (Exception e) { + if (retry > 0) { + --retry; + // sleep for 10 seconds + try { + Thread.sleep(10 * 1000); + } catch (InterruptedException ie) { + LOG.error("Interrupted while renewing TGT, exiting Login thread"); + return; + } + } else { + LOG.warn("Could not renew TGT due to problem running shell command: '" + cmd + + " " + kinitArgs + "'" + "; exception was:" + e + ". Exiting refresh thread.",e); + return; + } + } } - catch (LoginException e) { - LOG.error("Could not renew TGT due to LoginException: " + e + "." - + " Will try again at: " - + nextRefreshDate); + } + try { + int retry = 1; + while (retry >= 0) { + try { + reLogin(); + break; + } catch (LoginException le) { + if (retry > 0) { + --retry; + // sleep for 10 seconds. + try { + Thread.sleep(10 * 1000); + } catch (InterruptedException e) { + LOG.error("Interrupted during login retry after LoginException:", le); + throw le; + } + } else { + LOG.error("Could not refresh TGT for principal: " + principal + ".", le); + } + } } + } catch (LoginException le) { + LOG.error("Failed to refresh TGT: refresh thread exiting now.",le); + break; } } - }); - t.setDaemon(true); - } - else { - LOG.error("Not using Ticket Granting Ticket cache: will not start a TGT renewal thread."); - } + } + }); + t.setDaemon(true); } public void startThreadIfNeeded() { @@ -203,11 +261,25 @@ public void startThreadIfNeeded() { } } + public void shutdown() { + if ((t != null) && (t.isAlive())) { + t.interrupt(); + try { + t.join(); + } catch (InterruptedException e) { + LOG.warn("error while waiting for Login thread to shutdown: " + e); + } + } + } + + public Subject getSubject() { + return subject; + } private synchronized LoginContext login(final String loginContextName) throws LoginException { if (loginContextName == null) { throw new LoginException("loginContext name (JAAS file section header) was null. " + - "Please check your java.security.login.auth.config setting."); + "Please check your java.security.login.auth.config setting."); } LoginContext loginContext = new LoginContext(loginContextName,callbackHandler); loginContext.login(); @@ -215,17 +287,14 @@ private synchronized LoginContext login(final String loginContextName) throws Lo return loginContext; } - public Subject getSubject() { - return subject; - } - // c.f. org.apache.hadoop.security.UserGroupInformation. private long getRefreshTime(KerberosTicket tgt) { long start = tgt.getStartTime().getTime(); long expires = tgt.getEndTime().getTime(); - LOG.info("TGT valid starting at: " + tgt.getStartTime().toString()); - LOG.info("TGT expires: " + tgt.getEndTime().toString()); - long proposedRefresh = start + (long) ((expires - start) * TICKET_RENEW_WINDOW); + LOG.info("TGT valid starting at: " + tgt.getStartTime().toString()); + LOG.info("TGT expires: " + tgt.getEndTime().toString()); + long proposedRefresh = start + (long) ((expires - start) * + (TICKET_RENEW_WINDOW + (TICKET_RENEW_JITTER * rng.nextDouble()))); if (proposedRefresh > expires) { // proposedRefresh is too far in the future: it's after ticket expires: simply return now. return System.currentTimeMillis(); @@ -247,67 +316,80 @@ private synchronized KerberosTicket getTGT() { return null; } - // TODO : refactor this with login() to maximize code-sharing. - public synchronized void reloginFromTicketCache(final String loginContextName, LoginContext loginContext) - throws LoginException { - if (!(isKrbTicket && isUsingTicketCache)) { + private boolean hasSufficientTimeElapsed() { + long now = System.currentTimeMillis(); + if (now - getLastLogin() < MIN_TIME_BEFORE_RELOGIN ) { + LOG.warn("Not attempting to re-login since the last re-login was " + + "attempted less than " + (MIN_TIME_BEFORE_RELOGIN/1000) + " seconds"+ + " before."); + return false; + } + // register most recent relogin attempt + setLastLogin(now); + return true; + } + + /** + * Returns login object + * @return login + */ + private LoginContext getLogin() { + return login; + } + + /** + * Set the login object + * @param login + */ + private void setLogin(LoginContext login) { + this.login = login; + } + + /** + * Set the last login time. + * @param time the number of milliseconds since the beginning of time + */ + private void setLastLogin(long time) { + lastLogin = time; + } + + /** + * Get the time of the last login. + * @return the number of milliseconds since the beginning of time. + */ + private long getLastLogin() { + return lastLogin; + } + + /** + * Re-login a principal. This method assumes that {@link #login(String)} has happened already. + * @throws javax.security.auth.login.LoginException on a failure + */ + // c.f. HADOOP-6559 + private synchronized void reLogin() + throws LoginException { + if (!isKrbTicket) { return; } - if (loginContext == null) { + LoginContext login = getLogin(); + if (login == null) { throw new LoginException("login must be done first"); } - String principalName = getPrincipalName(); - try { - LOG.info("Logging out " + principalName); - //clear up the Kerberos state. But the tokens are not cleared! As per + if (!hasSufficientTimeElapsed()) { + return; + } + LOG.info("Initiating logout for " + principal); + synchronized (Login.class) { + //clear up the kerberos state. But the tokens are not cleared! As per //the Java kerberos login module code, only the kerberos credentials - //are cleared. - loginContext.logout(); + //are cleared + login.logout(); //login and also update the subject field of this instance to //have the new credentials (pass it to the LoginContext constructor) - if (loginContextName == null) { - throw new LoginException("loginContext name (JAAS file section header) was null. " + - "Please check your java.security.login.auth.config setting."); - } - if (subject == null) { - throw new LoginException("login subject was null."); - } - LOG.info("Logging in " + principalName); - loginContext.login(); - if (principalName.equals("(no principal name)")) { - // try again to get the principal name, in case the ticket cache was manually refreshed. - principalName = getPrincipalName(); - } - LOG.info("Login successful for " + principalName); - } catch (LoginException le) { - throw new LoginException("Login failure for " + principalName); - } - } - - private String getPrincipalName() { - try { - return getSubject().getPrincipals(KerberosPrincipal.class).toArray()[0].toString(); - } - catch (NullPointerException e) { - LOG.warn("could not display principal name because login was null or login's subject was null: returning '(no principal found)'."); + login = new LoginContext(loginContextName, getSubject()); + LOG.info("Initiating re-login for " + principal); + login.login(); + setLogin(login); } - catch (ArrayIndexOutOfBoundsException e) { - LOG.warn("could not display principal name because login's subject had no principals: returning '(no principal found)'."); - } - return "(no principal found)"; } - - public void shutdown() { - if ((t != null) && (t.isAlive())) { - t.interrupt(); - try { - t.join(); - } - catch (InterruptedException e) { - LOG.error("error while waiting for Login thread to shutdown: " + e); - } - } - } - } - From 36cf6e64f92a57588a19a26c4652157b4e6e265f Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Tue, 25 Oct 2011 04:08:39 +0000 Subject: [PATCH 014/444] Preparing for release 3.4.0 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1188509 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17122 -> 17122 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13496 -> 13496 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12445 -> 12445 bytes docs/recipes.pdf | Bin 31043 -> 31043 bytes docs/releasenotes.html | 2997 ++++++++++------ docs/releasenotes.pdf | Bin 61360 -> 79542 bytes docs/zookeeperAdmin.html | 42 +- docs/zookeeperAdmin.pdf | Bin 71094 -> 71973 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133759 -> 133759 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27563 -> 27563 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes .../content/xdocs/releasenotes.xml | 3072 ++++++++++------- 24 files changed, 3867 insertions(+), 2246 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f81877ee7de..d2c07116565 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Trunk +Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index b98602f2e2b673ea27e04933eba7a6d2c1d7cfff..1131a1c10997c81506a8cef020d3f3c8d2f2d6de 100644 GIT binary patch delta 176 zcmaE#{XTobBrZb(BNHP-19L;OjdO1D@EIED8kvL`8Cn^bTN#>dZswiDh$c2!nO`4G zXmbL;fm*zUi-Dz~lbMN`qq(E0g^P=$simofk(slRv7v#5rJIGFf(=0>v0Qd`T*W1c XMI{wQscBq>MrM{qT&k+B{%%|V?Zqq4 delta 176 zcmaE#{XTobBrXFBBTFM=BNId8jdO1D@EKU>8d-)I8Cw~dSQ#2`ZswiDh$c2!nO`4G zXmbL;fm*zon~}MbxvPbdo3o3FrIVYng^{C+rJ0M7i=&B!iGj17f(=0>v0Qd`T*W1c XMI{wQscBq>MrM{qT&k+B{%%|VDTXYM diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index e703fd6e762cefdbe43b743ee196d4f7d9f91b96..d75aa6e626588b052e63206a099a7b6e16acd0d2 100644 GIT binary patch delta 163 zcmey?!1=9#b3!YZp@ET!k)eUPq1nd%Gdz5T2D(NjAx4H)2If|VW}7p4Co!UlP3Gj+ zM-ysx<8OE4XWZ__&ve1S+1S+7$;HLgz{1kP!p+#y*}~1l)zHP!(ZbT)+0@*?!cM`4 Lkdo<<4otEDoB$|7 delta 163 zcmey?!1=9#b3!YZfrXK!k+G49q0z?vGdz3-7P>~3Ax6elMkZEHvCo!UlP3Gj+ zM-ysx<8OE4XWZ__&ve1S+0D_&%*@oq$<)!pz{$YX#MsTq+`_=j#MRQ!+|t$6%ud0E Lkdo<<4otEDsdFem diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 36fd032fc59586e4330060091e8f2f645e9b1a4c..da3d1836f21d4dbadbdcbbbde7cbac9499e1eeb4 100644 GIT binary patch delta 154 zcmZoY%-DLEal#}nLjxldBSQmoLz9hjZu0OM8t59CgcuoG8JJrcnrv?7oy3SHHd&cp zA5Ca;0{^ig^QxjJ=S5pf|b2|kaLP{oQCdvQ+ DN+~AQ diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index 520e2ec00e7c7d2bf7e3a983e17919dab78a361c..2273744657feef4d61e59018468f0afe06bd3286 100644 GIT binary patch delta 178 zcmaFV%J`_2al#}nLjxldBSQmoL$i%@Zu0OM8t59CgcuoG8JJrcnr&|8oy3SHHd&cp zA5Ca;0>83xysM#`tFx(*g|RV^HZU--Fn4q^aB_7va&vZbG%_@{Q?MbZB$mt0j;pvN Yv8be?C^e1C(A2`noJ&>J)!&T^04Me=fB*mh delta 178 zcmaFV%J`_2al#}n0}CTdBV!{IL&J@8Zu0OMSm+vAh8P)J8JSoa8g6dpoy3SHHd&cp zA5Ca;0>83xyqTe!xrKqbfr){Og_E(Fshf+Tp@FNZnWM3RnX#jbk)47KK_#(Vc6MCF ZC5c5P6-B9OT!y9=M&?|qs;>TSTmS{&E6@M{ diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index a81c4332b74d2771b44f02968a645926ad071ddf..96fc0f112b57716f9514bfd6c4c0bb864c6b398e 100644 GIT binary patch delta 176 zcmbP`J|TTVHYle49TiL;}HrHQGlo1>YFiLv0Qd`T*W1c XMI{wQscBq>Mn(pvT&k+B{%%|Vt)wg- delta 176 zcmbP`J|TTVHavxSqfnYpE_p^J+-kTP|2baZhubG9^eF*S3sFt$^$A*dvl%g&CgxFoTt Wq@pM_jmyx;$iS3KRn^tsjSB$EvMg2r diff --git a/docs/index.pdf b/docs/index.pdf index 05edc06f1591e33b60e57a4ecd10410142186cde..d62561db92d68eeb4d558b579de6ef568945f70f 100644 GIT binary patch delta 152 zcmdmyxg&GJIxa&4BNHP-19L-zjXS>b@EIED8kvL`8Cn^bTNxT`-pxCS5lw7zG`~KY z(B>KZ2UVP1%uNi8ES)S3OiW!3OfAif3|$OejLj_#TntTI4UCNK6l@47nLNit1^_}B BCT#!! delta 152 zcmdmyxg&GJIxYhXBTFM=BNGGjjXS>b@EKU>8d-)I8Cw~dSQ(ga-pxCS5lw7zG`~KY z(B>KZ2UVPn&5bO~O-!6DOq?y8%neK}ERCFzXkcVwWN2V+Xs~h8c^*DP16?DN5Fcax-#sH8yfIu~V=iq-65DW*Gp& CZzhZY delta 154 zcmeC`VCw2%n$W>zU}0owWNc(&XuNUKc^*Ck3tc125F=wNBNHn_duIHV^h$c2!o?jnL zXmbqzBL!z;GgC8TV8d-)I8Cw~dSQ#2_uIHV^h$c2!o?jnL zXmbqzBL!zeV*^to19M|%6H`+YV-pi|R}&W#XG3Q*GiM7o3uk9L1sg(2CN~<$0074d BC7S>M diff --git a/docs/recipes.pdf b/docs/recipes.pdf index 0855c6eb0e6ee4d4302621624e4f6ef2d3186015..67a0f228cb26808bbc36af921d78eb3dfb1e25f3 100644 GIT binary patch delta 178 zcmX^7iSh6!#t9R-3=ND-j0_FT4UIO=zQMz1XrOCk5@KX%WngY)XtcSJcM>C-*knb1 zeKeuXar|wW@#e0sE^f{)hDI)qPUdE=#+DYA&OoNKp^>YFp@FHZoq`QPC9zy~c3j0J YiA5z9MX70AMwZ4#7F?>TuKsRZ0N5ri?EnA( delta 178 zcmX^7iSh6!#t9R-3@nT+jf{;<42(CC-*knb1 zeKeuXar|wW@kRzt1}@G97S2WnZbptK24+r9&gPZ|u4Yb#jxMf_#&!xe1eL^c+1YUw amn0UIR1~GAaT!?}8(DCvs=E5SaRC6ynJt$9 diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 2255c7ec9c7..9f519793841 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -5,7 +5,7 @@ -ZooKeeper 3.0.0 Release Notes +ZooKeeper 3.4.0 Release Notes @@ -202,37 +202,12 @@ PDF -icon
PDF
-

ZooKeeper 3.0.0 Release Notes

+

ZooKeeper 3.4.0 Release Notes

@@ -249,12 +224,6 @@

ZooKeeper 3.0.0 Release Notes

- -

Migration Instructions when Upgrading to 3.0.0

+ + +

Changes Since ZooKeeper 3.3.0

-

+ + -You should only have to read this section if you are upgrading from a previous version of ZooKeeper to version 3.0.0, otw skip down to changes +Changes Since ZooKeeper 3.3.0 -

-

-A small number of changes in this release have resulted in non-backward compatible Zookeeper client user code and server instance data. The following instructions provide details on how to migrate code and date from version 2.2.1 to version 3.0.0. -

-

-Note: ZooKeeper increments the major version number (major.minor.fix) when backward incompatible changes are made to the source base. As part of the migration from SourceForge we changed the package structure (com.yahoo.zookeeper.* to org.apache.zookeeper.*) and felt it was a good time to incorporate some changes that we had been withholding. As a result the following will be required when migrating from 2.2.1 to 3.0.0 version of ZooKeeper. -

- - -

Migrating Client Code

-

- The underlying client-server protocol has changed in version 3.0.0 - of ZooKeeper. As a result clients must be upgraded along with - serving clusters to ensure proper operation of the system (old - pre-3.0.0 clients are not guaranteed to operate against upgraded - 3.0.0 servers and vice-versa). -

- -

Watch Management

-

-In previous releases of ZooKeeper any watches registered by clients were lost if the client lost a connection to a ZooKeeper server. -This meant that developers had to track watches they were interested in and reregister them if a session disconnect event was recieved. -In this release the client library tracks watches that a client has registered and reregisters the watches when a connection is made to a new server. -Applications that still manually reregister interest should continue working properly as long as they are able to handle unsolicited watches. -For example, an old application may register a watch for /foo and /goo, lose the connection, and reregister only /goo. -As long as the application is able to recieve a notification for /foo, (probably ignoring it) the applications does not to be changes. -One caveat to the watch management: it is possible to miss an event for the creation and deletion of a znode if watching for creation and both the create and delete happens while the client is disconnected from ZooKeeper. -

-

-This release also allows clients to specify call specific watch functions. -This gives the developer the ability to modularize logic in different watch functions rather than cramming everything in the watch function attached to the ZooKeeper handle. -Call specific watch functions receive all session events for as long as they are active, but will only receive the watch callbacks for which they are registered. -

- -

Java API

-
    - -
  1. -

    The java package structure has changed from com.yahoo.zookeeper* to org.apache.zookeeper*. This will probably effect all of your java code which makes use of ZooKeeper APIs (typically import statements)

    -
  2. - -
  3. -

    A number of constants used in the client ZooKeeper API were re-specified using enums (rather than ints). See ZOOKEEPER-7, ZOOKEEPER-132 and ZOOKEEPER-139 for full details

    -
  4. +
-
  • -

    -ZOOKEEPER-18 removed KeeperStateChanged, use KeeperStateDisconnected instead

    -
  • + + - -

    -Also see the current java API +

    -

    - -

    C API

    -
      - -
    1. -

      A number of constants used in the client ZooKeeper API were renamed in order to reduce namespace collision, see ZOOKEEPER-6 for full details

      -
    2. -
    - -

    Migrating Server Data

    -

    -The following issues resulted in changes to the on-disk data format (the snapshot and transaction log files contained within the ZK data directory) and require a migration utility to be run. -

    - + + + - -

    - -The following must be run once, and only once, when upgrading the ZooKeeper server instances to version 3.0.0. +

    -

    -
    -
    Note
    -
    - -

    - The <dataLogDir> and <dataDir> directories referenced - below are specified by the dataLogDir - and dataDir specification in your - ZooKeeper config file - respectively. dataLogDir defaults to the - value of dataDir if not specified explicitly - in the ZooKeeper server config file (in which case provide the - same directory for both parameters to the upgrade utility). -

    +
    + + + - - -
      - -
    1. -

      Shutdown the ZooKeeper server cluster.

      -
    2. - -
    3. -

      Backup your <dataLogDir> and <dataDir> directories

      -
    4. - -
    5. -

      Run upgrade using

      - -
        - -
      • -

        -bin/zkServer.sh upgrade <dataLogDir> <dataDir> -

        -
      • - -
      - -

      or

      - -
        - -
      • -

        -java -classpath pathtolog4j:pathtozookeeper.jar UpgradeMain <dataLogDir> <dataDir> -

        -
      • - -
      - -

      where <dataLogDir> is the directory where all transaction logs (log.*) are stored. <dataDir> is the directory where all the snapshots (snapshot.*) are stored.

      - -
    6. - -
    7. -

      Restart the cluster.

      -
    8. +
    + + - -

    - If you have any failure during the upgrade procedure keep reading to sanitize your database. -

    -

    This is how upgrade works in ZooKeeper. This will help you troubleshoot in case you have problems while upgrading

    -
      +
    + -
  • -

    Upgrade moves files from <dataLogDir> and <dataDir> to <dataLogDir>/version-1/ and <dataDir>/version-1 respectively (version-1 sub-directory is created by the upgrade utility).

    -
  • + + + -
  • -

    Upgrade creates a new version sub-directory <dataDir>/version-2 and <dataLogDir>/version-2

    -
  • + + + + + + -
  • -

    Upgrade reads the old database from <dataDir>/version-1 and <dataLogDir>/version-1 into the memory and creates a new upgraded snapshot.

    -
  • + + + + + + -
  • -

    Upgrade writes the new database in <dataDir>/version-2.

    -
  • + + + + + + - -

    Troubleshooting.

    -
      +
    + + + + + -
  • -

    In case you start ZooKeeper 3.0 without upgrading from 2.0 on a 2.0 database - the servers will start up with an empty database. - This is because the servers assume that <dataDir>/version-2 and <dataLogDir>/version-2 will have the database to start with. Since this will be empty - in case of no upgrade, the servers will start with an empty database. In such a case, shutdown the ZooKeeper servers, remove the version-2 directory (remember - this will lead to loss of updates after you started 3.0.) - and then start the upgrade procedure.

    -
  • + + + + + + -
  • -

    If the upgrade fails while trying to rename files into the version-1 directory, you should try and move all the files under <dataDir>/version-1 - and <dataLogDir>/version-1 to <dataDir> and <dataLogDir> respectively. Then try upgrade again. -

    +
  • + + + + + - + + + + + + -
  • -

    If you do not wish to run with ZooKeeper 3.0 and prefer to run with ZooKeeper 2.0 and have already upgraded - you can run ZooKeeper 2 with - the <dataDir> and <dataLogDir> directories changed to <dataDir>/version-1 and <dataLogDir>/version-1. Remember that you will lose all the updates that you made after the upgrade. -

    -
  • + + + + + + - - -

    Migrating Server Configuration

    -

    -There is a significant change to the ZooKeeper server configuration file. -

    -

    The default election algorithm, specified by - the electionAlg configuration attribute, has - changed from a default of 0 to a default - of 3. See - Cluster - Options section of the administrators guide, specifically - the electionAlg - and server.X properties. -

    -

    - You will either need to explicitly - set electionAlg to it's previous default value - of 0 or change - your server.X options to include the leader - election port. -

    - + + + - -

    Changes Since ZooKeeper 2.2.1

    -
    -

    -Version 2.2.1 code, documentation, binaries, etc... are still accessible on SourceForge +

    + + + -

    -
    Changes Since ZooKeeper 3.3.0
    IssueNotes
    + Sub-Tasks + -
  • -

    -ZOOKEEPER-38 headers (version+) in log/snap files -

    -
  • +
    + + ZOOKEEPER-784 + + server-side functionality for read-only mode +
    + + ZOOKEEPER-798 + + Fixup loggraph for FLE changes +
    + + ZOOKEEPER-839 + + deleteRecursive does not belong to the other methods +
    + + ZOOKEEPER-908 + + Remove code duplication and inconsistent naming in ClientCnxn.Packet creation +
    + + ZOOKEEPER-909 + + Extract NIO specific code from ClientCnxn +
    + + ZOOKEEPER-966 + + Client side for multi +
    + + ZOOKEEPER-967 + + Server side decoding and function dispatch +
    + + ZOOKEEPER-968 + + Database multi-update +
    + + ZOOKEEPER-1042 + + Generate zookeeper test jar for maven installation +
    + + ZOOKEEPER-1081 + + modify leader/follower code to correctly deal with new leader +
    + + ZOOKEEPER-1082 + + modify leader election to correctly take into account current epoch +
    + + ZOOKEEPER-1150 + + fix for this patch to compile on windows... +
    - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -Changes Since ZooKeeper 2.2.1 + + + + + - - - + + + + + + + + + - + - + + ZOOKEEPER-1028 + + - + - + + ZOOKEEPER-1033 + + - + - + + ZOOKEEPER-1034 + + - + - + + ZOOKEEPER-1046 + + - + - + + ZOOKEEPER-1049 + + - + - + + ZOOKEEPER-1051 + + - + - + + ZOOKEEPER-1052 + + - + - + + ZOOKEEPER-1055 + + - + - + + ZOOKEEPER-1058 + + - + - + + ZOOKEEPER-1059 + + - + - + + ZOOKEEPER-1060 + + - + - + + ZOOKEEPER-1061 + + - + - + + ZOOKEEPER-1063 + + - + - + + ZOOKEEPER-1068 + + - + - + + ZOOKEEPER-1069 + + - + - + + ZOOKEEPER-1073 + + - + - + + ZOOKEEPER-1074 + + - + - + + ZOOKEEPER-1076 + + - + - + + ZOOKEEPER-1083 + + - + - + + ZOOKEEPER-1086 + + - + - + + ZOOKEEPER-1087 + + - + - + + ZOOKEEPER-1088 + + - + - + + ZOOKEEPER-1090 + + - + - + + ZOOKEEPER-1091 + + - + - + + ZOOKEEPER-1097 + + - + - + + ZOOKEEPER-1101 + + - + - + + ZOOKEEPER-1108 + + - + - + + ZOOKEEPER-1109 + + - + - + + ZOOKEEPER-1111 + + - + - + + ZOOKEEPER-1119 + + - + - + + ZOOKEEPER-1124 + + - + - + + ZOOKEEPER-1136 + + - + - + + ZOOKEEPER-1138 + + - + - + + ZOOKEEPER-1139 + + - + - + + ZOOKEEPER-1140 + + - + - + + ZOOKEEPER-1141 + + - + - + + ZOOKEEPER-1142 + + - + - + + ZOOKEEPER-1144 + + - + - + + ZOOKEEPER-1145 + + - + - + + ZOOKEEPER-1146 + + - + - + + ZOOKEEPER-1152 + + - + - + + ZOOKEEPER-1154 + + - + - + + ZOOKEEPER-1156 + + - + - + + ZOOKEEPER-1165 + + - + - + + ZOOKEEPER-1168 + + - + - + + ZOOKEEPER-1171 + + - + - + + ZOOKEEPER-1174 + + - + - + + ZOOKEEPER-1181 + + - + - + + ZOOKEEPER-1185 + + - + - + + ZOOKEEPER-1189 + + - + - + + ZOOKEEPER-1190 + + - + - + + ZOOKEEPER-1195 + + - + - + + ZOOKEEPER-1203 + + - + - + + ZOOKEEPER-1206 + + - + - + + ZOOKEEPER-1212 + + - + - + + ZOOKEEPER-1237 + + + + + + + + + + - + + ZOOKEEPER-494 + + - + - + + ZOOKEEPER-500 + + - + - + + ZOOKEEPER-631 + + - + - + + ZOOKEEPER-636 + + - + - + + ZOOKEEPER-724 + + - + - + + ZOOKEEPER-733 + + - + - + + ZOOKEEPER-765 + + - + - + + ZOOKEEPER-773 + + - + - + + ZOOKEEPER-788 + + - + - + + ZOOKEEPER-789 + + - + - + + ZOOKEEPER-797 + + - + - + + ZOOKEEPER-809 + + - + - + + ZOOKEEPER-821 + + - + - + + ZOOKEEPER-853 + + - + - + + ZOOKEEPER-862 + + - + - + + ZOOKEEPER-864 + + - + - + + ZOOKEEPER-891 + + - + - + + ZOOKEEPER-905 + + - + - + + ZOOKEEPER-926 + + - + - + + ZOOKEEPER-977 + + - + - + + ZOOKEEPER-980 + + - + - + + ZOOKEEPER-993 + + - + - + + ZOOKEEPER-997 + + - + - + + ZOOKEEPER-1018 + + - + - + + ZOOKEEPER-1025 + + - + - + + ZOOKEEPER-1030 + + - + - + + ZOOKEEPER-1094 + + - + - + + ZOOKEEPER-1095 + + - + - + + ZOOKEEPER-1103 + + - + - + + ZOOKEEPER-1104 + + - + - + + ZOOKEEPER-1143 + + - + - + + ZOOKEEPER-1153 + + - + - + + ZOOKEEPER-1166 + + - + - + + ZOOKEEPER-1169 + + - + - + + ZOOKEEPER-1243 + + + + + + + + + + - + + ZOOKEEPER-464 + + - + - + + ZOOKEEPER-465 + + - + - + + ZOOKEEPER-546 + + - + - + + ZOOKEEPER-712 + + - + - + + ZOOKEEPER-729 + + - + - + + ZOOKEEPER-744 + + - + - + + ZOOKEEPER-747 + + - + - + + ZOOKEEPER-775 + + - + - + + ZOOKEEPER-799 + + - + - + + ZOOKEEPER-808 + + - + - + + ZOOKEEPER-859 + + - + - + + ZOOKEEPER-938 + + - + - + + ZOOKEEPER-992 + + - + - + + ZOOKEEPER-999 + + - + - + + ZOOKEEPER-1012 + + - + - + + ZOOKEEPER-1020 + + - + - + + ZOOKEEPER-1107 + + + + + + + + + + - + + ZOOKEEPER-754 + + - + - + + ZOOKEEPER-1149 + + + + + + + + + + - + + ZOOKEEPER-239 + + diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index c9c6b054ce91a8757740551482d15ae0edf94f85..006afc518289f6612bd719e11f99b33f55da243f 100644 GIT binary patch literal 79542 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 F{{Y9tm>>WE literal 61360 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ڠRCAUBwG<^EGvd7_X1d3&vU@)ubnGp}uZuRK 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 Data Directory Cleanup logs.

    A ZooKeeper server will not remove - old snapshots and log files, this is the - responsibility of the operator. Every serving environment is - different and therefore the requirements of managing these - files may differ from install to install (backup for example). + old snapshots and log files when using the default + configuration (see autopurge below), this is the + responsibility of the operator. Every serving environment is + different and therefore the requirements of managing these + files may differ from install to install (backup for example).

    The PurgeTxnLog utility implements a simple retention policy that administrators can use. The API docs contains details on @@ -891,6 +892,11 @@

    Ongoing Data Directory Cleanup

    can be run as a cron job on the ZooKeeper server machines to clean up the logs daily.

     java -cp zookeeper.jar:lib/slf4j-api-1.6.1.jar:lib/slf4j-log4j12-1.6.1.jar:lib/log4j-1.2.15.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>
    +

    Automatic purging of the snapshots and corresponding + transaction logs was introduced in version 3.4.0 and can be + enabled via the following configuration parameters autopurge.snapRetainCount and autopurge.purgeInterval. For more on + this, see Advanced Configuration + below.

    Debug Log Cleanup (log4j)

    See the section on logging in this document. It is @@ -1148,6 +1154,34 @@

    Advanced Configuration

    will allow the client to negotiate. Defaults to 20 times the tickTime.

    + + +
    +autopurge.snapRetainCount +
    +
    +

    (No Java system property)

    +

    +New in 3.4.0: + When enabled, ZooKeeper auto purge feature retains + the autopurge.snapRetainCount most + recent snapshots and the corresponding transaction logs in the + dataDir and dataLogDir respectively and deletes the rest. + Defaults to 3. Minimum value is 3.

    +
    + + +
    +autopurge.purgeInterval +
    +
    +

    (No Java system property)

    +

    +New in 3.4.0: The + time interval in hours for which the purge task has to + be triggered. Set to a positive integer (1 and above) + to enable the auto purging. Defaults to 0.

    +
    diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index 4892d6eada4ae00ec58c3ee0ec02792de6607c71..195c95a9680dd294248bcbe2650bcf35639700a4 100644 GIT binary patch delta 34638 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 diff --git a/docs/zookeeperHierarchicalQuorums.pdf b/docs/zookeeperHierarchicalQuorums.pdf index 4bcdf573f4cb8488d6b7248805b557df11ac9216..7709c4f61fb17c2c4dbdc6e3b867a0f4aaf9888a 100644 GIT binary patch delta 175 zcmexw{NH%OG%iB}BNHP-19L;OjSKGb@EIED8kvL`8Cn^bTN#>d?&h7uh$c2!n_nMI zXmbX?C0CrYv$3;_vyrijv7?KjnSrCFp_`$Rp^K@bv9Xz>v4OFjf(=0>v0Qd`T*W1c WMI{wQscBqhh9-tws;aL3Zd?GN$}4RE delta 175 zcmexw{NH%OG%f=RBTFM=BNIcTjSKGb@EKU>8d-)I8Cw~dSQ#2^?&h7uh$c2!n_nMI zXmbX?C0CrWlZlauxs!=05IZ`W8#!4zo4PqWxfwb;11)j1uv4%hs3exl&W@|NB(bQZ Uq9`?u%goTkkV{q7)!&T^0KdvB3%jBS#Z+6EkxQH%kLkGdl$vf=Xh!?CiLT YOA?DpDvDCmxPW?1ExA-xUH#p-08T(^b delta 178 zcmX@~m+8=7rU|WF1{Ow^M#e@ahQ=HF&+zaWSm+vAh8P)J8JSoa8gI_zoy3SHHkp%O zA5Cbp8^7%OcxN*wQzJ_QGc#8cGdFWnOJg@<;Kf=Xh!?CiLT ZOA?DpDvDCmxJ=A|=5VR1y863u0RW6=Ec*Ze diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf index aa4cedc7b50958d9786d1a76d99ebf8f43624d80..0481d0d615a2876a3dab212d0e817133c6dd70c8 100644 GIT binary patch delta 167 zcmaFV!1$5j6%-?Lz zf5sr*)ZE$8%*fHw#L?B!z}Upq#Ms5s*}%}!)Y8eq+{MhqPQiwtl2|S~JFeoA#G;al UqSQ1lLsMfjb1qd?SARDy0IrfMuK)l5 delta 167 zcmaFV!1$5j6%-?Lz zf5sr*)x^lc&Dg};&C$%j(!|i%+0@0=%+1x!)xyZo!r9fxPQiwtl2|S~JFeoA#G;al UqSQ1lLsMfjb1qd?SARDy0NFV!GXMYp diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index a46d3a3396ee487b7dda93b8a833696a6902d02e..e100f38ac28793ae015d1607eb22a350bd6be585 100644 GIT binary patch delta 152 zcmX?^ax!H?E0>{xk%^I^fw`f<#{M%re1-Hp$XMoh6Y9^MurCFh9(LWe=~jv5A3^iHp$XMo1{Ow^M#e@ahK3tk5ApCBSm+vAh8P)J8JSoa8g35doy3SHHu)=` zKAKRo5r4Z8KO+z`0WtG-BYqaq1{EU(BSQrPg&-~k1%1z4Qw4KN1kX^x&=|yp3W_zVqnjZ8v}46O{ztqhGe7xGSGL=&4V$ghtk zwAqh8P{zr{&DFrg(9*!nz}(f$$;iyy!rax+(9Frr(!kBt&A`x3!G?g6$q%(<0muO* A+W-In delta 151 zcmewt{x5t&CzpYRk)@Hbk%@uX#>p3W_zWy`jVwcqjIE4JtPIRH7xGSGL=&4V$ghtk zwAqh8P{zr`)yUP^(7@2d#L~dc*~QYp*x207*u>1x!qU*t)WpzE!G?g6$q%(<0nP{| AWdHyG diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 6bc79fc7cdbbc1a38faf7976daa5bf7620f8b2d9..1d2b14a7f39e82e4cf32ee23120416ba448e2557 100644 GIT binary patch delta 153 zcmZ2|opJSb#t9R-3=ND-j0_FT4b3*rzQMz1XrOCk5@KX%WngY)XtueLcM>C-*knb1 zeKeuXas2P1os7&KjZMrQ&5WH)jSS3A{mn{nb DMt3HP delta 153 zcmZ2|opJSb#t9R-3@nT+jf{;<3=KEVzQMz1V4-Vd8DeB?Wn^MyXt=qNcM>C-*knb1 zeKeuXas2P1oy;9wTn!yv&DA{mn{nb DecUIU diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index f2c9a6d4e315881cbc6740066cc6c5791edd0155..6d388dbd9ebbc1ee39a71704739cabb0e460c5ed 100644 GIT binary patch delta 154 zcmZ4Sj&a31#tHMe3=ND-j0_FT4UIOge8$6PXrOCk5@KX%WngY)Xta4Q?<7VvvB|dl z`e;I%tN8uXoeiB`%}flOoXm~QEsZSQ94!o8olPxGEi5feoLwBvjqMa{2q~G&Q!WDl DrCBE! delta 154 zcmZ4Sj&a31#tHMe3@nT+jf{;<49qvKe8$6PV4-Vd8DeB?Wn^MyV7_@S?<7VvvB|dl z`e;I%tN8uXo!y)rjm@209nGCAoJ|dljEv1pj7{B4%}h+qoDH4K4eS(b2q~G&Q!WDl D!&fIL diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index 6bce57b6eb7..b7631322de3 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -18,7 +18,7 @@
    - ZooKeeper 3.0.0 Release Notes + ZooKeeper 3.4.0 Release Notes @@ -40,200 +40,15 @@ These release notes include new developer and user facing incompatibilities, fea - Migration Instructions Changes -
    -Migration Instructions when Upgrading to 3.0.0 - - -You should only have to read this section if you are upgrading from a previous version of ZooKeeper to version 3.0.0, otw skip down to changes - - - -A small number of changes in this release have resulted in non-backward compatible Zookeeper client user code and server instance data. The following instructions provide details on how to migrate code and date from version 2.2.1 to version 3.0.0. - - - -Note: ZooKeeper increments the major version number (major.minor.fix) when backward incompatible changes are made to the source base. As part of the migration from SourceForge we changed the package structure (com.yahoo.zookeeper.* to org.apache.zookeeper.*) and felt it was a good time to incorporate some changes that we had been withholding. As a result the following will be required when migrating from 2.2.1 to 3.0.0 version of ZooKeeper. - - - - Migrating Client Code - Migrating Server Data - Migrating Server Configuration - - -
    -Migrating Client Code - - - The underlying client-server protocol has changed in version 3.0.0 - of ZooKeeper. As a result clients must be upgraded along with - serving clusters to ensure proper operation of the system (old - pre-3.0.0 clients are not guaranteed to operate against upgraded - 3.0.0 servers and vice-versa). - - -
    -Watch Management - - -In previous releases of ZooKeeper any watches registered by clients were lost if the client lost a connection to a ZooKeeper server. -This meant that developers had to track watches they were interested in and reregister them if a session disconnect event was recieved. -In this release the client library tracks watches that a client has registered and reregisters the watches when a connection is made to a new server. -Applications that still manually reregister interest should continue working properly as long as they are able to handle unsolicited watches. -For example, an old application may register a watch for /foo and /goo, lose the connection, and reregister only /goo. -As long as the application is able to recieve a notification for /foo, (probably ignoring it) the applications does not to be changes. -One caveat to the watch management: it is possible to miss an event for the creation and deletion of a znode if watching for creation and both the create and delete happens while the client is disconnected from ZooKeeper. - - - -This release also allows clients to specify call specific watch functions. -This gives the developer the ability to modularize logic in different watch functions rather than cramming everything in the watch function attached to the ZooKeeper handle. -Call specific watch functions receive all session events for as long as they are active, but will only receive the watch callbacks for which they are registered. - -
    - -
    -Java API - - - The java package structure has changed from com.yahoo.zookeeper* to org.apache.zookeeper*. This will probably effect all of your java code which makes use of ZooKeeper APIs (typically import statements) - A number of constants used in the client ZooKeeper API were re-specified using enums (rather than ints). See ZOOKEEPER-7, ZOOKEEPER-132 and ZOOKEEPER-139 for full details - ZOOKEEPER-18 removed KeeperStateChanged, use KeeperStateDisconnected instead - - - -Also see the current java API - -
    - -
    -C API - - - A number of constants used in the client ZooKeeper API were renamed in order to reduce namespace collision, see ZOOKEEPER-6 for full details - - -
    -
    - -
    -Migrating Server Data - - -The following issues resulted in changes to the on-disk data format (the snapshot and transaction log files contained within the ZK data directory) and require a migration utility to be run. - - - - ZOOKEEPER-27 Unique DB identifiers for servers and clients - ZOOKEEPER-32 CRCs for ZooKeeper data - ZOOKEEPER-33 Better ACL management - ZOOKEEPER-38 headers (version+) in log/snap files - - - - The following must be run once, and only once, when upgrading the ZooKeeper server instances to version 3.0.0. - - - - - The <dataLogDir> and <dataDir> directories referenced - below are specified by the dataLogDir - and dataDir specification in your - ZooKeeper config file - respectively. dataLogDir defaults to the - value of dataDir if not specified explicitly - in the ZooKeeper server config file (in which case provide the - same directory for both parameters to the upgrade utility). - - - - - Shutdown the ZooKeeper server cluster. - Backup your <dataLogDir> and <dataDir> directories - Run upgrade using - - bin/zkServer.sh upgrade <dataLogDir> <dataDir> - - or - - java -classpath pathtolog4j:pathtozookeeper.jar UpgradeMain <dataLogDir> <dataDir> - - where <dataLogDir> is the directory where all transaction logs (log.*) are stored. <dataDir> is the directory where all the snapshots (snapshot.*) are stored. - - Restart the cluster. - - - If you have any failure during the upgrade procedure keep reading to sanitize your database. - -This is how upgrade works in ZooKeeper. This will help you troubleshoot in case you have problems while upgrading - -Upgrade moves files from <dataLogDir> and <dataDir> to <dataLogDir>/version-1/ and <dataDir>/version-1 respectively (version-1 sub-directory is created by the upgrade utility). - Upgrade creates a new version sub-directory <dataDir>/version-2 and <dataLogDir>/version-2 - Upgrade reads the old database from <dataDir>/version-1 and <dataLogDir>/version-1 into the memory and creates a new upgraded snapshot. - Upgrade writes the new database in <dataDir>/version-2. - - - Troubleshooting. - - - In case you start ZooKeeper 3.0 without upgrading from 2.0 on a 2.0 database - the servers will start up with an empty database. - This is because the servers assume that <dataDir>/version-2 and <dataLogDir>/version-2 will have the database to start with. Since this will be empty - in case of no upgrade, the servers will start with an empty database. In such a case, shutdown the ZooKeeper servers, remove the version-2 directory (remember - this will lead to loss of updates after you started 3.0.) - and then start the upgrade procedure. - If the upgrade fails while trying to rename files into the version-1 directory, you should try and move all the files under <dataDir>/version-1 - and <dataLogDir>/version-1 to <dataDir> and <dataLogDir> respectively. Then try upgrade again. - - - If you do not wish to run with ZooKeeper 3.0 and prefer to run with ZooKeeper 2.0 and have already upgraded - you can run ZooKeeper 2 with - the <dataDir> and <dataLogDir> directories changed to <dataDir>/version-1 and <dataLogDir>/version-1. Remember that you will lose all the updates that you made after the upgrade. - - - -
    - -
    -Migrating Server Configuration - - -There is a significant change to the ZooKeeper server configuration file. - - -The default election algorithm, specified by - the electionAlg configuration attribute, has - changed from a default of 0 to a default - of 3. See - Cluster - Options section of the administrators guide, specifically - the electionAlg - and server.X properties. - - - - You will either need to explicitly - set electionAlg to it's previous default value - of 0 or change - your server.X options to include the leader - election port. - - -
    - -
    -Changes Since ZooKeeper 2.2.1 - - -Version 2.2.1 code, documentation, binaries, etc... are still accessible on SourceForge - +Changes Since ZooKeeper 3.3.0 -
    Changes Since ZooKeeper 2.2.1
    + + ZOOKEEPER-1160 + + test timeouts are too small +
    + + ZOOKEEPER-1201 + + Clean SaslServerCallbackHandler.java +
    + Bug Fixes + + +
    + + ZOOKEEPER-335 + + zookeeper servers should commit the new leader txn to their logs. +
    + + ZOOKEEPER-418 + + Need nifty zookeeper browser +
    + + ZOOKEEPER-603 + + zkpython should do a better job of freeing memory under error conditions +
    + + ZOOKEEPER-662 + + Too many CLOSE_WAIT socket state on a server +
    + + ZOOKEEPER-690 + + AsyncTestHammer test fails on hudson. +
    + + ZOOKEEPER-719 + + Add throttling to BookKeeper client +
    + + ZOOKEEPER-720 + + Use zookeeper-{version}-sources.jar instead of zookeeper-{version}-src.jar to publish sources in the Maven repository +
    + + ZOOKEEPER-722 + + zkServer.sh uses sh's builtin echo on BSD, behaves incorrectly. +
    + + ZOOKEEPER-731 + + Zookeeper#delete , #create - async versions miss a verb in the javadoc +
    + + ZOOKEEPER-734 + + QuorumPeerTestBase.java and ZooKeeperServerMainTest.java do not handle windows path correctly +
    + + ZOOKEEPER-735 + + cppunit test testipv6 assumes that the machine is ipv6 enabled. +
    + + ZOOKEEPER-737 + + some 4 letter words may fail with netcat (nc) +
    + + ZOOKEEPER-738 + + zookeeper.jute.h fails to compile with -pedantic +
    + + ZOOKEEPER-741 + + root level create on REST proxy fails +
    + + ZOOKEEPER-742 + + Deallocatng None on writes +
    + + ZOOKEEPER-746 + + learner outputs session id to log in dec (should be hex) +
    + + ZOOKEEPER-749 + + OSGi metadata not included in binary only jar +
    + + ZOOKEEPER-750 + + move maven artifacts into "dist-maven" subdir of the release (package target) +
    + + ZOOKEEPER-758 + + zkpython segfaults on invalid acl with missing key +
    + + ZOOKEEPER-763 + + Deadlock on close w/ zkpython / c client +
    + + ZOOKEEPER-764 + + Observer elected leader due to inconsistent voting view +
    + + ZOOKEEPER-766 + + forrest recipes docs don't mention the lock/queue recipe implementations available in the release +
    + + ZOOKEEPER-769 + + Leader can treat observers as quorum members +
    + + ZOOKEEPER-772 + + zkpython segfaults when watcher from async get children is invoked. +
    + + ZOOKEEPER-774 + + Recipes tests are slightly outdated: they do not compile against JUnit 4.8 +
    + + ZOOKEEPER-777 + + setting acl on a non existant node should return no node error +
    + + ZOOKEEPER-782 + + Incorrect C API documentation for Watches +
    + + ZOOKEEPER-783 + + committedLog in ZKDatabase is not properly synchronized +
    + + ZOOKEEPER-787 + + groupId in deployed pom is wrong +
    + + ZOOKEEPER-790 + + Last processed zxid set prematurely while establishing leadership +
    + + ZOOKEEPER-792 + + zkpython memory leak +
    + + ZOOKEEPER-794 + + Callbacks are not invoked when the client is closed +
    + + ZOOKEEPER-795 + + eventThread isn't shutdown after a connection "session expired" event coming +
    + + ZOOKEEPER-796 + + zkServer.sh should support an external PIDFILE variable +
    + + ZOOKEEPER-800 + + zoo_add_auth returns ZOK if zookeeper handle is in ZOO_CLOSED_STATE +
    + + ZOOKEEPER-804 + + c unit tests failing due to "assertion cptr failed" +
    + + ZOOKEEPER-813 + + maven install is broken due to incorrect organisation +
    + + ZOOKEEPER-814 + + monitoring scripts are missing apache license headers +
    + + ZOOKEEPER-820 + + update c unit tests to ensure "zombie" java server processes don't cause failure +
    + + ZOOKEEPER-822 + + Leader election taking a long time to complete +
    + + ZOOKEEPER-831 + + BookKeeper: Throttling improved for reads +
    + + ZOOKEEPER-844 + + handle auth failure in java client +
    + + ZOOKEEPER-846 + + zookeeper client doesn't shut down cleanly on the close call +
    + + ZOOKEEPER-854 + + BookKeeper does not compile due to changes in the ZooKeeper code +
    + + ZOOKEEPER-855 + + clientPortBindAddress should be clientPortAddress +
    + + ZOOKEEPER-861 + + Missing the test SSL certificate used for running junit tests. +
    + + ZOOKEEPER-867 + + ClientTest is failing on hudson - fd cleanup +
    + + ZOOKEEPER-870 + + Zookeeper trunk build broken. +
    + + ZOOKEEPER-874 + + FileTxnSnapLog.restore does not call listener +
    + + ZOOKEEPER-880 + + QuorumCnxManager$SendWorker grows without bounds +
    + + ZOOKEEPER-881 + + ZooKeeperServer.loadData loads database twice +
    + + ZOOKEEPER-882 + + Startup loads last transaction from snapshot +
    + + ZOOKEEPER-884 + + Remove LedgerSequence references from BookKeeper documentation and comments in tests +
    + + ZOOKEEPER-888 + + c-client / zkpython: Double free corruption on node watcher +
    + + ZOOKEEPER-893 + + ZooKeeper high cpu usage when invalid requests +
    + + ZOOKEEPER-897 + + C Client seg faults during close +
    + + ZOOKEEPER-898 + + C Client might not cleanup correctly during close +
    + + ZOOKEEPER-902 + + Fix findbug issue in trunk "Malicious code vulnerability" +
    + + ZOOKEEPER-904 + + super digest is not actually acting as a full superuser +
    + + ZOOKEEPER-913 + + Version parser fails to parse "3.3.2-dev" from build.xml. +
    + + ZOOKEEPER-917 + + Leader election selected incorrect leader +
    + + ZOOKEEPER-919 + + Ephemeral nodes remains in one of ensemble after deliberate SIGKILL +
    + + ZOOKEEPER-921 + + zkPython incorrectly checks for existence of required ACL elements +
    + + ZOOKEEPER-937 + + test -e not available on solaris /bin/sh +
    + + ZOOKEEPER-957 + + zkCleanup.sh doesn't do anything +
    + + ZOOKEEPER-958 + + Flag to turn off autoconsume in hedwig c++ client +
    + + ZOOKEEPER-961 + + Watch recovery after disconnection when connection string contains a prefix +
    + + ZOOKEEPER-962 + + leader/follower coherence issue when follower is receiving a DIFF +
    + + ZOOKEEPER-963 + + Make Forrest work with JDK6 +
    + + ZOOKEEPER-965 + + Need a multi-update command to allow multiple znodes to be updated safely +
    + + ZOOKEEPER-975 + + new peer goes in LEADING state even if ensemble is online +
    + + ZOOKEEPER-976 + + ZooKeeper startup script doesn't use JAVA_HOME +
    + + ZOOKEEPER-981 + + Hang in zookeeper_close() in the multi-threaded C client +
    + + ZOOKEEPER-983 + + running zkServer.sh start remotely using ssh hangs +
    + + ZOOKEEPER-985 + + Test BookieRecoveryTest fails on trunk. +
    + + ZOOKEEPER-1006 + + QuorumPeer "Address already in use" -- regression in 3.3.3 +
    + + ZOOKEEPER-1007 + + iarchive leak in C client +
    IssueNotes + + ZOOKEEPER-1013 + + zkServer.sh usage message should mention all startup options +
    + + ZOOKEEPER-1027 + + chroot not transparent in zoo_create() +
    - - ZOOKEEPER-43 - - Server side of auto reset watches. - + In python bindings, zookeeper.set2() should return a stat dict but instead returns None +
    - - ZOOKEEPER-132 - - Create Enum to replace CreateFlag in ZooKepper.create method - + c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src +
    - - ZOOKEEPER-139 - - Create Enums for WatcherEvent's KeeperState and EventType - + perl bindings should automatically find the zookeeper c-client headers +
    - - ZOOKEEPER-18 - - keeper state inconsistency - + Creating a new sequential node results in a ZNODEEXISTS error +
    - - ZOOKEEPER-38 - - headers in log/snap files - + Session expire/close flooding renders heartbeats to delay significantly +
    - - ZOOKEEPER-8 - - Stat enchaned to include num of children and size - + SIGPIPE in Zookeeper 0.3.* when send'ing after cluster disconnection +
    - - ZOOKEEPER-6 - - List of problem identifiers in zookeeper.h - + Findbugs warning in QuorumPeer.ResponderThread.run() +
    - - ZOOKEEPER-7 - - Use enums rather than ints for types and state - + check for duplicate ACLs in addACL() and create() +
    - - ZOOKEEPER-27 - - Unique DB identifiers for servers and clients - + fix typo in opToString for getData +
    - - ZOOKEEPER-32 - - CRCs for ZooKeeper data - + stat command isses on non-existing node causes NPE +
    - - ZOOKEEPER-33 - - Better ACL management - + QuorumPeer takes a long time to shutdown +
    - - ZOOKEEPER-203 - - fix datadir typo in releasenotes - + Zookeeper stop fails if start called twice +
    - - ZOOKEEPER-145 - - write detailed release notes for users migrating from 2.x to 3.0 - + Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes +
    - - ZOOKEEPER-23 - - Auto reset of watches on reconnect - + Documentation and default config suggest incorrect location for Zookeeper state +
    - - ZOOKEEPER-191 - - forrest docs for upgrade. - + Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log +
    - - ZOOKEEPER-201 - - validate magic number when reading snapshot and transaction logs - + address a documentation issue in ZOOKEEPER-1030 +
    - - ZOOKEEPER-200 - - the magic number for snapshot and log must be different - + zkServer.sh is missing nohup/sleep, which are necessary for remote invocation +
    - - ZOOKEEPER-199 - - fix log messages in persistence code - + some quorum tests are unnecessarily extending QuorumBase +
    - - ZOOKEEPER-197 - - create checksums for snapshots - + Javadoc for WatchedEvent not being generated +
    - - ZOOKEEPER-198 - - apache license header missing from FollowerSyncRequest.java - + zookeeper test jar has non mavenised dependency. +
    - - ZOOKEEPER-5 - - Upgrade Feature in Zookeeper server. - + ForceSync VM arguement not working when set to "no" +
    - - ZOOKEEPER-194 - - Fix terminology in zookeeperAdmin.xml - + delQuota does not remove the quota node and subesquent setquota calls for that path will fail +
    - - ZOOKEEPER-151 - - Document change to server configuration - + Race condition while taking snapshot can lead to not restoring data tree correctly +
    - - ZOOKEEPER-193 - - update java example doc to compile with latest zookeeper - + when the chrootPath of ClientCnxn is not null and the Watches of zooKeeper is not null and the method primeConnection(SelectionKey k) of ClientCnxn Occurred again for some reason ,then the wrong watcher clientPath is sended to server +
    - - ZOOKEEPER-187 - - CreateMode api docs missing - + Quota is not correctly rehydrated on snapshot reload +
    - - ZOOKEEPER-186 - - add new "releasenotes.xml" to forrest documentation - + Upload zookeeper-test maven artifacts to maven repository. +
    - - ZOOKEEPER-190 - - Reorg links to docs and navs to docs into related sections - + Various bugs in zoo_add_auth in C +
    - - ZOOKEEPER-189 - - forrest build not validated xml of input documents - + Zookeeper service is down when SyncRequestProcessor meets any exception. +
    - - ZOOKEEPER-188 - - Check that election port is present for all servers - + JMXEnv uses System.err instead of logging +
    - - ZOOKEEPER-185 - - Improved version of FLETest - + zkServer stop command incorrectly reading comment lines in zoo.cfg +
    - - ZOOKEEPER-184 - - tests: An explicit include derective is needed for the usage of memcpy functions - + Multiop submitted to non-leader always fails due to timeout +
    - - ZOOKEEPER-183 - - Array subscript is above array bounds in od_completion, src/cli.c. - + NEW_LEADER should be queued not sent to match the Zab 1.0 protocol on the twiki +
    - - ZOOKEEPER-182 - - zookeeper_init accepts empty host-port string and returns valid pointer to zhandle_t. - + release audit failing for a number of new files +
    - - ZOOKEEPER-17 - - zookeeper_init doc needs clarification - + jenkins is reporting two warnings, fix these +
    - - ZOOKEEPER-181 - - Some Source Forge Documents did not get moved over: javaExample, zookeeperTutorial, zookeeperInternals - + server shutdown is not stopping threads +
    - - ZOOKEEPER-180 - - Placeholder sections needed in document for new topics that the umbrella jira discusses - + zkpython fails tests under python 2.4 +
    - - ZOOKEEPER-179 - - Programmer's Guide "Basic Operations" section is missing content - + incorrect stat output +
    - - ZOOKEEPER-178 - - FLE test. - + ZooKeeperServer not starting on leader due to a race condition +
    - - ZOOKEEPER-159 - - Cover two corner cases of leader election - + ObserverTest.testObserver fails at particular point after several runs of ant junt.run -Dtestcase +
    - - ZOOKEEPER-156 - - update programmer guide with acl details from old wiki page - + significant regression in client (c/python) performance +
    - - ZOOKEEPER-154 - - reliability graph diagram in overview doc needs context - + Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer +
    - - ZOOKEEPER-157 - - Peer can't find existing leader - + Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election +
    - - ZOOKEEPER-155 - - improve "the zookeeper project" section of overview doc - + Log truncation truncating log too much - can cause data loss +
    - - ZOOKEEPER-140 - - Deadlock in QuorumCnxManager - + better eclipse support in tests +
    - - ZOOKEEPER-147 - - This is version of the documents with most of the [tbd...] scrubbed out - + ZooKeeper fails to run with IKVM +
    - - ZOOKEEPER-150 - - zookeeper build broken - + fix build for java 7 +
    - - ZOOKEEPER-136 - - sync causes hang in all followers of quorum. - + FD leak when network unreachable +
    - - ZOOKEEPER-134 - - findbugs cleanup - + Fix problems with Kerberos TGT renewal +
    - - ZOOKEEPER-133 - - hudson tests failing intermittently - + Send AuthFailed event to client if SASL authentication fails +
    - - ZOOKEEPER-144 - - add tostring support for watcher event, and enums for event type/state - + For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. +
    - - ZOOKEEPER-21 - - Improve zk ctor/watcher - + ant package is not including many of the bin scripts in the package (zkServer.sh for example) +
    - - ZOOKEEPER-142 - - Provide Javadoc as to the maximum size of the data byte array that may be stored within a znode - + SASL authorizedID being incorrectly set: should use getHostName() rather than getServiceName() +
    - - ZOOKEEPER-93 - - Create Documentation for Zookeeper - + Zookeeper systest is missing Junit Classes +
    - - ZOOKEEPER-117 - - threading issues in Leader election - + Sequential node creation does not use always use digits in node name given certain Locales. +
    - - ZOOKEEPER-137 - - client watcher objects can lose events - + zkServer.sh stop action is not conformat with LSB para 20.2 Init Script Actions +
    - - ZOOKEEPER-131 - - Old leader election can elect a dead leader over and over again - + ERRORs being logged when queued responses are sent after socket has closed. +
    + Improvements + +
    - - ZOOKEEPER-130 - - update build.xml to support apache release process - + zookeeper should install include headers in /usr/local/include/zookeeper +
    - - ZOOKEEPER-118 - - findbugs flagged switch statement in followerrequestprocessor.run - + Async methods shouldnt throw exceptions +
    - - ZOOKEEPER-115 - - Potential NPE in QuorumCnxManager - + zkpython's C code could do with a style clean-up +
    - - ZOOKEEPER-114 - - cleanup ugly event messages in zookeeper client - + configure.ac has instructions which override the contents of CFLAGS and CXXFLAGS. +
    - - ZOOKEEPER-112 - - src/java/main ZooKeeper.java has test code embedded into it. - + Improve junit test integration - log harness information +
    - - ZOOKEEPER-39 - - Use Watcher objects rather than boolean on read operations. - + use netty to handle client connections +
    - - ZOOKEEPER-97 - - supports optional output directory in code generator. - + Add python example script +
    - - ZOOKEEPER-101 - - Integrate ZooKeeper with "violations" feature on hudson - + Log visualisation +
    - - ZOOKEEPER-105 - - Catch Zookeeper exceptions and print on the stderr. - + Add server id to message logs +
    - - ZOOKEEPER-42 - - Change Leader Election to fast tcp. - + Improve FLE log messages +
    - - ZOOKEEPER-48 - - auth_id now handled correctly when no auth ids present - + c client source with AI_ADDRCONFIG cannot be compiled with early glibc +
    - - ZOOKEEPER-44 - - Create sequence flag children with prefixes of 0's so that they can be lexicographically sorted. - + Improved REST Interface +
    - - ZOOKEEPER-108 - - Fix sync operation reordering on a Quorum. - + Add ZooKeeper version information to zkpython +
    - - ZOOKEEPER-25 - - Fuse module for Zookeeper. - + Make zookeeper.is_unrecoverable return True or False and not an integer +
    - - ZOOKEEPER-58 - - Race condition on ClientCnxn.java - + Hedwig created ledgers with hardcoded Bookkeeper ensemble and quorum size. Make these a server config parameter instead. +
    - - ZOOKEEPER-56 - - Add clover support to build.xml. - + Hedwig C++ client improvements +
    - - ZOOKEEPER-75 - - register the ZooKeeper mailing lists with nabble.com - + Allow non-numeric version strings +
    - - ZOOKEEPER-54 - - remove sleeps in the tests. - + enhance zkServer.sh for easier zookeeper automation-izing +
    - - ZOOKEEPER-55 - - build.xml failes to retrieve a release number from SVN and the ant target "dist" fails - + Fork Hadoop common's test-patch.sh and modify for Zookeeper +
    - - ZOOKEEPER-89 - - invoke WhenOwnerListener.whenNotOwner when the ZK connection fails - + passing null for path_buffer in zoo_create +
    - - ZOOKEEPER-90 - - invoke WhenOwnerListener.whenNotOwner when the ZK session expires and the znode is the leader - + allow configuration parameters for log4j.properties +
    - - ZOOKEEPER-82 - - Make the ZooKeeperServer more DI friendly. - + Code improvements +
    - - ZOOKEEPER-110 - - Build script relies on svnant, which is not compatible with subversion 1.5 working copies - + ZkClient ignores command if there are any space in front of it +
    - - ZOOKEEPER-111 - - Significant cleanup of existing tests. - + The connection permutation in get_addrs uses a weak and inefficient shuffle +
    - - ZOOKEEPER-122 - - Fix NPE in jute's Utils.toCSVString. - + zkCli is overly sensitive to to spaces. +
    - - ZOOKEEPER-123 - - Fix the wrong class is specified for the logger. - + Increase default for maxClientCnxns +
    - - ZOOKEEPER-2 - - Fix synchronization issues in QuorumPeer and FastLeader election. - + Small improvements to LeaderElection and Vote classes +
    - - ZOOKEEPER-125 - - Remove unwanted class declaration in FastLeaderElection. - + Simple leader election recipe +
    - - ZOOKEEPER-61 - - Address in client/server test cases. - + In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. +
    - - ZOOKEEPER-75 - - cleanup the library directory - + CLONE - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. +
    - - ZOOKEEPER-109 - - cleanup of NPE and Resource issue nits found by static analysis - + quorum send & recv workers are missing thread names +
    - - ZOOKEEPER-76 - - Commit 677109 removed the cobertura library, but not the build targets. - + Deprecate AuthFLE and LE +
    - - ZOOKEEPER-63 - - Race condition in client close - + Please add a few svn:ignore properties +
    - - ZOOKEEPER-70 - - Add skeleton forrest doc structure for ZooKeeper - + Fix compiler (eclipse) warnings in (generated) jute code +
    - - ZOOKEEPER-79 - - Document jacob's leader election on the wiki recipes page - + New 4lw for short simple monitoring ldck +
    + Features + +
    - - ZOOKEEPER-73 - - Move ZK wiki from SourceForge to Apache - + Need procedure to garbage collect ledgers +
    - - ZOOKEEPER-72 - - Initial creation/setup of ZooKeeper ASF site. - + Ledger size in bytes +
    - - ZOOKEEPER-71 - - Determine what to do re ZooKeeper Changelog - + add "diskless" ensemble support +
    - - ZOOKEEPER-68 - - parseACLs in ZooKeeper.java fails to parse elements of ACL, should be lastIndexOf rather than IndexOf - + Bookie recovery +
    - - ZOOKEEPER-130 - - update build.xml to support apache release process. - + Recursively delete a znode - zkCli.sh rmr /node +
    - - ZOOKEEPER-131 - - Fix Old leader election can elect a dead leader over and over again. - + Add monitoring four-letter word +
    - - ZOOKEEPER-137 - - client watcher objects can lose events - + Add C# generation to Jute +
    - - ZOOKEEPER-117 - - threading issues in Leader election - + A large scale pub/sub system +
    - - ZOOKEEPER-128 - - test coverage on async client operations needs to be improved - + Add tools and recipes for monitoring as a contrib +
    - - ZOOKEEPER-127 - - Use of non-standard election ports in config breaks services - + Web-based Administrative Interface +
    - - ZOOKEEPER-53 - - tests failing on solaris. - + Native Windows version of C client +
    - - ZOOKEEPER-172 - - FLE Test - + Support Kerberos authentication of clients. +
    - - ZOOKEEPER-41 - - Sample startup script - + MT Native Version of Windows C Client +
    - - ZOOKEEPER-33 - - Better ACL management - + Create an package integration project +
    - - ZOOKEEPER-49 - - SetACL does not work - + support distinct JVMFLAGS for zookeeper server in zkServer.sh and zookeeper client in zkCli.sh +
    - - ZOOKEEPER-20 - - Child watches are not triggered when the node is deleted - + Implement function in C client to determine which host you're currently connected to. +
    - - ZOOKEEPER-15 - - handle failure better in build.xml:test - + automating log and snapshot cleaning +
    + Tasks + +
    - - ZOOKEEPER-11 - - ArrayList is used instead of List - + numerous misspellings "succesfully" +
    - - ZOOKEEPER-45 - - Restructure the SVN repository after initial import - + users cannot migrate from 3.4->3.3->3.4 server code against a single datadir +
    + Tests + +
    - - ZOOKEEPER-1 - - Initial ZooKeeper code contribution from Yahoo! - + ZooKeeper System Tests +
    -Changes Since ZooKeeper 2.2.1 +
    +Changes Since ZooKeeper 3.3.0 @@ -244,1003 +59,1888 @@ Version 2.2.1 code, documentation, binaries, etc... are still accessible on - - - ZOOKEEPER-43 - - - Server side of auto reset watches. - - - - - - ZOOKEEPER-132 - - - Create Enum to replace CreateFlag in ZooKepper.create method - - - - - - ZOOKEEPER-139 - - - Create Enums for WatcherEvent's KeeperState and EventType - - - - - - ZOOKEEPER-18 - - - keeper state inconsistency - - - - - - ZOOKEEPER-38 - - - headers in log/snap files - - - - - - ZOOKEEPER-8 - - - Stat enchaned to include num of children and size - - - - - - ZOOKEEPER-6 - - - List of problem identifiers in zookeeper.h - - - - - - ZOOKEEPER-7 - - - Use enums rather than ints for types and state - - - - - - ZOOKEEPER-27 - - - Unique DB identifiers for servers and clients - - - - - - ZOOKEEPER-32 - - - CRCs for ZooKeeper data - - - - - - ZOOKEEPER-33 - - - Better ACL management - - - - - - ZOOKEEPER-203 - - - fix datadir typo in releasenotes - - - - - - ZOOKEEPER-145 - - - write detailed release notes for users migrating from 2.x to 3.0 - - - - - - ZOOKEEPER-23 - - - Auto reset of watches on reconnect - - - - - - ZOOKEEPER-191 - - - forrest docs for upgrade. - - - - - - ZOOKEEPER-201 - - - validate magic number when reading snapshot and transaction logs - - - - - - ZOOKEEPER-200 - - - the magic number for snapshot and log must be different - - - - - - ZOOKEEPER-199 - - - fix log messages in persistence code - - - - - - ZOOKEEPER-197 - - - create checksums for snapshots - - - - - - ZOOKEEPER-198 - - - apache license header missing from FollowerSyncRequest.java - - - - - - ZOOKEEPER-5 - - - Upgrade Feature in Zookeeper server. - - - - - - ZOOKEEPER-194 - - - Fix terminology in zookeeperAdmin.xml - - - - - - ZOOKEEPER-151 - - - Document change to server configuration - - - - - - ZOOKEEPER-193 - - - update java example doc to compile with latest zookeeper - - - - - - ZOOKEEPER-187 - - - CreateMode api docs missing - - - - - - ZOOKEEPER-186 - - - add new "releasenotes.xml" to forrest documentation - - - - - - ZOOKEEPER-190 - - - Reorg links to docs and navs to docs into related sections - - - - - - ZOOKEEPER-189 - - - forrest build not validated xml of input documents - - - - - - ZOOKEEPER-188 - - - Check that election port is present for all servers - - - - - - ZOOKEEPER-185 - - - Improved version of FLETest - - - - - - ZOOKEEPER-184 - - - tests: An explicit include derective is needed for the usage of memcpy functions - - - - - - ZOOKEEPER-183 - - - Array subscript is above array bounds in od_completion, src/cli.c. - - - - - - ZOOKEEPER-182 - - - zookeeper_init accepts empty host-port string and returns valid pointer to zhandle_t. - - - - - - ZOOKEEPER-17 - - - zookeeper_init doc needs clarification - - - - - - ZOOKEEPER-181 - - - Some Source Forge Documents did not get moved over: javaExample, zookeeperTutorial, zookeeperInternals - - - - - - ZOOKEEPER-180 - - - Placeholder sections needed in document for new topics that the umbrella jira discusses - - - - - - ZOOKEEPER-179 - - - Programmer's Guide "Basic Operations" section is missing content - - - - - - ZOOKEEPER-178 - - - FLE test. - - - - - - ZOOKEEPER-159 - - - Cover two corner cases of leader election - - - - - - ZOOKEEPER-156 - - - update programmer guide with acl details from old wiki page - - - - - - ZOOKEEPER-154 - - - reliability graph diagram in overview doc needs context - - - - - - ZOOKEEPER-157 - - - Peer can't find existing leader - - - - - - ZOOKEEPER-155 - - - improve "the zookeeper project" section of overview doc - - - - - - ZOOKEEPER-140 - - - Deadlock in QuorumCnxManager - - - - - - ZOOKEEPER-147 - - - This is version of the documents with most of the [tbd...] scrubbed out - - - - - - ZOOKEEPER-150 - - - zookeeper build broken - - - - - - ZOOKEEPER-136 - - - sync causes hang in all followers of quorum. - - - - - - ZOOKEEPER-134 - - - findbugs cleanup - - - - - - ZOOKEEPER-133 - - - hudson tests failing intermittently - - - - - - ZOOKEEPER-144 - - - add tostring support for watcher event, and enums for event type/state - - - - - - ZOOKEEPER-21 - - - Improve zk ctor/watcher - - - - - - ZOOKEEPER-142 - - - Provide Javadoc as to the maximum size of the data byte array that may be stored within a znode - - - - - - ZOOKEEPER-93 - - - Create Documentation for Zookeeper - - - - - - ZOOKEEPER-117 - - - threading issues in Leader election - - - - - - ZOOKEEPER-137 - - - client watcher objects can lose events - - - - - - ZOOKEEPER-131 - - - Old leader election can elect a dead leader over and over again - - - - - - ZOOKEEPER-130 - - - update build.xml to support apache release process - - - - - - ZOOKEEPER-118 - - - findbugs flagged switch statement in followerrequestprocessor.run - - - - - - ZOOKEEPER-115 - - - Potential NPE in QuorumCnxManager - - - - - - ZOOKEEPER-114 - - - cleanup ugly event messages in zookeeper client - - - - - - ZOOKEEPER-112 - - - src/java/main ZooKeeper.java has test code embedded into it. - - - - - - ZOOKEEPER-39 - - - Use Watcher objects rather than boolean on read operations. - - - - - - ZOOKEEPER-97 - - - supports optional output directory in code generator. - - - - - - ZOOKEEPER-101 - - - Integrate ZooKeeper with "violations" feature on hudson - - - - - - ZOOKEEPER-105 - - - Catch Zookeeper exceptions and print on the stderr. - - - - - - ZOOKEEPER-42 - - - Change Leader Election to fast tcp. - - - - - - ZOOKEEPER-48 - - - auth_id now handled correctly when no auth ids present - - - - - - ZOOKEEPER-44 - - - Create sequence flag children with prefixes of 0's so that they can be lexicographically sorted. - - - - - - ZOOKEEPER-108 - - - Fix sync operation reordering on a Quorum. - - - - - - ZOOKEEPER-25 - - - Fuse module for Zookeeper. - - - - - - ZOOKEEPER-58 - - - Race condition on ClientCnxn.java - - - - - - ZOOKEEPER-56 - - - Add clover support to build.xml. - - - - - - ZOOKEEPER-75 - - - register the ZooKeeper mailing lists with nabble.com - - - - - - ZOOKEEPER-54 - - - remove sleeps in the tests. - - - - - - ZOOKEEPER-55 - - - build.xml failes to retrieve a release number from SVN and the ant target "dist" fails - - - - - - ZOOKEEPER-89 - - - invoke WhenOwnerListener.whenNotOwner when the ZK connection fails - - - - - - ZOOKEEPER-90 - - - invoke WhenOwnerListener.whenNotOwner when the ZK session expires and the znode is the leader - - - - - - ZOOKEEPER-82 - - - Make the ZooKeeperServer more DI friendly. - - - - - - ZOOKEEPER-110 - - - Build script relies on svnant, which is not compatible with subversion 1.5 working copies - - - - - - ZOOKEEPER-111 - - - Significant cleanup of existing tests. - - - - - - ZOOKEEPER-122 - - - Fix NPE in jute's Utils.toCSVString. - - - - - - ZOOKEEPER-123 - - - Fix the wrong class is specified for the logger. - - - - - - ZOOKEEPER-2 - - - Fix synchronization issues in QuorumPeer and FastLeader election. - - - - - - ZOOKEEPER-125 - - - Remove unwanted class declaration in FastLeaderElection. - - - - - - ZOOKEEPER-61 - - - Address in client/server test cases. - - - - - - ZOOKEEPER-75 - - - cleanup the library directory - - - - - - ZOOKEEPER-109 - - - cleanup of NPE and Resource issue nits found by static analysis - - - - - - ZOOKEEPER-76 - - - Commit 677109 removed the cobertura library, but not the build targets. - - - - - - ZOOKEEPER-63 - - - Race condition in client close - - - - - - ZOOKEEPER-70 - - - Add skeleton forrest doc structure for ZooKeeper - - - - - - ZOOKEEPER-79 - - - Document jacob's leader election on the wiki recipes page - - - - - - ZOOKEEPER-73 - - - Move ZK wiki from SourceForge to Apache - - - - - - ZOOKEEPER-72 - - - Initial creation/setup of ZooKeeper ASF site. - - - - - - ZOOKEEPER-71 - - - Determine what to do re ZooKeeper Changelog - - - - - - ZOOKEEPER-68 - - - parseACLs in ZooKeeper.java fails to parse elements of ACL, should be lastIndexOf rather than IndexOf - - - - - - ZOOKEEPER-130 - - - update build.xml to support apache release process. - - - - - - ZOOKEEPER-131 - - - Fix Old leader election can elect a dead leader over and over again. - - - - - - ZOOKEEPER-137 - - - client watcher objects can lose events - - - - - - ZOOKEEPER-117 - - - threading issues in Leader election - - - - - - ZOOKEEPER-128 - - - test coverage on async client operations needs to be improved - - - - - - ZOOKEEPER-127 - - - Use of non-standard election ports in config breaks services - - - - - - ZOOKEEPER-53 - - - tests failing on solaris. - - - - - - ZOOKEEPER-172 - - - FLE Test - - - - - - ZOOKEEPER-41 - - - Sample startup script - - - - - - ZOOKEEPER-33 - - - Better ACL management - - - - - - ZOOKEEPER-49 - - - SetACL does not work - - - - - - ZOOKEEPER-20 - - - Child watches are not triggered when the node is deleted - - - - - - ZOOKEEPER-15 - - - handle failure better in build.xml:test - - - - - - ZOOKEEPER-11 - - - ArrayList is used instead of List - - - - - - ZOOKEEPER-45 - - - Restructure the SVN repository after initial import - - - - - - ZOOKEEPER-1 - - - Initial ZooKeeper code contribution from Yahoo! - + + Sub-Tasks + + + + + + + + + ZOOKEEPER-784 + + + server-side functionality for read-only mode + + + + + + ZOOKEEPER-798 + + + Fixup loggraph for FLE changes + + + + + + ZOOKEEPER-839 + + + deleteRecursive does not belong to the other methods + + + + + + ZOOKEEPER-908 + + + Remove code duplication and inconsistent naming in ClientCnxn.Packet creation + + + + + + ZOOKEEPER-909 + + + Extract NIO specific code from ClientCnxn + + + + + + ZOOKEEPER-966 + + + Client side for multi + + + + + + ZOOKEEPER-967 + + + Server side decoding and function dispatch + + + + + + ZOOKEEPER-968 + + + Database multi-update + + + + + + ZOOKEEPER-1042 + + + Generate zookeeper test jar for maven installation + + + + + + ZOOKEEPER-1081 + + + modify leader/follower code to correctly deal with new leader + + + + + + ZOOKEEPER-1082 + + + modify leader election to correctly take into account current epoch + + + + + + ZOOKEEPER-1150 + + + fix for this patch to compile on windows... + + + + + + ZOOKEEPER-1160 + + + test timeouts are too small + + + + + + ZOOKEEPER-1201 + + + Clean SaslServerCallbackHandler.java + + + + + Bug Fixes + + + + + + + + + ZOOKEEPER-335 + + + zookeeper servers should commit the new leader txn to their logs. + + + + + + ZOOKEEPER-418 + + + Need nifty zookeeper browser + + + + + + ZOOKEEPER-603 + + + zkpython should do a better job of freeing memory under error conditions + + + + + + ZOOKEEPER-662 + + + Too many CLOSE_WAIT socket state on a server + + + + + + ZOOKEEPER-690 + + + AsyncTestHammer test fails on hudson. + + + + + + ZOOKEEPER-719 + + + Add throttling to BookKeeper client + + + + + + ZOOKEEPER-720 + + + Use zookeeper-{version}-sources.jar instead of zookeeper-{version}-src.jar to publish sources in the Maven repository + + + + + + ZOOKEEPER-722 + + + zkServer.sh uses sh's builtin echo on BSD, behaves incorrectly. + + + + + + ZOOKEEPER-731 + + + Zookeeper#delete , #create - async versions miss a verb in the javadoc + + + + + + ZOOKEEPER-734 + + + QuorumPeerTestBase.java and ZooKeeperServerMainTest.java do not handle windows path correctly + + + + + + ZOOKEEPER-735 + + + cppunit test testipv6 assumes that the machine is ipv6 enabled. + + + + + + ZOOKEEPER-737 + + + some 4 letter words may fail with netcat (nc) + + + + + + ZOOKEEPER-738 + + + zookeeper.jute.h fails to compile with -pedantic + + + + + + ZOOKEEPER-741 + + + root level create on REST proxy fails + + + + + + ZOOKEEPER-742 + + + Deallocatng None on writes + + + + + + ZOOKEEPER-746 + + + learner outputs session id to log in dec (should be hex) + + + + + + ZOOKEEPER-749 + + + OSGi metadata not included in binary only jar + + + + + + ZOOKEEPER-750 + + + move maven artifacts into "dist-maven" subdir of the release (package target) + + + + + + ZOOKEEPER-758 + + + zkpython segfaults on invalid acl with missing key + + + + + + ZOOKEEPER-763 + + + Deadlock on close w/ zkpython / c client + + + + + + ZOOKEEPER-764 + + + Observer elected leader due to inconsistent voting view + + + + + + ZOOKEEPER-766 + + + forrest recipes docs don't mention the lock/queue recipe implementations available in the release + + + + + + ZOOKEEPER-769 + + + Leader can treat observers as quorum members + + + + + + ZOOKEEPER-772 + + + zkpython segfaults when watcher from async get children is invoked. + + + + + + ZOOKEEPER-774 + + + Recipes tests are slightly outdated: they do not compile against JUnit 4.8 + + + + + + ZOOKEEPER-777 + + + setting acl on a non existant node should return no node error + + + + + + ZOOKEEPER-782 + + + Incorrect C API documentation for Watches + + + + + + ZOOKEEPER-783 + + + committedLog in ZKDatabase is not properly synchronized + + + + + + ZOOKEEPER-787 + + + groupId in deployed pom is wrong + + + + + + ZOOKEEPER-790 + + + Last processed zxid set prematurely while establishing leadership + + + + + + ZOOKEEPER-792 + + + zkpython memory leak + + + + + + ZOOKEEPER-794 + + + Callbacks are not invoked when the client is closed + + + + + + ZOOKEEPER-795 + + + eventThread isn't shutdown after a connection "session expired" event coming + + + + + + ZOOKEEPER-796 + + + zkServer.sh should support an external PIDFILE variable + + + + + + ZOOKEEPER-800 + + + zoo_add_auth returns ZOK if zookeeper handle is in ZOO_CLOSED_STATE + + + + + + ZOOKEEPER-804 + + + c unit tests failing due to "assertion cptr failed" + + + + + + ZOOKEEPER-813 + + + maven install is broken due to incorrect organisation + + + + + + ZOOKEEPER-814 + + + monitoring scripts are missing apache license headers + + + + + + ZOOKEEPER-820 + + + update c unit tests to ensure "zombie" java server processes don't cause failure + + + + + + ZOOKEEPER-822 + + + Leader election taking a long time to complete + + + + + + ZOOKEEPER-831 + + + BookKeeper: Throttling improved for reads + + + + + + ZOOKEEPER-844 + + + handle auth failure in java client + + + + + + ZOOKEEPER-846 + + + zookeeper client doesn't shut down cleanly on the close call + + + + + + ZOOKEEPER-854 + + + BookKeeper does not compile due to changes in the ZooKeeper code + + + + + + ZOOKEEPER-855 + + + clientPortBindAddress should be clientPortAddress + + + + + + ZOOKEEPER-861 + + + Missing the test SSL certificate used for running junit tests. + + + + + + ZOOKEEPER-867 + + + ClientTest is failing on hudson - fd cleanup + + + + + + ZOOKEEPER-870 + + + Zookeeper trunk build broken. + + + + + + ZOOKEEPER-874 + + + FileTxnSnapLog.restore does not call listener + + + + + + ZOOKEEPER-880 + + + QuorumCnxManager$SendWorker grows without bounds + + + + + + ZOOKEEPER-881 + + + ZooKeeperServer.loadData loads database twice + + + + + + ZOOKEEPER-882 + + + Startup loads last transaction from snapshot + + + + + + ZOOKEEPER-884 + + + Remove LedgerSequence references from BookKeeper documentation and comments in tests + + + + + + ZOOKEEPER-888 + + + c-client / zkpython: Double free corruption on node watcher + + + + + + ZOOKEEPER-893 + + + ZooKeeper high cpu usage when invalid requests + + + + + + ZOOKEEPER-897 + + + C Client seg faults during close + + + + + + ZOOKEEPER-898 + + + C Client might not cleanup correctly during close + + + + + + ZOOKEEPER-902 + + + Fix findbug issue in trunk "Malicious code vulnerability" + + + + + + ZOOKEEPER-904 + + + super digest is not actually acting as a full superuser + + + + + + ZOOKEEPER-913 + + + Version parser fails to parse "3.3.2-dev" from build.xml. + + + + + + ZOOKEEPER-917 + + + Leader election selected incorrect leader + + + + + + ZOOKEEPER-919 + + + Ephemeral nodes remains in one of ensemble after deliberate SIGKILL + + + + + + ZOOKEEPER-921 + + + zkPython incorrectly checks for existence of required ACL elements + + + + + + ZOOKEEPER-937 + + + test -e not available on solaris /bin/sh + + + + + + ZOOKEEPER-957 + + + zkCleanup.sh doesn't do anything + + + + + + ZOOKEEPER-958 + + + Flag to turn off autoconsume in hedwig c++ client + + + + + + ZOOKEEPER-961 + + + Watch recovery after disconnection when connection string contains a prefix + + + + + + ZOOKEEPER-962 + + + leader/follower coherence issue when follower is receiving a DIFF + + + + + + ZOOKEEPER-963 + + + Make Forrest work with JDK6 + + + + + + ZOOKEEPER-965 + + + Need a multi-update command to allow multiple znodes to be updated safely + + + + + + ZOOKEEPER-975 + + + new peer goes in LEADING state even if ensemble is online + + + + + + ZOOKEEPER-976 + + + ZooKeeper startup script doesn't use JAVA_HOME + + + + + + ZOOKEEPER-981 + + + Hang in zookeeper_close() in the multi-threaded C client + + + + + + ZOOKEEPER-983 + + + running zkServer.sh start remotely using ssh hangs + + + + + + ZOOKEEPER-985 + + + Test BookieRecoveryTest fails on trunk. + + + + + + ZOOKEEPER-1006 + + + QuorumPeer "Address already in use" -- regression in 3.3.3 + + + + + + ZOOKEEPER-1007 + + + iarchive leak in C client + + + + + + ZOOKEEPER-1013 + + + zkServer.sh usage message should mention all startup options + + + + + + ZOOKEEPER-1027 + + + chroot not transparent in zoo_create() + + + + + + ZOOKEEPER-1028 + + + In python bindings, zookeeper.set2() should return a stat dict but instead returns None + + + + + + ZOOKEEPER-1033 + + + c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src + + + + + + ZOOKEEPER-1034 + + + perl bindings should automatically find the zookeeper c-client headers + + + + + + ZOOKEEPER-1046 + + + Creating a new sequential node results in a ZNODEEXISTS error + + + + + + ZOOKEEPER-1049 + + + Session expire/close flooding renders heartbeats to delay significantly + + + + + + ZOOKEEPER-1051 + + + SIGPIPE in Zookeeper 0.3.* when send'ing after cluster disconnection + + + + + + ZOOKEEPER-1052 + + + Findbugs warning in QuorumPeer.ResponderThread.run() + + + + + + ZOOKEEPER-1055 + + + check for duplicate ACLs in addACL() and create() + + + + + + ZOOKEEPER-1058 + + + fix typo in opToString for getData + + + + + + ZOOKEEPER-1059 + + + stat command isses on non-existing node causes NPE + + + + + + ZOOKEEPER-1060 + + + QuorumPeer takes a long time to shutdown + + + + + + ZOOKEEPER-1061 + + + Zookeeper stop fails if start called twice + + + + + + ZOOKEEPER-1063 + + + Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes + + + + + + ZOOKEEPER-1068 + + + Documentation and default config suggest incorrect location for Zookeeper state + + + + + + ZOOKEEPER-1069 + + + Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log + + + + + + ZOOKEEPER-1073 + + + address a documentation issue in ZOOKEEPER-1030 + + + + + + ZOOKEEPER-1074 + + + zkServer.sh is missing nohup/sleep, which are necessary for remote invocation + + + + + + ZOOKEEPER-1076 + + + some quorum tests are unnecessarily extending QuorumBase + + + + + + ZOOKEEPER-1083 + + + Javadoc for WatchedEvent not being generated + + + + + + ZOOKEEPER-1086 + + + zookeeper test jar has non mavenised dependency. + + + + + + ZOOKEEPER-1087 + + + ForceSync VM arguement not working when set to "no" + + + + + + ZOOKEEPER-1088 + + + delQuota does not remove the quota node and subesquent setquota calls for that path will fail + + + + + + ZOOKEEPER-1090 + + + Race condition while taking snapshot can lead to not restoring data tree correctly + + + + + + ZOOKEEPER-1091 + + + when the chrootPath of ClientCnxn is not null and the Watches of zooKeeper is not null and the method primeConnection(SelectionKey k) of ClientCnxn Occurred again for some reason ,then the wrong watcher clientPath is sended to server + + + + + + ZOOKEEPER-1097 + + + Quota is not correctly rehydrated on snapshot reload + + + + + + ZOOKEEPER-1101 + + + Upload zookeeper-test maven artifacts to maven repository. + + + + + + ZOOKEEPER-1108 + + + Various bugs in zoo_add_auth in C + + + + + + ZOOKEEPER-1109 + + + Zookeeper service is down when SyncRequestProcessor meets any exception. + + + + + + ZOOKEEPER-1111 + + + JMXEnv uses System.err instead of logging + + + + + + ZOOKEEPER-1119 + + + zkServer stop command incorrectly reading comment lines in zoo.cfg + + + + + + ZOOKEEPER-1124 + + + Multiop submitted to non-leader always fails due to timeout + + + + + + ZOOKEEPER-1136 + + + NEW_LEADER should be queued not sent to match the Zab 1.0 protocol on the twiki + + + + + + ZOOKEEPER-1138 + + + release audit failing for a number of new files + + + + + + ZOOKEEPER-1139 + + + jenkins is reporting two warnings, fix these + + + + + + ZOOKEEPER-1140 + + + server shutdown is not stopping threads + + + + + + ZOOKEEPER-1141 + + + zkpython fails tests under python 2.4 + + + + + + ZOOKEEPER-1142 + + + incorrect stat output + + + + + + ZOOKEEPER-1144 + + + ZooKeeperServer not starting on leader due to a race condition + + + + + + ZOOKEEPER-1145 + + + ObserverTest.testObserver fails at particular point after several runs of ant junt.run -Dtestcase + + + + + + ZOOKEEPER-1146 + + + significant regression in client (c/python) performance + + + + + + ZOOKEEPER-1152 + + + Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer + + + + + + ZOOKEEPER-1154 + + + Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election + + + + + + ZOOKEEPER-1156 + + + Log truncation truncating log too much - can cause data loss + + + + + + ZOOKEEPER-1165 + + + better eclipse support in tests + + + + + + ZOOKEEPER-1168 + + + ZooKeeper fails to run with IKVM + + + + + + ZOOKEEPER-1171 + + + fix build for java 7 + + + + + + ZOOKEEPER-1174 + + + FD leak when network unreachable + + + + + + ZOOKEEPER-1181 + + + Fix problems with Kerberos TGT renewal + + + + + + ZOOKEEPER-1185 + + + Send AuthFailed event to client if SASL authentication fails + + + + + + ZOOKEEPER-1189 + + + For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. + + + + + + ZOOKEEPER-1190 + + + ant package is not including many of the bin scripts in the package (zkServer.sh for example) + + + + + + ZOOKEEPER-1195 + + + SASL authorizedID being incorrectly set: should use getHostName() rather than getServiceName() + + + + + + ZOOKEEPER-1203 + + + Zookeeper systest is missing Junit Classes + + + + + + ZOOKEEPER-1206 + + + Sequential node creation does not use always use digits in node name given certain Locales. + + + + + + ZOOKEEPER-1212 + + + zkServer.sh stop action is not conformat with LSB para 20.2 Init Script Actions + + + + + + ZOOKEEPER-1237 + + + ERRORs being logged when queued responses are sent after socket has closed. + + + + + Improvements + + + + + + + + + ZOOKEEPER-494 + + + zookeeper should install include headers in /usr/local/include/zookeeper + + + + + + ZOOKEEPER-500 + + + Async methods shouldnt throw exceptions + + + + + + ZOOKEEPER-631 + + + zkpython's C code could do with a style clean-up + + + + + + ZOOKEEPER-636 + + + configure.ac has instructions which override the contents of CFLAGS and CXXFLAGS. + + + + + + ZOOKEEPER-724 + + + Improve junit test integration - log harness information + + + + + + ZOOKEEPER-733 + + + use netty to handle client connections + + + + + + ZOOKEEPER-765 + + + Add python example script + + + + + + ZOOKEEPER-773 + + + Log visualisation + + + + + + ZOOKEEPER-788 + + + Add server id to message logs + + + + + + ZOOKEEPER-789 + + + Improve FLE log messages + + + + + + ZOOKEEPER-797 + + + c client source with AI_ADDRCONFIG cannot be compiled with early glibc + + + + + + ZOOKEEPER-809 + + + Improved REST Interface + + + + + + ZOOKEEPER-821 + + + Add ZooKeeper version information to zkpython + + + + + + ZOOKEEPER-853 + + + Make zookeeper.is_unrecoverable return True or False and not an integer + + + + + + ZOOKEEPER-862 + + + Hedwig created ledgers with hardcoded Bookkeeper ensemble and quorum size. Make these a server config parameter instead. + + + + + + ZOOKEEPER-864 + + + Hedwig C++ client improvements + + + + + + ZOOKEEPER-891 + + + Allow non-numeric version strings + + + + + + ZOOKEEPER-905 + + + enhance zkServer.sh for easier zookeeper automation-izing + + + + + + ZOOKEEPER-926 + + + Fork Hadoop common's test-patch.sh and modify for Zookeeper + + + + + + ZOOKEEPER-977 + + + passing null for path_buffer in zoo_create + + + + + + ZOOKEEPER-980 + + + allow configuration parameters for log4j.properties + + + + + + ZOOKEEPER-993 + + + Code improvements + + + + + + ZOOKEEPER-997 + + + ZkClient ignores command if there are any space in front of it + + + + + + ZOOKEEPER-1018 + + + The connection permutation in get_addrs uses a weak and inefficient shuffle + + + + + + ZOOKEEPER-1025 + + + zkCli is overly sensitive to to spaces. + + + + + + ZOOKEEPER-1030 + + + Increase default for maxClientCnxns + + + + + + ZOOKEEPER-1094 + + + Small improvements to LeaderElection and Vote classes + + + + + + ZOOKEEPER-1095 + + + Simple leader election recipe + + + + + + ZOOKEEPER-1103 + + + In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. + + + + + + ZOOKEEPER-1104 + + + CLONE - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. + + + + + + ZOOKEEPER-1143 + + + quorum send & recv workers are missing thread names + + + + + + ZOOKEEPER-1153 + + + Deprecate AuthFLE and LE + + + + + + ZOOKEEPER-1166 + + + Please add a few svn:ignore properties + + + + + + ZOOKEEPER-1169 + + + Fix compiler (eclipse) warnings in (generated) jute code + + + + + + ZOOKEEPER-1243 + + + New 4lw for short simple monitoring ldck + + + + + Features + + + + + + + + + ZOOKEEPER-464 + + + Need procedure to garbage collect ledgers + + + + + + ZOOKEEPER-465 + + + Ledger size in bytes + + + + + + ZOOKEEPER-546 + + + add "diskless" ensemble support + + + + + + ZOOKEEPER-712 + + + Bookie recovery + + + + + + ZOOKEEPER-729 + + + Recursively delete a znode - zkCli.sh rmr /node + + + + + + ZOOKEEPER-744 + + + Add monitoring four-letter word + + + + + + ZOOKEEPER-747 + + + Add C# generation to Jute + + + + + + ZOOKEEPER-775 + + + A large scale pub/sub system + + + + + + ZOOKEEPER-799 + + + Add tools and recipes for monitoring as a contrib + + + + + + ZOOKEEPER-808 + + + Web-based Administrative Interface + + + + + + ZOOKEEPER-859 + + + Native Windows version of C client + + + + + + ZOOKEEPER-938 + + + Support Kerberos authentication of clients. + + + + + + ZOOKEEPER-992 + + + MT Native Version of Windows C Client + + + + + + ZOOKEEPER-999 + + + Create an package integration project + + + + + + ZOOKEEPER-1012 + + + support distinct JVMFLAGS for zookeeper server in zkServer.sh and zookeeper client in zkCli.sh + + + + + + ZOOKEEPER-1020 + + + Implement function in C client to determine which host you're currently connected to. + + + + + + ZOOKEEPER-1107 + + + automating log and snapshot cleaning + + + + + Tasks + + + + + + + + + ZOOKEEPER-754 + + + numerous misspellings "succesfully" + + + + + + ZOOKEEPER-1149 + + + users cannot migrate from 3.4->3.3->3.4 server code against a single datadir + + + + + Tests + + + + + + + + + ZOOKEEPER-239 + + + ZooKeeper System Tests +
    From d00f43a19bd42060ee2375ffbcab4653f6927778 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Fri, 28 Oct 2011 14:29:03 +0000 Subject: [PATCH 015/444] ZOOKEEPER-1264: FollowerResyncConcurrencyTest failing intermittently (phunt via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1190352 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../test/FollowerResyncConcurrencyTest.java | 143 ++++++++++-------- .../org/apache/zookeeper/test/QuorumTest.java | 10 +- 3 files changed, 89 insertions(+), 66 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d2c07116565..f36a89b35b6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -347,6 +347,8 @@ BUGFIXES: ZOOKEEPER-1181. Fix problems with Kerberos TGT renewal. (Eugene Koontz via mahadev) + + ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing intermittently. (phunt via camille) IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information diff --git a/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java b/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java index 2752d5c76d6..b62ec20ccf0 100644 --- a/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java +++ b/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java @@ -27,6 +27,7 @@ import java.util.HashSet; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.log4j.Logger; @@ -43,12 +44,11 @@ public class FollowerResyncConcurrencyTest extends ZKTestCase { - volatile int counter = 0; - volatile int errors = 0; - private static final Logger LOG = Logger.getLogger(FollowerResyncConcurrencyTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; + private volatile int counter = 0; + private volatile int errors = 0; /** * See ZOOKEEPER-962. This tests for one of the bugs hit while fixing this, @@ -64,8 +64,9 @@ public class FollowerResyncConcurrencyTest extends ZKTestCase { * @throws KeeperException */ @Test - public void testResyncBySnapThenDiffAfterFollowerCrashes () - throws IOException, InterruptedException, KeeperException, Throwable{ + public void testResyncBySnapThenDiffAfterFollowerCrashes() + throws IOException, InterruptedException, KeeperException, Throwable + { final Semaphore sem = new Semaphore(0); QuorumUtil qu = new QuorumUtil(1); @@ -75,31 +76,36 @@ public void testResyncBySnapThenDiffAfterFollowerCrashes () CountdownWatcher watcher3 = new CountdownWatcher(); int index = 1; - while(qu.getPeer(index).peer.leader == null) + while(qu.getPeer(index).peer.leader == null) { index++; + } Leader leader = qu.getPeer(index).peer.leader; - assertNotNull(leader); - /* - * Reusing the index variable to select a follower to connect to - */ + + /* Reusing the index variable to select a follower to connect to */ index = (index == 1) ? 2 : 1; + LOG.info("Connecting to follower:" + index); + qu.shutdown(index); - final ZooKeeper zk3 = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(3).peer.getClientPort(), 1000,watcher3); - watcher3.waitForConnected(CONNECTION_TIMEOUT); + + final ZooKeeper zk3 = + createClient(qu.getPeer(3).peer.getClientPort(), watcher3); + LOG.info("zk3 has session id 0x" + Long.toHexString(zk3.getSessionId())); + zk3.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); qu.restart(index); - ZooKeeper zk = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), 1000, watcher1); + final ZooKeeper zk1 = + createClient(qu.getPeer(index).peer.getClientPort(), watcher1); + LOG.info("zk1 has session id 0x" + Long.toHexString(zk1.getSessionId())); - ZooKeeper zk2 = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), 1000, watcher2); - - watcher1.waitForConnected(CONNECTION_TIMEOUT); - watcher2.waitForConnected(CONNECTION_TIMEOUT); + final ZooKeeper zk2 = + createClient(qu.getPeer(index).peer.getClientPort(), watcher2); + LOG.info("zk2 has session id 0x" + Long.toHexString(zk2.getSessionId())); - zk.create("/first", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - Thread t = new Thread(new Runnable() { + zk1.create("/first", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + Thread mytestfooThread = new Thread(new Runnable() { @Override public void run() { @@ -115,8 +121,6 @@ public void processResult(int rc, String path, Object ctx, String name) { if(counter == 14200){ sem.release(); } - - } }, null); if(i%10==0){ @@ -131,7 +135,6 @@ public void processResult(int rc, String path, Object ctx, String name) { } }); - for(int i = 0; i < 13000; i++) { zk3.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, new AsyncCallback.StringCallback() { @@ -144,8 +147,6 @@ public void processResult(int rc, String path, Object ctx, String name) { if(counter == 14200){ sem.release(); } - - } }, null); @@ -158,7 +159,7 @@ public void processResult(int rc, String path, Object ctx, String name) { qu.restart(index); Thread.sleep(300); qu.shutdown(index); - t.start(); + mytestfooThread.start(); Thread.sleep(300); qu.restart(index); LOG.info("Setting up server: " + index); @@ -169,7 +170,6 @@ public void processResult(int rc, String path, Object ctx, String name) { if(i%50 == 0) { zk2.create("/newbaz", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, new AsyncCallback.StringCallback() { - @Override public void processResult(int rc, String path, Object ctx, String name) { counter++; @@ -179,22 +179,28 @@ public void processResult(int rc, String path, Object ctx, String name) { if(counter == 14200){ sem.release(); } - - } }, null); } } // Wait until all updates return - if(!sem.tryAcquire(20000, TimeUnit.MILLISECONDS)) { + if(!sem.tryAcquire(ClientBase.CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { LOG.warn("Did not aquire semaphore fast enough"); } - t.join(10000); + mytestfooThread.join(ClientBase.CONNECTION_TIMEOUT); + if (mytestfooThread.isAlive()) { + LOG.error("mytestfooThread is still alive"); + } Thread.sleep(1000); - verifyState(qu, index, leader); + verifyState(qu, index, leader); + + zk1.close(); + zk2.close(); + zk3.close(); + qu.shutdownAll(); } /** @@ -222,8 +228,9 @@ public void processResult(int rc, String path, Object ctx, String name) { */ @Test - public void testResyncByDiffAfterFollowerCrashes () - throws IOException, InterruptedException, KeeperException, Throwable{ + public void testResyncByDiffAfterFollowerCrashes() + throws IOException, InterruptedException, KeeperException, Throwable + { final Semaphore sem = new Semaphore(0); QuorumUtil qu = new QuorumUtil(1); @@ -232,34 +239,35 @@ public void testResyncByDiffAfterFollowerCrashes () CountdownWatcher watcher2 = new CountdownWatcher(); CountdownWatcher watcher3 = new CountdownWatcher(); - int index = 1; - while(qu.getPeer(index).peer.leader == null) + while(qu.getPeer(index).peer.leader == null) { index++; + } Leader leader = qu.getPeer(index).peer.leader; - assertNotNull(leader); - /* - * Reusing the index variable to select a follower to connect to - */ + /* Reusing the index variable to select a follower to connect to */ index = (index == 1) ? 2 : 1; + LOG.info("Connecting to follower:" + index); + + final ZooKeeper zk1 = + createClient(qu.getPeer(index).peer.getClientPort(), watcher1); + LOG.info("zk1 has session id 0x" + Long.toHexString(zk1.getSessionId())); + + final ZooKeeper zk2 = + createClient(qu.getPeer(index).peer.getClientPort(), watcher2); + LOG.info("zk2 has session id 0x" + Long.toHexString(zk2.getSessionId())); - ZooKeeper zk = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), 1000, watcher1); + final ZooKeeper zk3 = + createClient(qu.getPeer(3).peer.getClientPort(), watcher3); + LOG.info("zk3 has session id 0x" + Long.toHexString(zk3.getSessionId())); - ZooKeeper zk2 = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), 1000,watcher2); - final ZooKeeper zk3 = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(3).peer.getClientPort(), 1000, watcher3); - watcher1.waitForConnected(CONNECTION_TIMEOUT); - watcher2.waitForConnected(CONNECTION_TIMEOUT); - watcher3.waitForConnected(CONNECTION_TIMEOUT); - zk.create("/first", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk1.create("/first", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk2.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); - final AtomicBoolean runNow = new AtomicBoolean(false); - Thread t = new Thread(new Runnable() { - + Thread mytestfooThread = new Thread(new Runnable() { @Override public void run() { int inSyncCounter = 0; @@ -276,8 +284,6 @@ public void processResult(int rc, String path, Object ctx, String name) { if(counter > 7300){ sem.release(); } - - } }, null); @@ -286,8 +292,7 @@ public void processResult(int rc, String path, Object ctx, String name) { } catch (Exception e) { } inSyncCounter++; - } - else { + } else { Thread.yield(); } } @@ -295,7 +300,7 @@ public void processResult(int rc, String path, Object ctx, String name) { } }); - t.start(); + mytestfooThread.start(); for(int i = 0; i < 5000; i++) { zk2.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, new AsyncCallback.StringCallback() { @@ -308,8 +313,6 @@ public void processResult(int rc, String path, Object ctx, String name) { if(counter > 7300){ sem.release(); } - - } }, null); @@ -317,7 +320,6 @@ public void processResult(int rc, String path, Object ctx, String name) { qu.shutdown(index); Thread.sleep(1100); LOG.info("Shutting down s1"); - } if(i == 1100 || i == 1150 || i == 1200) { Thread.sleep(1000); @@ -330,7 +332,6 @@ public void processResult(int rc, String path, Object ctx, String name) { LOG.info("Setting up server: " + index); } - if(i>=1000 && i%2== 0) { zk3.create("/newbaz", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, new AsyncCallback.StringCallback() { @@ -343,8 +344,6 @@ public void processResult(int rc, String path, Object ctx, String name) { if(counter > 7300){ sem.release(); } - - } }, null); } @@ -354,15 +353,35 @@ public void processResult(int rc, String path, Object ctx, String name) { } // Wait until all updates return - if(!sem.tryAcquire(15000, TimeUnit.MILLISECONDS)) { + if(!sem.tryAcquire(ClientBase.CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { LOG.warn("Did not aquire semaphore fast enough"); } - t.join(10000); + mytestfooThread.join(ClientBase.CONNECTION_TIMEOUT); + if (mytestfooThread.isAlive()) { + LOG.error("mytestfooThread is still alive"); + } + Thread.sleep(1000); // Verify that server is following and has the same epoch as the leader verifyState(qu, index, leader); + zk1.close(); + zk2.close(); + zk3.close(); + + qu.shutdownAll(); + } + + private static DisconnectableZooKeeper createClient(int port, + CountdownWatcher watcher) + throws IOException, TimeoutException, InterruptedException + { + DisconnectableZooKeeper zk = new DisconnectableZooKeeper( + "127.0.0.1:" + port, ClientBase.CONNECTION_TIMEOUT, watcher); + + watcher.waitForConnected(CONNECTION_TIMEOUT); + return zk; } private void verifyState(QuorumUtil qu, int index, Leader leader) { diff --git a/src/java/test/org/apache/zookeeper/test/QuorumTest.java b/src/java/test/org/apache/zookeeper/test/QuorumTest.java index 13d0e83e8e8..0926811f67d 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumTest.java @@ -378,9 +378,11 @@ public void testNoLogBeforeLeaderEstablishment () */ index = (index == 1) ? 2 : 1; - ZooKeeper zk = new DisconnectableZooKeeper("127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), 1000, new Watcher() { - public void process(WatchedEvent event) { - }}); + ZooKeeper zk = new DisconnectableZooKeeper( + "127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), + ClientBase.CONNECTION_TIMEOUT, new Watcher() { + public void process(WatchedEvent event) { } + }); zk.create("/blah", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); @@ -412,7 +414,7 @@ public void processResult(int rc, String path, Object ctx, } // Wait until all updates return - sem.tryAcquire(15000, TimeUnit.MILLISECONDS); + sem.tryAcquire(15, TimeUnit.SECONDS); // Verify that server is following and has the same epoch as the leader Assert.assertTrue("Not following", qu.getPeer(index).peer.follower != null); From 3926cb41e43629d1fea88b13cd8b0a9e5a4acec8 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Tue, 1 Nov 2011 07:13:21 +0000 Subject: [PATCH 016/444] ZOOKEEPER-1268. problems with read only mode, intermittent test failures and ERRORs in the log. (phunt via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1195853 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../content/xdocs/zookeeperAdmin.xml | 29 +++++++ .../zookeeper/server/quorum/QuorumPeer.java | 79 +++++++++++-------- .../zookeeper/test/ReadOnlyModeTest.java | 5 +- 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f36a89b35b6..b75bdfca857 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -350,6 +350,9 @@ BUGFIXES: ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing intermittently. (phunt via camille) + ZOOKEEPER-1268. problems with read only mode, intermittent test failures + and ERRORs in the log. (phunt via mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index ee212e8347b..a8fef594a47 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1015,6 +1015,35 @@ server.3=zoo3:2888:3888 +

    + Experimental Options/Features + + New features that are currently considered experimental. + + + + Read Only Mode Server + + + (Java system property: readonlymode.enabled) + + New in 3.4.0: + Setting this value to true enables Read Only Mode server + support (disabled by default). ROM allows clients + sessions which requested ROM support to connect to the + server even when the server might be partitioned from + the quorum. In this mode ROM clients can still read + values from the ZK service, but will be unable to write + values and see changes from other clients. See + ZOOKEEPER-784 for more details. + + + + + +
    +
    Unsafe Options 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 c53043eae18..35df0c9bafa 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -666,42 +666,55 @@ public void run() { case LOOKING: LOG.info("LOOKING"); - // Create read-only server but don't start it immediately - final ReadOnlyZooKeeperServer roZk = new ReadOnlyZooKeeperServer( - logFactory, this, - new ZooKeeperServer.BasicDataTreeBuilder(), - this.zkDb); - - // Instead of starting roZk immediately, wait some grace - // period before we decide we're partitioned. - // - // Thread is used here because otherwise it would require - // changes in each of election strategy classes which is - // unnecessary code coupling. - Thread roZkMgr = new Thread() { - public void run() { - try { - // lower-bound grace period to 2 secs - sleep(Math.max(2000, tickTime)); - if (ServerState.LOOKING.equals(getPeerState())) { - roZk.startup(); + if (Boolean.getBoolean("readonlymode.enabled")) { + LOG.info("Attempting to start ReadOnlyZooKeeperServer"); + + // Create read-only server but don't start it immediately + final ReadOnlyZooKeeperServer roZk = new ReadOnlyZooKeeperServer( + logFactory, this, + new ZooKeeperServer.BasicDataTreeBuilder(), + this.zkDb); + + // Instead of starting roZk immediately, wait some grace + // period before we decide we're partitioned. + // + // Thread is used here because otherwise it would require + // changes in each of election strategy classes which is + // unnecessary code coupling. + Thread roZkMgr = new Thread() { + public void run() { + try { + // lower-bound grace period to 2 secs + sleep(Math.max(2000, tickTime)); + if (ServerState.LOOKING.equals(getPeerState())) { + roZk.startup(); + } + } catch (InterruptedException e) { + LOG.info("Interrupted while attempting to start ReadOnlyZooKeeperServer, not started"); + } catch (Exception e) { + LOG.error("FAILED to start ReadOnlyZooKeeperServer", e); } - } catch (Exception e) { - LOG.error("FAILED to start ReadOnlyZooKeeperServer", e); } + }; + try { + roZkMgr.start(); + setCurrentVote(makeLEStrategy().lookForLeader()); + } catch (Exception e) { + LOG.warn("Unexpected exception",e); + setPeerState(ServerState.LOOKING); + } finally { + // If the thread is in the the grace period, interrupt + // to come out of waiting. + roZkMgr.interrupt(); + roZk.shutdown(); + } + } else { + try { + setCurrentVote(makeLEStrategy().lookForLeader()); + } catch (Exception e) { + LOG.warn("Unexpected exception", e); + setPeerState(ServerState.LOOKING); } - }; - try { - roZkMgr.start(); - setCurrentVote(makeLEStrategy().lookForLeader()); - } catch (Exception e) { - LOG.warn("Unexpected exception",e); - setPeerState(ServerState.LOOKING); - } finally { - // If the thread is in the the grace period, interrupt - // to come out of waiting. - roZkMgr.interrupt(); - roZk.shutdown(); } break; case OBSERVING: diff --git a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java index 0b4157f67ae..c705a7bba56 100644 --- a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java +++ b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java @@ -52,11 +52,13 @@ public class ReadOnlyModeTest extends ZKTestCase { @Before public void setUp() throws Exception { + System.setProperty("readonlymode.enabled", "true"); qu.startQuorum(); } @After public void tearDown() throws Exception { + System.setProperty("readonlymode.enabled", "false"); qu.tearDown(); } @@ -133,9 +135,10 @@ public void process(WatchedEvent event) { long start = System.currentTimeMillis(); while (!(zk.getState() == States.CONNECTEDREADONLY)) { Thread.sleep(200); + // FIXME this was originally 5 seconds, but realistically, on random/slow/virt hosts, there is no way to guarantee this Assert.assertTrue("Can't connect to the server", System .currentTimeMillis() - - start < 5000); + - start < 30000); } // At this point states list should contain, in the given order, From 48d66e2e0d207727ba70396efa9ba559f63febd7 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Tue, 1 Nov 2011 14:31:37 +0000 Subject: [PATCH 017/444] ZOOKEEPER-1246. Dead code in PrepRequestProcessor catch Exception block (camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1196013 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/PrepRequestProcessor.java | 53 +++++--- .../server/PrepRequestProcessorTest.java | 122 ++++++++++++++++++ 3 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java diff --git a/CHANGES.txt b/CHANGES.txt index b75bdfca857..cfae21c49bd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -352,6 +352,8 @@ BUGFIXES: ZOOKEEPER-1268. problems with read only mode, intermittent test failures and ERRORs in the log. (phunt via mahadev) + + ZOOKEEPER-1246. Dead code in PrepRequestProcessor catch Exception block. (camille) IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 077cab6c95a..145c3ebef86 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -19,6 +19,7 @@ package org.apache.zookeeper.server; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashSet; @@ -291,14 +292,16 @@ static void checkACL(ZooKeeperServer zks, List acl, int perm, * @param record */ @SuppressWarnings("unchecked") - protected void pRequest2Txn(int type, long zxid, Request request, Record record) throws KeeperException { + protected void pRequest2Txn(int type, long zxid, Request request, Record record, boolean deserialize) throws KeeperException, IOException { request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid, zks.getTime(), type); switch (type) { - case OpCode.create: + case OpCode.create: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); - CreateRequest createRequest = (CreateRequest)record; + CreateRequest createRequest = (CreateRequest)record; + if(deserialize) + ByteBufferInputStream.byteBuffer2Record(request.request, createRequest); String path = createRequest.getPath(); int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 || failCreate) { @@ -357,6 +360,8 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record) case OpCode.delete: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); DeleteRequest deleteRequest = (DeleteRequest)record; + if(deserialize) + ByteBufferInputStream.byteBuffer2Record(request.request, deleteRequest); path = deleteRequest.getPath(); lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 @@ -385,6 +390,8 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record) case OpCode.setData: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); SetDataRequest setDataRequest = (SetDataRequest)record; + if(deserialize) + ByteBufferInputStream.byteBuffer2Record(request.request, setDataRequest); path = setDataRequest.getPath(); nodeRecord = getRecordForPath(path); checkACL(zks, nodeRecord.acl, ZooDefs.Perms.WRITE, @@ -403,6 +410,8 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record) case OpCode.setACL: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); SetACLRequest setAclRequest = (SetACLRequest)record; + if(deserialize) + ByteBufferInputStream.byteBuffer2Record(request.request, setAclRequest); path = setAclRequest.getPath(); listACL = removeDuplicates(setAclRequest.getAcl()); if (!fixupACL(request.authInfo, listACL)) { @@ -457,6 +466,8 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record) case OpCode.check: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); CheckVersionRequest checkVersionRequest = (CheckVersionRequest)record; + if(deserialize) + ByteBufferInputStream.byteBuffer2Record(request.request, checkVersionRequest); path = checkVersionRequest.getPath(); nodeRecord = getRecordForPath(path); checkACL(zks, nodeRecord.acl, ZooDefs.Perms.READ, @@ -489,34 +500,34 @@ protected void pRequest(Request request) { switch (request.type) { case OpCode.create: CreateRequest createRequest = new CreateRequest(); - ByteBufferInputStream.byteBuffer2Record(request.request, createRequest); - pRequest2Txn(request.type, zks.getNextZxid(), request, createRequest); + pRequest2Txn(request.type, zks.getNextZxid(), request, createRequest, true); break; case OpCode.delete: - DeleteRequest deleteRequest = new DeleteRequest(); - ByteBufferInputStream.byteBuffer2Record(request.request, deleteRequest); - pRequest2Txn(request.type, zks.getNextZxid(), request, deleteRequest); + DeleteRequest deleteRequest = new DeleteRequest(); + pRequest2Txn(request.type, zks.getNextZxid(), request, deleteRequest, true); break; case OpCode.setData: - SetDataRequest setDataRequest = new SetDataRequest(); - ByteBufferInputStream.byteBuffer2Record(request.request, setDataRequest); - pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest); + SetDataRequest setDataRequest = new SetDataRequest(); + pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest, true); break; case OpCode.setACL: - SetACLRequest setAclRequest = new SetACLRequest(); - ByteBufferInputStream.byteBuffer2Record(request.request, setAclRequest); - pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest); + SetACLRequest setAclRequest = new SetACLRequest(); + pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest, true); break; case OpCode.check: - CheckVersionRequest checkRequest = new CheckVersionRequest(); - ByteBufferInputStream.byteBuffer2Record(request.request, checkRequest); - pRequest2Txn(request.type, zks.getNextZxid(), request, checkRequest); + CheckVersionRequest checkRequest = new CheckVersionRequest(); + pRequest2Txn(request.type, zks.getNextZxid(), request, checkRequest, true); break; case OpCode.multi: MultiTransactionRecord multiRequest = new MultiTransactionRecord(); - ByteBufferInputStream.byteBuffer2Record(request.request, multiRequest); + try { + ByteBufferInputStream.byteBuffer2Record(request.request, multiRequest); + } catch(IOException e) { + request.hdr = new TxnHeader(request.sessionId, request.cxid, zks.getNextZxid(), + zks.getTime(), OpCode.multi); + throw e; + } List txns = new ArrayList(); - //Each op in a multi-op must have the same zxid! long zxid = zks.getNextZxid(); KeeperException ke = null; @@ -540,7 +551,7 @@ protected void pRequest(Request request) { /* Prep the request and convert to a Txn */ else { try { - pRequest2Txn(op.getType(), zxid, request, subrequest); + pRequest2Txn(op.getType(), zxid, request, subrequest, false); } catch (KeeperException e) { if (ke == null) { ke = e; @@ -579,7 +590,7 @@ protected void pRequest(Request request) { //create/close session don't require request record case OpCode.createSession: case OpCode.closeSession: - pRequest2Txn(request.type, zks.getNextZxid(), request, null); + pRequest2Txn(request.type, zks.getNextZxid(), request, null, true); break; //All the rest don't need to create a Txn - just verify session diff --git a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java new file mode 100644 index 00000000000..a38e36ce73e --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java @@ -0,0 +1,122 @@ +/** + * 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.server; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.PrintWriter; +import java.nio.ByteBuffer; + +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.KeeperException.Code; +import org.apache.zookeeper.KeeperException.SessionExpiredException; +import org.apache.zookeeper.KeeperException.SessionMovedException; +import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.server.PrepRequestProcessor; +import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.RequestProcessor; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.SyncRequestProcessor; +import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.txn.ErrorTxn; +import org.junit.Assert; +import org.junit.Test; + +public class PrepRequestProcessorTest extends ClientBase { + private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); + private static final int CONNECTION_TIMEOUT = 3000; + + @Test + public void testPRequest() throws Exception { + File tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(100); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); + zks.sessionTracker = new MySessionTracker(); + PrepRequestProcessor processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); + Request foo = new Request(null, 1l, 1, OpCode.create, ByteBuffer.allocate(3), null); + processor.pRequest(foo); + } + + + private class MyRequestProcessor implements RequestProcessor { + @Override + public void processRequest(Request request) { + Assert.assertEquals("Request should have marshalling error", new ErrorTxn(Code.MARSHALLINGERROR.intValue()), request.txn); + + } + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + } + + private class MySessionTracker implements SessionTracker { + @Override + public void addSession(long id, int to) { + // TODO Auto-generated method stub + + } + @Override + public void checkSession(long sessionId, Object owner) + throws SessionExpiredException, SessionMovedException { + // TODO Auto-generated method stub + + } + @Override + public long createSession(int sessionTimeout) { + // TODO Auto-generated method stub + return 0; + } + @Override + public void dumpSessions(PrintWriter pwriter) { + // TODO Auto-generated method stub + + } + @Override + public void removeSession(long sessionId) { + // TODO Auto-generated method stub + + } + @Override + public void setOwner(long id, Object owner) + throws SessionExpiredException { + // TODO Auto-generated method stub + + } + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + @Override + public boolean touchSession(long sessionId, int sessionTimeout) { + // TODO Auto-generated method stub + return false; + } + } +} From 605a4e23d0cf3aabe17739c569d8ccd91db90a1e Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 2 Nov 2011 21:54:01 +0000 Subject: [PATCH 018/444] ZOOKEEPER-1271. testEarlyLeaderAbandonment failing on solaris - clients not retrying connection (mahadev via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1196820 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + ivy.xml | 2 + .../apache/zookeeper/ClientCnxnSocketNIO.java | 38 +++++++-- .../apache/zookeeper/ClientReconnectTest.java | 78 +++++++++++++++++++ 4 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/ClientReconnectTest.java diff --git a/CHANGES.txt b/CHANGES.txt index cfae21c49bd..b238a491d0e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -355,6 +355,9 @@ BUGFIXES: ZOOKEEPER-1246. Dead code in PrepRequestProcessor catch Exception block. (camille) + ZOOKEEPER-1271. testEarlyLeaderAbandonment failing on solaris - + clients not retrying connection (mahadev via phunt) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/ivy.xml b/ivy.xml index f0a29ff6d1e..e0f870257de 100644 --- a/ivy.xml +++ b/ivy.xml @@ -53,6 +53,8 @@ + diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java index b8ebe2ca5ad..ad445040683 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java @@ -176,23 +176,45 @@ void close() { LOG.warn("Ignoring exception during selector close", e); } } - - @Override - void connect(InetSocketAddress addr) throws IOException { + + /** + * create a socket channel. + * @return the created socket channel + * @throws IOException + */ + SocketChannel createSock() throws IOException { SocketChannel sock; sock = SocketChannel.open(); sock.configureBlocking(false); sock.socket().setSoLinger(false, -1); sock.socket().setTcpNoDelay(true); + return sock; + } + + /** + * register with the selection and connect + * @param sock the {@link SocketChannel} + * @param addr the address of remote host + * @throws IOException + */ + void registerAndConnect(SocketChannel sock, InetSocketAddress addr) + throws IOException { + sockKey = sock.register(selector, SelectionKey.OP_CONNECT); + boolean immediateConnect = sock.connect(addr); + if (immediateConnect) { + sendThread.primeConnection(); + } + } + + @Override + void connect(InetSocketAddress addr) throws IOException { + SocketChannel sock = createSock(); try { - sockKey = sock.register(selector, SelectionKey.OP_CONNECT); - boolean immediateConnect = sock.connect(addr); - if (immediateConnect) { - sendThread.primeConnection(); - } + registerAndConnect(sock, addr); } catch (IOException e) { LOG.error("Unable to open socket to " + addr); sock.close(); + throw e; } initialized = false; diff --git a/src/java/test/org/apache/zookeeper/ClientReconnectTest.java b/src/java/test/org/apache/zookeeper/ClientReconnectTest.java new file mode 100644 index 00000000000..8d66a7e6920 --- /dev/null +++ b/src/java/test/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 junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.zookeeper.client.HostProvider; +import org.junit.Test; + +public class ClientReconnectTest extends TestCase { + private SocketChannel sc; + private CountDownLatch countDownLatch = new CountDownLatch(3); + + class MockCnxn extends ClientCnxnSocketNIO { + MockCnxn() throws IOException { + super(); + } + + @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(1111); + when(hostProvider.next(anyLong())).thenReturn(inaddr); + ZooKeeper zk = mock(ZooKeeper.class); + 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(); + } +} \ No newline at end of file From d1b00b32b5b5525dc47d795853f78403ecff1f6b Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sat, 5 Nov 2011 06:14:20 +0000 Subject: [PATCH 019/444] ZOOKEEPER-1192. Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished (Alexander Shraer via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1197890 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../zookeeper/server/quorum/Leader.java | 24 +++++++--- .../zookeeper/server/quorum/Zab1_0Test.java | 44 ++++++++++++++++--- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b238a491d0e..75c544265c4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -358,6 +358,9 @@ BUGFIXES: ZOOKEEPER-1271. testEarlyLeaderAbandonment failing on solaris - clients not retrying connection (mahadev via phunt) + ZOOKEEPER-1192. Leader.waitForEpochAck() checks waitingForNewEpoch instead + of checking electionFinished (Alexander Shraer via mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 6638b1fb9ce..335c7d2a323 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -784,9 +784,15 @@ public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws Interrupt waitingForNewEpoch = false; connectingFollowers.notifyAll(); } else { - connectingFollowers.wait(self.getInitLimit()*self.getTickTime()); + long start = System.currentTimeMillis(); + long cur = start; + long end = start + self.getInitLimit()*self.getTickTime(); + while(waitingForNewEpoch && cur < end) { + connectingFollowers.wait(end - cur); + cur = System.currentTimeMillis(); + } if (waitingForNewEpoch) { - throw new InterruptedException("Out of time to propose an epoch"); + throw new InterruptedException("Timeout while waiting for epoch from quorum"); } } return epoch; @@ -810,10 +816,16 @@ public void waitForEpochAck(long id, StateSummary ss) throws IOException, Interr if (readyToStart && verifier.containsQuorum(electingFollowers)) { electionFinished = true; electingFollowers.notifyAll(); - } else { - electingFollowers.wait(self.getInitLimit()*self.getTickTime()); - if (waitingForNewEpoch) { - throw new InterruptedException("Out of time to propose an epoch"); + } else { + long start = System.currentTimeMillis(); + long cur = start; + long end = start + self.getInitLimit()*self.getTickTime(); + while(!electionFinished && cur < end) { + electingFollowers.wait(end - cur); + cur = System.currentTimeMillis(); + } + if (!electionFinished) { + throw new InterruptedException("Timeout while waiting for epoch to be acked by quorum"); } } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 53aa4eee4e2..a6a86d07f2b 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -44,7 +44,10 @@ import org.apache.zookeeper.server.ZooKeeperServer.DataTreeBuilder; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Leader; +import org.apache.zookeeper.server.quorum.LearnerInfo; +import org.apache.zookeeper.server.quorum.QuorumPacket; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.Zab1_0Test.LeaderConversation; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.util.ZxidUtils; import org.junit.Assert; @@ -124,7 +127,7 @@ static void readPacketSkippingPing(InputArchive ia, QuorumPacket qp) throws IOEx } static public interface LeaderConversation { - void converseWithLeader(InputArchive ia, OutputArchive oa) throws Exception; + void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws Exception; } static public interface FollowerConversation { @@ -160,7 +163,7 @@ public void testConversation(LeaderConversation conversation) throws Exception { OutputArchive oa = BinaryOutputArchive.getArchive(followerSocket .getOutputStream()); - conversation.converseWithLeader(ia, oa); + conversation.converseWithLeader(ia, oa, leader); } finally { recursiveDelete(tmpDir); if (leader != null) { @@ -176,7 +179,7 @@ public void testConversation(LeaderConversation conversation) throws Exception { @Test public void testNormalRun() throws Exception { testConversation(new LeaderConversation() { - public void converseWithLeader(InputArchive ia, OutputArchive oa) + public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException { /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000); @@ -209,7 +212,7 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa) @Test public void testLeaderBehind() throws Exception { testConversation(new LeaderConversation() { - public void converseWithLeader(InputArchive ia, OutputArchive oa) + public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException { /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000); @@ -240,7 +243,38 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa) }); } - + /** + * Tests that when a quorum of followers send LearnerInfo but do not ack the epoch (which is sent + * by the leader upon receipt of LearnerInfo from a quorum), the leader does not start using this epoch + * as it would in the normal case (when a quorum do ack the epoch). This tests ZK-1192 + * @throws Exception + */ + @Test + public void testAbandonBeforeACKEpoch() throws Exception { + testConversation(new LeaderConversation() { + public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) + throws IOException, InterruptedException { + /* we test a normal run. everything should work out well. */ + LearnerInfo li = new LearnerInfo(1, 0x10000); + byte liBytes[] = new byte[12]; + ByteBufferOutputStream.record2ByteBuffer(li, + ByteBuffer.wrap(liBytes)); + QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 0, + liBytes, null); + oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.LEADERINFO, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), + 0x10000); + Thread.sleep(l.self.getInitLimit()*l.self.getTickTime() + 5000); + + // The leader didn't get a quorum of acks - make sure that leader's current epoch is not advanced + Assert.assertEquals(0, l.self.getCurrentEpoch()); + } + }); + } + private void recursiveDelete(File file) { if (file.isFile()) { file.delete(); From 288830d3157d310fb3d66fc036ef3a80dc6dcdd1 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sat, 5 Nov 2011 06:35:54 +0000 Subject: [PATCH 020/444] ZOOKEEPER-1270. testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. (Flavio, Camille and Alexander Shraer via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1197892 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/quorum/Leader.java | 5 +- .../server/quorum/QuorumPeerMainTest.java | 4 +- .../zookeeper/server/quorum/Zab1_0Test.java | 106 ++++++++++++++++++ .../org/apache/zookeeper/test/ClientBase.java | 2 +- 5 files changed, 116 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 75c544265c4..87eb04520f4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -361,6 +361,9 @@ BUGFIXES: ZOOKEEPER-1192. Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished (Alexander Shraer via mahadev) + ZOOKEEPER-1270. testEarlyLeaderAbandonment failing intermittently, + quorum formed, no serving. (Flavio, Camille and Alexander Shraer via mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 335c7d2a323..039dbe6c826 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -780,7 +780,8 @@ public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws Interrupt } connectingFollowers.add(sid); QuorumVerifier verifier = self.getQuorumVerifier(); - if (verifier.containsQuorum(connectingFollowers)) { + if (connectingFollowers.contains(self.getId()) && verifier.containsQuorum(connectingFollowers)) +{ waitingForNewEpoch = false; connectingFollowers.notifyAll(); } else { @@ -813,7 +814,7 @@ public void waitForEpochAck(long id, StateSummary ss) throws IOException, Interr electingFollowers.add(id); } QuorumVerifier verifier = self.getQuorumVerifier(); - if (readyToStart && verifier.containsQuorum(electingFollowers)) { + if (electingFollowers.contains(self.getId()) && verifier.containsQuorum(electingFollowers)) { electionFinished = true; electingFollowers.notifyAll(); } else { 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 45a61c79709..0acf8f26521 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -28,6 +28,7 @@ import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Map; +import java.util.Map.Entry; import java.util.regex.Pattern; import org.apache.log4j.Layout; @@ -319,7 +320,8 @@ private void waitForAll(ZooKeeper[] zks, States state) throws InterruptedExcepti boolean someoneNotConnected = true; while(someoneNotConnected) { if (iterations-- == 0) { - throw new RuntimeException("Waiting too long"); + ClientBase.logAllStackTraces(); + throw new RuntimeException("Waiting too long"); } someoneNotConnected = false; diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index a6a86d07f2b..0f0e159c2bb 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -71,6 +71,112 @@ public void run() { } } } + + public static final class FollowerMockThread extends Thread { + private final Leader leader; + private final long followerSid; + public long epoch = -1; + public String msg = null; + private boolean onlyGetEpochToPropose; + + private FollowerMockThread(long followerSid, Leader leader, boolean onlyGetEpochToPropose) { + this.leader = leader; + this.followerSid = followerSid; + this.onlyGetEpochToPropose = onlyGetEpochToPropose; + } + + public void run() { + if (onlyGetEpochToPropose) { + try { + epoch = leader.getEpochToPropose(followerSid, 0); + } catch (Exception e) { + } + } else { + try{ + leader.waitForEpochAck(followerSid, new StateSummary(0, 0)); + msg = "FollowerMockThread (id = " + followerSid + ") returned from waitForEpochAck"; + } catch (Exception e) { + } + } + } + } + @Test + public void testLeaderInConnectingFollowers() throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + try { + QuorumPeer peer = createQuorumPeer(tmpDir); + leader = createLeader(tmpDir, peer); + peer.leader = leader; + peer.setAcceptedEpoch(5); + + FollowerMockThread f1 = new FollowerMockThread(1, leader, true); + FollowerMockThread f2 = new FollowerMockThread(2, leader, true); + f1.start(); + f2.start(); + + // wait until followers time out in getEpochToPropose - they shouldn't return + // normally because the leader didn't execute getEpochToPropose and so its epoch was not + // accounted for + f1.join(leader.self.getInitLimit()*leader.self.getTickTime() + 5000); + f2.join(leader.self.getInitLimit()*leader.self.getTickTime() + 5000); + + // even though followers timed out, their ids are in connectingFollowers, and their + // epoch were accounted for, so the leader should not block and since it started with + // accepted epoch = 5 it should now have 6 + try { + long epoch = leader.getEpochToPropose(leader.self.getId(), leader.self.getAcceptedEpoch()); + Assert.assertEquals("leader got wrong epoch from getEpochToPropose", 6, epoch); + } catch (Exception e){ + Assert.fail("leader timed out in getEpochToPropose"); + } + } finally { + recursiveDelete(tmpDir); + if (leader != null) { + leader.shutdown("end of test"); + } + } + } + + @Test + public void testLeaderInElectingFollowers() throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + try { + QuorumPeer peer = createQuorumPeer(tmpDir); + leader = createLeader(tmpDir, peer); + peer.leader = leader; + + FollowerMockThread f1 = new FollowerMockThread(1, leader, false); + FollowerMockThread f2 = new FollowerMockThread(2, leader, false); + + // things needed for waitForEpochAck to run (usually in leader.lead(), but we're not running leader here) + leader.readyToStart = true; + leader.leaderStateSummary = new StateSummary(leader.self.getCurrentEpoch(), leader.zk.getLastProcessedZxid()); + + f1.start(); + f2.start(); + + // wait until followers time out in waitForEpochAck - they shouldn't return + // normally because the leader didn't execute waitForEpochAck + f1.join(leader.self.getInitLimit()*leader.self.getTickTime() + 5000); + f2.join(leader.self.getInitLimit()*leader.self.getTickTime() + 5000); + + // make sure that they timed out and didn't return normally + Assert.assertTrue(f1.msg + " without waiting for leader", f1.msg == null); + Assert.assertTrue(f2.msg + " without waiting for leader", f2.msg == null); + } finally { + recursiveDelete(tmpDir); + if (leader != null) { + leader.shutdown("end of test"); + } + } + } + private static final class NullServerCnxnFactory extends ServerCnxnFactory { public void startup(ZooKeeperServer zkServer) throws IOException, InterruptedException { diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 0fc7d258d69..84e17bd9750 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -513,7 +513,7 @@ public static boolean recursiveDelete(File d) { return d.delete(); } - private static void logAllStackTraces() { + public static void logAllStackTraces() { StringBuilder sb = new StringBuilder(); sb.append("Starting logAllStackTraces()\n"); Map threads = Thread.getAllStackTraces(); From 41ec40fb9b16fe7c63f62b5095ff959b4973bc05 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Sat, 5 Nov 2011 20:14:21 +0000 Subject: [PATCH 021/444] ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing intermittently. ZOOKEEPER-1282. Learner.java not following Zab 1.0 protocol - setCurrentEpoch should be done upon receipt of NEWLEADER (before acking it) and not upon receipt of UPTODATE. ZOOKEEPER-1291. AcceptedEpoch not updated at leader before it proposes the epoch to followers. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1198043 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 9 + .../zookeeper/server/quorum/Leader.java | 8 +- .../zookeeper/server/quorum/Learner.java | 34 ++- .../zookeeper/server/quorum/Zab1_0Test.java | 280 ++++++++++++++++-- .../test/FollowerResyncConcurrencyTest.java | 12 +- 5 files changed, 299 insertions(+), 44 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 87eb04520f4..24f4db18cc1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -363,6 +363,15 @@ BUGFIXES: ZOOKEEPER-1270. testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. (Flavio, Camille and Alexander Shraer via mahadev) + + ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing + intermittently. (breed, camille and Alex Shraer via camille) + + ZOOKEEPER-1282. Learner.java not following Zab 1.0 protocol - + setCurrentEpoch should be done upon receipt of NEWLEADER + (before acking it) and not upon receipt of UPTODATE (breed via camille) + + ZOOKEEPER-1291. AcceptedEpoch not updated at leader before it proposes the epoch to followers. (Alex Shraer via camille) IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 039dbe6c826..ff761720b94 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -311,7 +311,6 @@ void lead() throws IOException, InterruptedException { readyToStart = true; long epoch = getEpochToPropose(self.getId(), self.getAcceptedEpoch()); - self.setAcceptedEpoch(epoch); zk.setZxid(ZxidUtils.makeZxid(epoch, 0)); @@ -770,7 +769,7 @@ synchronized public long startForwarding(LearnerHandler handler, } private HashSet connectingFollowers = new HashSet(); - public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException { + public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException { synchronized(connectingFollowers) { if (!waitingForNewEpoch) { return epoch; @@ -782,8 +781,9 @@ public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws Interrupt QuorumVerifier verifier = self.getQuorumVerifier(); if (connectingFollowers.contains(self.getId()) && verifier.containsQuorum(connectingFollowers)) { - waitingForNewEpoch = false; - connectingFollowers.notifyAll(); + waitingForNewEpoch = false; + self.setAcceptedEpoch(epoch); + connectingFollowers.notifyAll(); } else { long start = System.currentTimeMillis(); long cur = start; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index a97a5431ff1..f4a77314721 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -312,8 +312,10 @@ protected long registerWithLeader(int pktType) throws IOException{ protected void syncWithLeader(long newLeaderZxid) throws IOException, InterruptedException{ QuorumPacket ack = new QuorumPacket(Leader.ACK, 0, null, null); QuorumPacket qp = new QuorumPacket(); + long newEpoch = ZxidUtils.getEpochFromZxid(newLeaderZxid); readPacket(qp); + LinkedList packetsCommitted = new LinkedList(); LinkedList packetsNotCommitted = new LinkedList(); synchronized (zk) { if (qp.getType() == Leader.DIFF) { @@ -376,12 +378,16 @@ else if (qp.getType() == Leader.SNAP) { packetsNotCommitted.add(pif); break; case Leader.COMMIT: - pif = packetsNotCommitted.peekFirst(); - if (pif.hdr.getZxid() != qp.getZxid()) { - LOG.warn("Committing " + qp.getZxid() + ", but next proposal is " + pif.hdr.getZxid()); + if (!snapshotTaken) { + pif = packetsNotCommitted.peekFirst(); + if (pif.hdr.getZxid() != qp.getZxid()) { + LOG.warn("Committing " + qp.getZxid() + ", but next proposal is " + pif.hdr.getZxid()); + } else { + zk.getZKDatabase().processTxn(pif.hdr, pif.rec); + packetsNotCommitted.remove(); + } } else { - zk.getZKDatabase().processTxn(pif.hdr, pif.rec); - packetsNotCommitted.remove(); + packetsCommitted.add(qp.getZxid()); } break; case Leader.INFORM: @@ -390,28 +396,34 @@ else if (qp.getType() == Leader.SNAP) { zk.getZKDatabase().processTxn(hdr, txn); break; case Leader.UPTODATE: - if (!snapshotTaken) { + if (!snapshotTaken) { // true for the pre v1.0 case zk.takeSnapshot(); + self.setCurrentEpoch(newEpoch); } self.cnxnFactory.setZooKeeperServer(zk); break outerLoop; case Leader.NEWLEADER: // it will be NEWLEADER in v1.0 zk.takeSnapshot(); + self.setCurrentEpoch(newEpoch); snapshotTaken = true; writePacket(new QuorumPacket(Leader.ACK, newLeaderZxid, null, null), true); break; } } } - long newEpoch = ZxidUtils.getEpochFromZxid(newLeaderZxid); - self.setCurrentEpoch(newEpoch); ack.setZxid(ZxidUtils.makeZxid(newEpoch, 0)); writePacket(ack, true); sock.setSoTimeout(self.tickTime * self.syncLimit); zk.startup(); - //We have to have a commit processor to do this - for(PacketInFlight p: packetsNotCommitted) { - ((FollowerZooKeeperServer)zk).logRequest(p.hdr, p.rec); + // We need to log the stuff that came in between the snapshot and the uptodate + if (zk instanceof FollowerZooKeeperServer) { + FollowerZooKeeperServer fzk = (FollowerZooKeeperServer)zk; + for(PacketInFlight p: packetsNotCommitted) { + fzk.logRequest(p.hdr, p.rec); + } + for(Long zxid: packetsCommitted) { + fzk.commit(zxid); + } } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 0f0e159c2bb..aa8d5475774 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -18,6 +18,7 @@ package org.apache.zookeeper.server.quorum; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -26,7 +27,6 @@ import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; -import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.HashMap; @@ -34,22 +34,24 @@ import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; -import org.apache.zookeeper.ZKUtil; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.Watcher.Event.EventType; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.ByteBufferOutputStream; -import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.server.ZooKeeperServer.DataTreeBuilder; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; -import org.apache.zookeeper.server.quorum.Leader; -import org.apache.zookeeper.server.quorum.LearnerInfo; -import org.apache.zookeeper.server.quorum.QuorumPacket; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; -import org.apache.zookeeper.server.quorum.Zab1_0Test.LeaderConversation; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.util.ZxidUtils; +import org.apache.zookeeper.txn.CreateTxn; +import org.apache.zookeeper.txn.SetDataTxn; +import org.apache.zookeeper.txn.TxnHeader; import org.junit.Assert; import org.junit.Test; @@ -209,13 +211,6 @@ public void closeSession(long sessionId) { public void closeAll() { } } - static class MockDataTreeBuilder implements DataTreeBuilder { - @Override - public DataTree build() { - return new DataTree(); - } - - } static Socket[] getSocketPair() throws IOException { ServerSocket ss = new ServerSocket(); ss.bind(null); @@ -237,10 +232,10 @@ static public interface LeaderConversation { } static public interface FollowerConversation { - void converseWithFollower(InputArchive ia, OutputArchive oa) throws Exception; + void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception; } - public void testConversation(LeaderConversation conversation) throws Exception { + public void testLeaderConversation(LeaderConversation conversation) throws Exception { Socket pair[] = getSocketPair(); Socket leaderSocket = pair[0]; Socket followerSocket = pair[1]; @@ -281,12 +276,215 @@ public void testConversation(LeaderConversation conversation) throws Exception { } } } - + + public void testFollowerConversation(FollowerConversation conversation) throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + Thread followerThread = null; + ConversableFollower follower = null; + QuorumPeer peer = null; + try { + peer = createQuorumPeer(tmpDir); + follower = createFollower(tmpDir, peer); + peer.follower = follower; + + ServerSocket ss = new ServerSocket(); + ss.bind(null); + follower.setLeaderSocketAddress((InetSocketAddress)ss.getLocalSocketAddress()); + final Follower followerForThread = follower; + + followerThread = new Thread() { + public void run() { + try { + followerForThread.followLeader(); + } catch(Exception e) { + e.printStackTrace(); + } + } + }; + followerThread.start(); + Socket leaderSocket = ss.accept(); + + InputArchive ia = BinaryInputArchive.getArchive(leaderSocket + .getInputStream()); + OutputArchive oa = BinaryOutputArchive.getArchive(leaderSocket + .getOutputStream()); + + conversation.converseWithFollower(ia, oa, follower); + } finally { + if (follower != null) { + follower.shutdown(); + } + if (followerThread != null) { + followerThread.interrupt(); + followerThread.join(); + } + if (peer != null) { + peer.shutdown(); + } + recursiveDelete(tmpDir); + } + } + + @Test + public void testNormalFollowerRun() throws Exception { + testFollowerConversation(new FollowerConversation() { + @Override + public void converseWithFollower(InputArchive ia, OutputArchive oa, + Follower f) throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile(); + File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); + try { + Assert.assertEquals(0, f.self.getAcceptedEpoch()); + Assert.assertEquals(0, f.self.getCurrentEpoch()); + + // Setup a database with a single /foo node + ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); + final long firstZxid = ZxidUtils.makeZxid(1, 1); + zkDb.processTxn(new TxnHeader(13, 1313, firstZxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1)); + Stat stat = new Stat(); + Assert.assertEquals("data1", new String(zkDb.getData("/foo", stat, null))); + + QuorumPacket qp = new QuorumPacket(); + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.FOLLOWERINFO, qp.getType()); + Assert.assertEquals(qp.getZxid(), 0); + LearnerInfo learnInfo = new LearnerInfo(); + ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo); + Assert.assertEquals(learnInfo.getProtocolVersion(), 0x10000); + Assert.assertEquals(learnInfo.getServerid(), 0); + + // We are simulating an established leader, so the epoch is 1 + qp.setType(Leader.LEADERINFO); + qp.setZxid(ZxidUtils.makeZxid(1, 0)); + byte protoBytes[] = new byte[4]; + ByteBuffer.wrap(protoBytes).putInt(0x10000); + qp.setData(protoBytes); + oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACKEPOCH, qp.getType()); + Assert.assertEquals(0, qp.getZxid()); + Assert.assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer.wrap(qp.getData()).getInt()); + Assert.assertEquals(1, f.self.getAcceptedEpoch()); + Assert.assertEquals(0, f.self.getCurrentEpoch()); + + // Send the snapshot we created earlier + qp.setType(Leader.SNAP); + qp.setData(new byte[0]); + qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); + oa.writeRecord(qp, null); + zkDb.serializeSnapshot(oa); + oa.writeString("BenWasHere", null); + qp.setType(Leader.NEWLEADER); + qp.setZxid(ZxidUtils.makeZxid(1, 0)); + oa.writeRecord(qp, null); + + // Get the ack of the new leader + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(1, f.self.getAcceptedEpoch()); + Assert.assertEquals(1, f.self.getCurrentEpoch()); + + Assert.assertEquals(firstZxid, f.fzk.getLastProcessedZxid()); + + // Make sure the data was recorded in the filesystem ok + ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); + long lastZxid = zkDb2.loadDataBase(); + Assert.assertEquals("data1", new String(zkDb2.getData("/foo", stat, null))); + Assert.assertEquals(firstZxid, lastZxid); + + // Propose an update + long proposalZxid = ZxidUtils.makeZxid(1, 1000); + proposeSetData(qp, proposalZxid, "data2", 2); + oa.writeRecord(qp, null); + + // We want to track the change with a callback rather than depending on timing + class TrackerWatcher implements Watcher { + boolean changed; + synchronized void waitForChange() throws InterruptedException { + while(!changed) { + wait(); + } + } + @Override + public void process(WatchedEvent event) { + if (event.getType() == EventType.NodeDataChanged) { + synchronized(this) { + changed = true; + notifyAll(); + } + } + } + synchronized public boolean changed() { + return changed; + } + + }; + TrackerWatcher watcher = new TrackerWatcher(); + + // The change should not have happened yet, since we haven't committed + Assert.assertEquals("data1", new String(f.fzk.getZKDatabase().getData("/foo", stat, watcher))); + + // The change should happen now + qp.setType(Leader.COMMIT); + qp.setZxid(proposalZxid); + oa.writeRecord(qp, null); + + qp.setType(Leader.UPTODATE); + qp.setZxid(0); + oa.writeRecord(qp, null); + + // Read the uptodate ack + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(proposalZxid, qp.getZxid()); + + watcher.waitForChange(); + Assert.assertEquals("data2", new String(f.fzk.getZKDatabase().getData("/foo", stat, null))); + + // check and make sure the change is persisted + zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); + lastZxid = zkDb2.loadDataBase(); + Assert.assertEquals("data2", new String(zkDb2.getData("/foo", stat, null))); + Assert.assertEquals(proposalZxid, lastZxid); + } finally { + recursiveDelete(tmpDir); + } + + } + + private void proposeSetData(QuorumPacket qp, long zxid, String data, int version) throws IOException { + qp.setType(Leader.PROPOSAL); + qp.setZxid(zxid); + TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.setData); + SetDataTxn sdt = new SetDataTxn("/foo", data.getBytes(), version); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputArchive boa = BinaryOutputArchive.getArchive(baos); + boa.writeRecord(hdr, null); + boa.writeRecord(sdt, null); + qp.setData(baos.toByteArray()); + } + }); + } + @Test public void testNormalRun() throws Exception { - testConversation(new LeaderConversation() { + testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException { + Assert.assertEquals(0, l.self.getAcceptedEpoch()); + Assert.assertEquals(0, l.self.getCurrentEpoch()); + /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000); byte liBytes[] = new byte[12]; @@ -295,20 +493,30 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 0, liBytes, null); oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.LEADERINFO, qp.getType()); Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); Assert.assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), 0x10000); + Assert.assertEquals(1, l.self.getAcceptedEpoch()); + Assert.assertEquals(0, l.self.getCurrentEpoch()); + qp = new QuorumPacket(Leader.ACKEPOCH, 0, new byte[4], null); oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.DIFF, qp.getType()); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.NEWLEADER, qp.getType()); Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(1, l.self.getAcceptedEpoch()); + Assert.assertEquals(1, l.self.getCurrentEpoch()); + qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.UPTODATE, qp.getType()); } @@ -317,7 +525,7 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) @Test public void testLeaderBehind() throws Exception { - testConversation(new LeaderConversation() { + testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException { /* we test a normal run. everything should work out well. */ @@ -357,7 +565,7 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) */ @Test public void testAbandonBeforeACKEpoch() throws Exception { - testConversation(new LeaderConversation() { + testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException, InterruptedException { /* we test a normal run. everything should work out well. */ @@ -400,10 +608,36 @@ private Leader createLeader(File tmpDir, QuorumPeer peer) addrField.setAccessible(true); addrField.set(peer, new InetSocketAddress(33556)); ZKDatabase zkDb = new ZKDatabase(logFactory); - DataTreeBuilder treeBuilder = new MockDataTreeBuilder(); - LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer, treeBuilder, zkDb); + LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer, new ZooKeeperServer.BasicDataTreeBuilder(), zkDb); return new Leader(peer, zk); } + + static class ConversableFollower extends Follower { + + ConversableFollower(QuorumPeer self, FollowerZooKeeperServer zk) { + super(self, zk); + } + + InetSocketAddress leaderAddr; + public void setLeaderSocketAddress(InetSocketAddress addr) { + leaderAddr = addr; + } + + @Override + protected InetSocketAddress findLeader() { + return leaderAddr; + } + } + private ConversableFollower createFollower(File tmpDir, QuorumPeer peer) + throws IOException { + FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); + peer.setTxnFactory(logFactory); + ZKDatabase zkDb = new ZKDatabase(logFactory); + FollowerZooKeeperServer zk = new FollowerZooKeeperServer(logFactory, peer, new ZooKeeperServer.BasicDataTreeBuilder(), zkDb); + peer.setZKDatabase(zkDb); + return new ConversableFollower(peer, zk); + } + private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, FileNotFoundException { QuorumPeer peer = new QuorumPeer(); diff --git a/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java b/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java index b62ec20ccf0..535baf37aa6 100644 --- a/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java +++ b/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java @@ -109,7 +109,7 @@ public void testResyncBySnapThenDiffAfterFollowerCrashes() @Override public void run() { - for(int i = 0; i < 1000; i++) { + for(int i = 0; i < 3000; i++) { zk3.create("/mytestfoo", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, new AsyncCallback.StringCallback() { @Override @@ -118,7 +118,7 @@ public void processResult(int rc, String path, Object ctx, String name) { if (rc != 0) { errors++; } - if(counter == 14200){ + if(counter == 16200){ sem.release(); } } @@ -144,7 +144,7 @@ public void processResult(int rc, String path, Object ctx, String name) { if (rc != 0) { errors++; } - if(counter == 14200){ + if(counter == 16200){ sem.release(); } } @@ -156,10 +156,10 @@ public void processResult(int rc, String path, Object ctx, String name) { } if(i == 12000){ //Restart off of snap, then get some txns for a log, then shut down + mytestfooThread.start(); qu.restart(index); Thread.sleep(300); - qu.shutdown(index); - mytestfooThread.start(); + qu.shutdown(index); Thread.sleep(300); qu.restart(index); LOG.info("Setting up server: " + index); @@ -176,7 +176,7 @@ public void processResult(int rc, String path, Object ctx, String name) { if (rc != 0) { errors++; } - if(counter == 14200){ + if(counter == 16200){ sem.release(); } } From 051c2848f96b46bbab989ba49b5b9fc04c94faf6 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sat, 5 Nov 2011 23:23:51 +0000 Subject: [PATCH 022/444] Preparing for release 3.4.0 - take 2 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1198109 13f79535-47bb-0310-9956-ffa450edef68 --- docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17122 -> 17122 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13496 -> 13496 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12445 -> 12445 bytes docs/recipes.pdf | Bin 31043 -> 31043 bytes docs/releasenotes.html | 98 ++++++++++++++++++ docs/releasenotes.pdf | Bin 79542 -> 83098 bytes docs/zookeeperAdmin.html | 28 +++++ docs/zookeeperAdmin.pdf | Bin 71973 -> 72515 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133759 -> 133759 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27563 -> 27563 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes .../content/xdocs/releasenotes.xml | 74 +++++++++++++ 23 files changed, 200 insertions(+) diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 1131a1c10997c81506a8cef020d3f3c8d2f2d6de..ccc33f5a88bf4fcace595d5fb831e734f4560d4f 100644 GIT binary patch delta 172 zcmaE#{XTobWKKf^Q$sT&BU6Kob8hkQ8tNLDh8UVz85vob8f?TG+CM7099mj z0>6P;yrHqHp@ECJo2!$vp`nSZk&&gjk%_5`k*TAZxrw8jv7LeqK_#(Vc6MCFC5c5P V6-B9OT!uzwmPTBvs;>TSTmV+$D-Qqw delta 172 zcmaE#{XTobWKIJk6C*?TG+CM7099mj z0>6P;yoHN_rJ<9ViJ7Chqp5|9i=(NfsfCf5vyri(frX`;g`I*8K_#(Vc6MCFC5c5P V6-B9OT!uzwmPTBvs;>TSTmVSs diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index d75aa6e626588b052e63206a099a7b6e16acd0d2..1c895929eccc814ebc350db5ed9f52bdf8ff4270 100644 GIT binary patch delta 159 zcmey?!1=9#b3z-Zp@FHPnURsH$;SS(JiLaw2Bsl~W>!Wc*wWd;&BWEv#nI8i(%jk9+`z(4!G@5M J>5&dhvH%emCw>3` diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index da3d1836f21d4dbadbdcbbbde7cbac9499e1eeb4..e702d5fdeabebb56ccc8206abde08ac709b81435 100644 GIT binary patch delta 150 zcmZoY%-DLEal&LyLjzMoGb1BY!;N!p@$eez8kmL{npqhcS(zGcZswiLh$=K$nco0a zWOD-lu|Q{26IW*!Cue6PM?-TbH)A&wBWH6LClfOZH)Bgj19J;I1sg(2CTAwf007CY BCRG3c delta 150 zcmZoY%-DLEal&Ly10xe7Lj!X|lZ|t3@$eex8kvL`8Cn^bTN#>cZswiLh$=K$nco0a zWOD-lu|Q`xX9H&^Lq|6Q3s*~XBOr1!bux7`a&|Q_bu~7$G_zB%A*5t-W}*xLzDyv0Qd`T*W1c XMI{wQscBq>rWQu#T&k+B{%%|Vf6gm# delta 174 zcmaFV%J`_2al&Ly10xe7Lj!X|vyF3Z@$eex8kvL`8Cn^bTN#>dZswiLh$=K$nco0a zWOD+)vT?ktp_{9-sgZ@TF_1PeFt9LpbTV*qbvANyc5^f`G`3T)A*dvl%g&CgxFoTt Wq@pM_jmyx~!pNLURn^tsjSB#gYAd_| diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index 96fc0f112b57716f9514bfd6c4c0bb864c6b398e..6ec735eb6950d0303c2a472bde9aebbb5ccd095c 100644 GIT binary patch delta 172 zcmbP`J|TTV52vAlsiB#Xk%`5|sh4?p4RsAnLk!KVjEt;IEH;<&PG&?Enk>q1fGV;% zi2uHFysM?5qnnePlc|fdv7>>hnW3|hrIDqpv89`#fr+!3lbwPMK_#(Vc6MCFC5c5P V6-B9OT!uzQ2Buu9s;>TSTmT?yD>MKA delta 172 zcmbP`J|TTV52t~ViIJg!xuMC%sh4?p4Rno6LW~Tp49u+zO*WVEPG&?Enk>q1fGV;% zi2uHFyor;urG<&JqlKl3sjHi#nTv_Dv7@20sgbLriL0rjlbwPMK_#(Vc6MCFC5c5P V6-B9OT!uzQ2Buu9s;>TSTmTy@D_Q^m diff --git a/docs/index.pdf b/docs/index.pdf index d62561db92d68eeb4d558b579de6ef568945f70f..cbfb2e9ca4a317967d2294ce3bcd6dbd562d4bb8 100644 GIT binary patch delta 147 zcmdmyxg&GJdQL+FQ$sT&BNK~_JHGMo8tNLDh8UVz85vobSZvVg%uLMhjqMa{2q>96+e8)s!V@M% delta 147 zcmdmyxg&GJdQJl)6C*96+e8)snMoye diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index cbca834431fb7f40b8c6583f7d12d15a38ac23ce..e7fa4b11802012f8d8d19dddcbbf1a5be4ea26dc 100644 GIT binary patch delta 150 zcmeC`VCw2%n$XE?TG+CbC099mj z4F4kqXG=#nBTHvj6DJdMa{~i6M?*&w6BA<#Q*&n%GgmWnOFIP{LP{n#8pr?uP01wp delta 148 zcmbQ6I5%;^L{0-E6C*?TG+CbC099mj z4F4kqXJa!{Gh<^TGXot<8 delta 174 zcmX^7iSh6!#tD-+4U9~T3=PZ;jW*7{$-`@)Yh)5)WN2kzZe?h+xsi7=BdXA3MScTR zkChanges Since ZooKeeper 3.3.0 + + + ZOOKEEPER-1268 + + + problems with read only mode, intermittent test failures and ERRORs in the log. + + + + + + + + + ZOOKEEPER-1271 + + + testEarlyLeaderAbandonment failing on solaris - clients not retrying connection. + + + + + + + + + ZOOKEEPER-1192 + + +Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished. + + + + + + + + + + ZOOKEEPER-1246 + + + Dead code in PrepRequestProcessor catch Exception block. + + + + + + + + + ZOOKEEPER-1264 + + + FollowerResyncConcurrencyTest failing intermittently. + + + + + + + + + ZOOKEEPER-1270 + + + testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. + + + + + + + + + ZOOKEEPER-1291 + + + AcceptedEpoch not updated at leader before it proposes the epoch to followers. + + + + + + + + + ZOOKEEPER-1282 + + +Learner.java not following Zab 1.0 protocol - setCurrentEpoch should be done upon receipt of NEWLEADER + (before acking it) and not upon receipt of UPTODATE. + + + + + + ZOOKEEPER-335 diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 006afc518289f6612bd719e11f99b33f55da243f..1bfb351b9a3e6e9cdfcd961f05d45f097aa1a11d 100644 GIT binary patch delta 34317 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 a%;X>7;1{r>%f#V;kv!Ax-P$I5nf?cMJ+=%0 delta 32417 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 Ga{eD8b7?#P diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html index 4ffa98d994a..53f0a31a97a 100644 --- a/docs/zookeeperAdmin.html +++ b/docs/zookeeperAdmin.html @@ -291,6 +291,9 @@

    A Guide to Deployment and Administration

    Authentication & Authorization Options
  • +Experimental Options/Features +
  • +
  • Unsafe Options
  • @@ -1371,6 +1374,31 @@

    Authentication & Authorization Options

    connection.

    + + +

    Experimental Options/Features

    +

    New features that are currently considered experimental.

    +
    + +
    +Read Only Mode Server +
    +
    +

    (Java system property: readonlymode.enabled)

    +

    +New in 3.4.0: + Setting this value to true enables Read Only Mode server + support (disabled by default). ROM allows clients + sessions which requested ROM support to connect to the + server even when the server might be partitioned from + the quorum. In this mode ROM clients can still read + values from the ZK service, but will be unable to write + values and see changes from other clients. See + ZOOKEEPER-784 for more details. +

    +
    + +

    Unsafe Options

    diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index 195c95a9680dd294248bcbe2650bcf35639700a4..d5c5c466bb946d85e1d80224644b2457931f87e3 100644 GIT binary patch delta 24228 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 z zctARRs($Ruxs%VGK6`L#X8PRovomMM;GNG-&*CDCb>!u0X7chFvGVfvRiq*!aAja( zLe@Wj`t0O&4qS(G$?2hvM- z9y9A4dL{e zc(i`-3r{{(j|kqWlz6Uwdy*d<>5geJos!KnGv)n4=cjlEW_9q~6i za8Yk*@;26Hbg9=|-rO5^cLt-0S~5P_X_>@V;QRQO09#@4YA0;u7AWHVUYf3upEcSl0YG9;m>{;LS7A!nyG{JHwnCc;>g^p9JoL0yO1k?g zg}je~%GSV&V6MWd($@bBvEW+5G!gFJF&%o_gxA@=Cb8l2Sob{ zeMM`grMFE|a_K9mxEq8L(duon)+&?HDk=+$NazZdC@kLIMYz3o6qHC=FW``go>wDo z4{S=t!T%t>52BDhLmcr`h_Q8%w$pcsA^1t8i34L%&A}7gs;xP0BljCc6 zt`JVRw@qB3NV+nVu-!6|5f9da9wb4!%BwtA(#sjHH1qKyF9^5}?Vy-&FZgNv;KGA3 zmEmm#0oTF{Qxq}o?gd8xxqHENWRLQyf+M^d>A^yKyHUbJ5by{Essgky?garFjhBNW z)LvN>_%5N#PlS;@bSeoZ#MT$~X%Cppc~wM&l}C(n=eki}w_2v~x;dGZ+ap^I{B{)bGLCWl{R|#f207A#Jj`~BE#AM>e zoutAZ>y{XTAC>lw-GTW<0{nF7b^u8uKb{miu*(ph_5`cQn+zqx2iQDXS~U4b^CB%7 zx|D~=F>Fr{2DKT_K}>l)7qbI(OR!|fewQs;m8TQO33dG4!C)BoD8CpC9{JJ~QAf2n zqV<%PXtN|bswR~dDmtR3ju53Es~`HmD9L@(*>lsg=aM@fRK-wtHyTHeO?Cel!*EPE delta 23793 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 U6-B9OTxNzQhFq$uuKsRZ05F>?WdHyG delta 171 zcmexw{NH%ObWQ^!6C*?TG+CS9099mj z2EQd&oU^mBvx~Elv5T>zi=ml;qotvnp^>4BsiU#6nWM3Rv7LeqK_#(Vc6MCFC5c5P U6-B9OTxNzQhFq$uuKsRZ01bgEsQ>@~ diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index 68ed5f951da6e73110b695b948cff29b1cd2e955..a2cd4ba30707ec7aa425359b1a4a770842598c1b 100644 GIT binary patch delta 174 zcmX@~m+8=7rU`AFh6bjFW=2M)CL8xQa^> Xi%KerQq#Ch%*;(Kxl~nM{oS|#f&g7lUh$=LhlivVU zWV0K;?D}|1OJg@910ypdS0fWMOG9HbOG_h16LS+Ya|<_115-0Q1sj4&V!7<>xQa^> Wi%KerQq#DAdQC04R8?L5-M9d?xGP}* diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf index 0481d0d615a2876a3dab212d0e817133c6dd70c8..81ea966b4b2d4d354119d4561265eb325efcb37f 100644 GIT binary patch delta 174 zcmaFV!1$!Wv0Qd`T*W1c XMI{wQscBq>rp9LGT&k+B{%%|V>{2YM delta 174 zcmaFV!1$MJiG?FMkXOfhE@jVR))r#V|gbtq6$s^%V&Tp zve}CNq(Qu?xwE60k)x%FqpPEVv5BjRv5Td%fuW_TrIUrZiv0Qd`T*W1c XMI{wQscBq>rp9LGT&k+B{%%|V$JZ=0 diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index e100f38ac28793ae015d1607eb22a350bd6be585..ea4ee3cb9dbadf8424c6e28739d0794b6feec3f2 100644 GIT binary patch delta 148 zcmX?^ax!H?8>gXxsiB#Xk*V><{fMhiIJg!xuLHp$Rpdh6bjFW=2M)MjKlX^Y9w#8kmL{npqhcS(zGb4&|N9h$=MsE1vHp$Rpd21X`Eh6d(_CL3E1^Y9wz8kvL`8Cn^bTN#>c4&|N9h$=MsE1vS5* y?B;bgHZd@AF|#yqcC|2ev@mzGa56M;HgR>da51zrFmf`mQ?MbVWP0OV7FhthS2Ae; diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index a3017b273a9acccf27fa2367cb0c966f9f7c34c8..9a8d6f16a3325ff59337ac622ebbdd6837023e7a 100644 GIT binary patch delta 179 zcmexAh2#GejtTvoh6bjFW=2M)#v5l`bH82e^G_x`?vNAQ^T+KU~5mjiiG`|6= zNOJ`L_6UB)a%?*uR44e%ejm%svT`dh99bMcUT`dhAjg4IG6l@48iRH4h d<0>vmEGnreN=@T3G_o`{G2~KJb@g}S0st}*E}Q@W delta 179 zcmexAh2#GejtTvo21X`Eh6d(_<{M{RXH8Ke?GPE)vmEGnreN=@T3G_o`{G2~KJb@g}S0st?OE{FgC diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index a07f166b0bf9bb73cccaf3140574452b8067db0d..298e1f3f91994bf9a4c67701a2e26a5f24588d06 100644 GIT binary patch delta 172 zcmewt{x5t&7pI|tsiB#Xk%{HT$rpKe4RsAnLk!KVjEt;IEH@YOPG&?Enk>j~fGV=t zk3Uc*-onhu$j~fGV=t zk3Uc*-o?$;z{Swgz|6qh)y&Dr%-q7<)zHw)$<5Nh&DG7o&`!aIppsZFJ3Fr8lEk8t VilWpsE<*!j19L7_RabvEE&w=KD)ayV diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 1d2b14a7f39e82e4cf32ee23120416ba448e2557..bfb8b3ec74101e83a149696bd6ef316200229390 100644 GIT binary patch delta 150 zcmZ2|opJSb#tD-+4Gl~U&5VppjW*7{$-`@?YhW5;Xl7+(WMyi!xsi7=BdXA3MScTR zk delta 150 zcmZ2|opJSb#tD-+4U9~T3=PZ;%{I=y$-`@)Yh)5)WN2kzZe?h;xsi7=BdXA3MScTR zka diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index 6d388dbd9ebbc1ee39a71704739cabb0e460c5ed..46c087e5d8e8f3da2cca0d28e9e4a5607c0e404c 100644 GIT binary patch delta 146 zcmZ4Sj&a31#t92J3=K>T&5VppH?DZW!(*sxU>ag*W@Th#Wx9C|?-WK9k;yjvhA0A? zEBOP`ot-V6Of6g-%`KdqoJ>p&osA6KTuj_7jZ9n(T-}V!P3;tH2q~G&Q!WDltdS+! delta 146 zcmZ4Sj&a31#t92J42(>S3=PZ;H?DZW!(*UpWD;UzXk}n-Ww?0`?-WK9k;yjvhA0A? zEBOP`oeiB`%}flOoXm~QEsZSQ94!o8olPxGEi5feoLwBvjqMa{2q~G&Q!WDlmyjih diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index b7631322de3..2e284e738f0 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -200,6 +200,80 @@ These release notes include new developer and user facing incompatibilities, fea + + + + ZOOKEEPER-1268 + + + problems with read only mode, intermittent test failures and ERRORs in the log. + + + + + + ZOOKEEPER-1271 + + + testEarlyLeaderAbandonment failing on solaris - clients not retrying connection. + + + + + + ZOOKEEPER-1192 + + +Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished. + + + + + + + ZOOKEEPER-1246 + + + Dead code in PrepRequestProcessor catch Exception block. + + + + + + ZOOKEEPER-1264 + + + FollowerResyncConcurrencyTest failing intermittently. + + + + + + ZOOKEEPER-1270 + + + testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. + + + + + + ZOOKEEPER-1291 + + + AcceptedEpoch not updated at leader before it proposes the epoch to followers. + + + + + + ZOOKEEPER-1282 + + +Learner.java not following Zab 1.0 protocol - setCurrentEpoch should be done upon receipt of NEWLEADER + (before acking it) and not upon receipt of UPTODATE. + + From d28208909bb3564c86279729e0849b1493974ac4 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Mon, 14 Nov 2011 19:18:02 +0000 Subject: [PATCH 023/444] ZOOKEEPER-1208. Ephemeral node not removed after the client session is long gone. (phunt via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1201830 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/PrepRequestProcessor.java | 3 + .../zookeeper/server/SessionTracker.java | 7 ++ .../zookeeper/server/SessionTrackerImpl.java | 16 ++- .../server/quorum/LearnerSessionTracker.java | 4 + .../server/PrepRequestProcessorTest.java | 4 + .../test/SessionInvalidationTest.java | 105 ++++++++++++++++++ 7 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 24f4db18cc1..209425eacc5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -373,6 +373,8 @@ BUGFIXES: ZOOKEEPER-1291. AcceptedEpoch not updated at leader before it proposes the epoch to followers. (Alex Shraer via camille) + ZOOKEEPER-1208. Ephemeral node not removed after the client session is long gone. (phunt via camille) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 145c3ebef86..13cb8f95644 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -459,7 +459,10 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, addChangeRecord(new ChangeRecord(request.hdr.getZxid(), path2Delete, null, 0, null)); } + + zks.sessionTracker.setSessionClosing(request.sessionId); } + LOG.info("Processed session termination for sessionid: 0x" + Long.toHexString(request.sessionId)); break; diff --git a/src/java/main/org/apache/zookeeper/server/SessionTracker.java b/src/java/main/org/apache/zookeeper/server/SessionTracker.java index 8f9f68c6734..3535e1b5c9c 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTracker.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTracker.java @@ -34,6 +34,7 @@ public interface SessionTracker { public static interface Session { long getSessionId(); int getTimeout(); + boolean isClosing(); } public static interface SessionExpirer { void expire(Session session); @@ -52,6 +53,12 @@ public static interface SessionExpirer { */ boolean touchSession(long sessionId, int sessionTimeout); + /** + * Mark that the session is in the process of closing. + * @param sessionId + */ + void setSessionClosing(long sessionId); + /** * */ diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java index e5042ea219e..cbae57af499 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java @@ -57,16 +57,19 @@ public static class SessionImpl implements Session { this.sessionId = sessionId; this.timeout = timeout; this.tickTime = expireTime; + isClosing = false; } final long sessionId; final int timeout; long tickTime; + boolean isClosing; Object owner; public long getSessionId() { return sessionId; } public int getTimeout() { return timeout; } + public boolean isClosing() { return isClosing; } } public static long initializeNextSession(long id) { @@ -189,6 +192,17 @@ synchronized public boolean touchSession(long sessionId, int timeout) { return true; } + synchronized public void setSessionClosing(long sessionId) { + if (LOG.isTraceEnabled()) { + LOG.info("Session closing: 0x" + Long.toHexString(sessionId)); + } + SessionImpl s = sessionsById.get(sessionId); + if (s == null) { + return; + } + s.isClosing = true; + } + synchronized public void removeSession(long sessionId) { SessionImpl s = sessionsById.remove(sessionId); sessionsWithTimeout.remove(sessionId); @@ -240,7 +254,7 @@ synchronized public void addSession(long id, int sessionTimeout) { synchronized public void checkSession(long sessionId, Object owner) throws KeeperException.SessionExpiredException, KeeperException.SessionMovedException { SessionImpl session = sessionsById.get(sessionId); - if (session == null) { + if (session == null || session.isClosing()) { throw new KeeperException.SessionExpiredException(); } if (session.owner == null) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java index d61c9c6dc34..318241970a5 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java @@ -89,4 +89,8 @@ public void dumpSessions(PrintWriter pwriter) { // dup what we had before pwriter.println(toString()); } + + public void setSessionClosing(long sessionId) { + // Nothing to do here. + } } diff --git a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java index a38e36ce73e..bdbe45ad13a 100644 --- a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java +++ b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java @@ -118,5 +118,9 @@ public boolean touchSession(long sessionId, int sessionTimeout) { // TODO Auto-generated method stub return false; } + @Override + public void setSessionClosing(long sessionId) { + // TODO Auto-generated method stub + } } } diff --git a/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java b/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java new file mode 100644 index 00000000000..104ec77fdda --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java @@ -0,0 +1,105 @@ +/** + * 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.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import junit.framework.Assert; + +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.proto.ConnectRequest; +import org.apache.zookeeper.proto.CreateRequest; +import org.apache.zookeeper.proto.RequestHeader; +import org.junit.Test; + +public class SessionInvalidationTest extends ClientBase { + /** + * Test solution for ZOOKEEPER-1208. Verify that operations are not + * accepted after a close session. + * + * We're using our own marshalling here in order to force an operation + * after the session is closed (ZooKeeper.class will not allow this). Also + * by filling the pipe with operations it increases the likelyhood that + * the server will process the create before FinalRequestProcessor + * removes the session from the tracker. + */ + @Test + public void testCreateAfterCloseShouldFail() throws Exception { + for (int i = 0; i < 10; i++) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); + + // open a connection + boa.writeInt(44, "len"); + ConnectRequest conReq = new ConnectRequest(0, 0, 30000, 0, new byte[16]); + conReq.serialize(boa, "connect"); + + // close connection + boa.writeInt(8, "len"); + RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.closeSession); + h.serialize(boa, "header"); + + // create ephemeral znode + boa.writeInt(52, "len"); // We'll fill this in later + RequestHeader header = new RequestHeader(2, OpCode.create); + header.serialize(boa, "header"); + CreateRequest createReq = new CreateRequest("/foo" + i, new byte[0], + Ids.OPEN_ACL_UNSAFE, 1); + createReq.serialize(boa, "request"); + baos.close(); + + System.out.println("Length:" + baos.toByteArray().length); + + String hp[] = hostPort.split(":"); + Socket sock = new Socket(hp[0], Integer.parseInt(hp[1])); + InputStream resultStream = null; + try { + OutputStream outstream = sock.getOutputStream(); + byte[] data = baos.toByteArray(); + outstream.write(data); + outstream.flush(); + + resultStream = sock.getInputStream(); + byte[] b = new byte[10000]; + int len; + while ((len = resultStream.read(b)) >= 0) { + // got results + System.out.println("gotlen:" + len); + } + } finally { + if (resultStream != null) { + resultStream.close(); + } + sock.close(); + } + } + + ZooKeeper zk = createClient(); + Assert.assertEquals(1, zk.getChildren("/", false).size()); + + zk.close(); + } +} From bea47d0fe7ca358ff145fd2c0c68fbf1748960e0 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Tue, 15 Nov 2011 18:28:38 +0000 Subject: [PATCH 024/444] ZOOKEEPER-1239. add logging/stats to identify fsync stalls. (phunt via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1202358 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../content/xdocs/zookeeperAdmin.xml | 15 +++++++++++++++ .../server/persistence/FileTxnLog.java | 17 +++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 209425eacc5..7f0df39413b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -375,6 +375,8 @@ BUGFIXES: ZOOKEEPER-1208. Ephemeral node not removed after the client session is long gone. (phunt via camille) + ZOOKEEPER-1239. add logging/stats to identify fsync stalls. (phunt via camille) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index a8fef594a47..85d2b8bda6a 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -783,6 +783,21 @@ server.3=zoo3:2888:3888 + + fsync.warningthresholdms + + (Java system property: fsync.warningthresholdms) + + New in 3.3.4: A + warning message will be output to the log whenever an + fsync in the Transactional Log (WAL) takes longer than + this value. The values is specified in milliseconds and + defaults to 1000. This value can only be set as a + system property. + + + autopurge.snapRetainCount diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index 471fe1045d5..3a0f68084e1 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.zip.Adler32; import java.util.zip.Checksum; @@ -97,6 +98,9 @@ public class FileTxnLog implements TxnLog { public final static int VERSION = 2; + /** Maximum time we allow for elapsed fsync before WARNing */ + private final static long fsyncWarningThresholdMS; + static { LOG = LoggerFactory.getLogger(FileTxnLog.class); @@ -108,6 +112,7 @@ public class FileTxnLog implements TxnLog { LOG.warn(size + " is not a valid value for preAllocSize"); } } + fsyncWarningThresholdMS = Long.getLong("fsync.warningthresholdms", 1000); } long lastZxidSeen; @@ -307,7 +312,19 @@ public synchronized void commit() throws IOException { for (FileOutputStream log : streamsToFlush) { log.flush(); if (forceSync) { + long startSyncNS = System.nanoTime(); + log.getChannel().force(false); + + long syncElapsedMS = + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startSyncNS); + if (syncElapsedMS > fsyncWarningThresholdMS) { + LOG.warn("fsync-ing the write ahead log in " + + Thread.currentThread().getName() + + " took " + syncElapsedMS + + "ms which will adversely effect operation latency. " + + "See the ZooKeeper troubleshooting guide"); + } } } while (streamsToFlush.size() > 1) { From 92b4bd14c5279670bd9f54306434d56295243d07 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 16 Nov 2011 01:31:51 +0000 Subject: [PATCH 025/444] Preparing for release 3.4.0 take 3 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1202501 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/releasenotes.xml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index 2e284e738f0..886318265df 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -64,6 +64,24 @@ These release notes include new developer and user facing incompatibilities, fea + + + + + + ZOOKEEPER-1239 + + + add logging/stats to identify fsync stalls. + + + + + + ZOOKEEPER-1208 + + +Ephemeral node not removed after the client session is long gone. @@ -72,7 +90,9 @@ These release notes include new developer and user facing incompatibilities, fea ZOOKEEPER-784 - server-side functionality for read-only mode + server-side functionality for read-only mode. This is not thoroughly tested. + Avoid using it in production. This is also at risk of being removed from + the feature set later. From 78236032b9720bbd67aca7dd72d6a2d52f017ac5 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 16 Nov 2011 02:08:03 +0000 Subject: [PATCH 026/444] Changes to docs git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1202507 13f79535-47bb-0310-9956-ffa450edef68 --- docs/bookkeeperConfig.pdf | Bin 13807 -> 13786 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147553 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24944 bytes docs/bookkeeperStarted.pdf | Bin 17122 -> 17101 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13179 bytes docs/index.pdf | Bin 13496 -> 13475 bytes docs/javaExample.pdf | Bin 33802 -> 33781 bytes docs/linkmap.pdf | Bin 12445 -> 12424 bytes docs/recipes.pdf | Bin 31043 -> 31022 bytes docs/releasenotes.html | 28 +++++++++++++++++++++++++- docs/releasenotes.pdf | Bin 83098 -> 84059 bytes docs/zookeeperAdmin.html | 15 ++++++++++++++ docs/zookeeperAdmin.pdf | Bin 72515 -> 72877 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6634 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48813 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16461 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12852 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302473 bytes docs/zookeeperProgrammers.pdf | Bin 133759 -> 133738 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11241 bytes docs/zookeeperStarted.pdf | Bin 27563 -> 27542 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30483 bytes 22 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index ccc33f5a88bf4fcace595d5fb831e734f4560d4f..5f373cdcf45ff860bb943353a361b0c89f8b73bd 100644 GIT binary patch delta 965 zcmah{Jxc>Y5JkblBbv(2;!q+e7W1*Qw;RGijCYNoO)5de!ZZPkn8Gf#mBRTC1S=cK zAF%K**jU^97u>`m$xgtnmbY)_y?rx3{jdJx(>hupu*ROhJ%83}XuCj7u+0FB?>8P+ z7LaPcez*XPd-a#KRg;Z3w_djoBuim((wj7yUJxC5g?TI0H|~vW`~>6#BYMD`iLuO? zxE2W|<{_}I6%?5EJDK?e1EhoniDO|CJ``iZEY&+@qrk_%)^nYOeVhtp4q~-X>zwK= zta-*-#j{pYI9-gMh_%(s$GL%cRt6m~S0B7P3JDq&fobDsHr1~QIjJ( z8Az3zoe@|Ur;6Y}m4FYMH(k5A{R4>8LPVWD5-6+4I0+_nosARGELrF!{?Qs=b)|Jl zzH}uz7E|~m;A3C5I~H9K?3i}JMV_|)rCcO=p4<+v2E)6-buU4(877TJaaJb3bjQXS delta 970 zcmah{y-I^Y5X6AZhY~~^!Qumr0a4i9yS<+fBGG&)6+uf;5p4p3F@=?R1ckG;wX%^s zfQ1iWW#da&+j)ryW-s7X$K2e^?(BYaK09~!WneT!3>#pp0M+M*_nA51eKa+q+6b-z z3~pv`r&2g9me*5suvdPZE*TnpEj%r)S(n4`H(c#o7EeHqZHjp* z0oGZGAz`A2pds;Kv^esJ62Emuo+fF`sI|m2feJ5V`@qU4-I7v2(tng$qvXZ%NxC6v z@Y0Q$C-7X1XlE>DVB?JVjB(IPV8D)KZor;hcvZ^>;9x?6$|NB~k)%|obmHc;_2E>n zl!(Knn(vz=V+4aTF$4=K=12|_!BfIRAh|$@F^XXj$7730Bzr(qN{$LqPgr0!Nik?F zgiWp=p(+qswq0y($InHyv81_0+%#4<+p3Y*|D_yejYf9WJL&eWyJzPalJS&PD#dO) F`v!(l$5j9T diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index 1c895929eccc814ebc350db5ed9f52bdf8ff4270..3b5ccc84f3432a0633b0c8c704e23f90795c6a3f 100644 GIT binary patch delta 1175 zcmaizy=qiJ6vxR}?ImHmxaA^QizUT5b3SG!yKWSBZzb4>g&=65r9w<$w*kS}DG1JU zNQyw7KzxAhg}i`GY%^<`y>kTbEiU}d$NbO#p1C^d{W|G=Iqji>2W5Qr{o9Z2ZPX{= z4?qtJID5PEd8tdEjwWADZM6)EU z`!V>@^7)^GW}~!e2kSwK3S*WK}Vu z^^(h-t;LkvU>Y)e;}HuP&so{n!9g}yzC3jPg*=e3z!c!&TzR33mc5oY%zeu(&F8b> f^uH<3o7t>+cl`3``2EqV*A22FhGsNcJZ_t7GH>!D delta 1184 zcmah|J!@1!6eZK`Lk$u(Zn;R-K!|Yf+>e>reQwBeH*E;m3JNONSi}@|8?Z<^BNp>3 zQiK%NTL`AekMK{}>TDt`ZzkYt2G0FD_uTV-9d~{ocg{{$K~Sv~ehO|AVDsYa=e>s@ zFHncp4sYNgz~!6!Usg)^+n)#9C3@*rPdkRg=XvjB-dp)T@0qQ~Ti*9!@%2CStHq1V z!DEgIqKn;;d9tpy9RskQIUbD6+JS3_kO{5km=H-D15lJK4}bwRUX6^8-P&|_926N! zKAQ;O)rF$FD8=`++2{!h(m>t>rF5jlz1wHZ88$|a$+M~IzgX0UEuMh(e% z8z(6?6V{?Kux1J*r9iS>kVPol1u00#F&4$VmV%=NHfvp0G~|ORc$X7oagN$DA*V)p zEsK|;781v0t-ZCB1S%WaJBBb~JRh5lnF1EA6-u+F8x?uPw!}nuN$Zvs=^dE6k)y2} z`A`%mduCQje-4&p*xLL!%tU3sgf0pG(=Az`5S`VPgPn)?W5?95CXFVCgxw}g1*eGq sxepqey#(w|8rO{b^?y@dR^xH?@#yv8(Wk?=?ncK`qY diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index e702d5fdeabebb56ccc8206abde08ac709b81435..9bbc9ed0291f735eeba11ed042d220829e8a12a4 100644 GIT binary patch delta 1361 zcmah}y>1gh5SAkfI0dCNrKQA{kW$Rg?(9rtS+WwF4jLN3AyAN#!la<$DvDGfUEY8v zKz;*|@&w@}sCWc6iI9_-NL+RLZg#$(nZ3_Hdw+iPzMefm0Rl<+<@ayDHaAh81MPwA zRbX}c@JsI&(mmeVy#=g}w=V8)q_P@6YW8>03qHXrcFtaqgZjcKV9IzMUy`r;AZP znnx@}y7R@bZ^=6#s>-^J!|fI;ji)9C3SMfIJ}+Vf-x-NGW|One0c%<1ZQZ16Sr&TTS;~myTqAJlCxmXVU z3fNlxwRkKwk1F17u09r)px%)EVFHxW&5wpHbBLKyeSE|eL=kWfXCbACW%c8&w_mSz zqi(|b-a@gkMD;3%h6of4A+k44SZ}8rFV!q=_Iq);iG(Qy$C$%kflwq8m*TgaP?fvi zZ^lRaGDp$5O;cu+=|P&pY$~&4S(6^j@?aZnKU0x-Y!o*F5ewD#0=I9hqGt% F<}Z2S6VLzv delta 1398 zcmah}&1w{35M*AzFR7^=V+??%r_EqN~TlpEtGwR^z)>vj<{~X#I>{kE;F!;A=3r)Gd#9+vrs} zA^B*rObRPHBq5A*(tHM1N@xuXvIphpt|v?#<%%PRu@-Q;_cKVs|Gwr#~fj zzWcSP%Q-`UNTj>7yx#(r3Uf|+5Qid8O@NRyox#wyKp5ImE*mdawQ4OsYF`+V&0qwx zk1_(9G!VrO4&jR6@xMGI7Nw36v-T87nN3|5O^}5mb?=`&XoGX6ampo%eGV@J$wiP+ z9-<``lC?bKWco$sHTilo^&CsW_24K8-P4o0D}e2rpu!6;@QHyf@O3@-c9 z&Tz~|2(5Ub48IVNXr}W z1jvsOQPQJ4N!CljdTyjlwK{v|p6|}w`E%ELzH8k-ETaK|iJU$?zO>tDCqO%3JC!i| zx$>*Epp@IIpBIGL#p-MELBj0tOyuQ6WzbLSJ0akRo`$v=FFLi|AqW}N^ delta 1134 zcmaizzfK!L5XNP?SR@u8gp$e<7$iib`MVcvWDSw4@1aLI_{k*va`VDPJG+fF< zfcfLcADt-IpL-`!o!|CeyBkJx{xaJ?w$g{i*MI1DmkX36IcPac{psv*n$&FpWld2l z;4~y*@|uGAIxq!~1On3bUavU8#5`wHM&Upa@WvnpSZ|MVeLvdW6G8_DmNeDhM+Z|y z6~CDcy$KQ%2N`ddTcwdS^0#Qy~-YEskq^djYITnSVO^TidF h3X2g-nP9$zNj1^4{^@%q^=!QJfl_xcnA}`-{{iM2<8}Z5 diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index 6ec735eb6950d0303c2a472bde9aebbb5ccd095c..655945f7417c5904637cd9286466f6fa10760185 100644 GIT binary patch delta 818 zcmaixy-EW?6orWj;t~y5C}Oi9iy(sc-1!d@7~-b2u@FJT!q{2FAcDm{ff(F2K7<4x z!B%`95!?ikWG3KL!?)+$IeYHl`l~y5T!AJgEphfb{HWKVPl%hOn-v&6tUlFdNV(nE zp8=!O#@+IQq|xWb;?^Fkl*T8$Nt4M1kR&6fB$eL>y9Ebdh^-9O6*DH(Il&@efMFLf zQW}w068*BZznG+qRpkrWCBxE{@04xI$O%{$Weqz8IksVTz!AdtjWTm|Cdz3HDDuwf z!l`lg_1TJvEXp@zMHM|50F|(Tb~rER3B<3}W|AAO;pZfH#no z9zpO7mbrtCc~6kA6L72Lw=?sf`Okj$M>nIVDli%%?T*3L0qSorpDSy?=V%&4jS@Ei z#&@gF3lfLr>XD@5ZuMcQ!aRQ8THHC*CWYdpw`o#d5H+!2Y%okt>bZ+vfE$t6Hlo$sIY-|J)vRDm}kJ~~}u4MV_UO`*2Yv4J_ zW)8F7Msp%~5cLpnw&~r%E<{wqOeE#P7d0+oq;wPE_ry5!0Cl+O@!*K}HG d^WTt*q}599hgXB)kEc5hy;^O!7fzEFP41Lq@fnGaASH^Z7^`*PJJx|9d+~cHWr;N8-C-&0oGbl~1n2sB?_bMN Z*6(K*)6rylH90-YkUT+F6y^Rf`vRd5zIOls delta 947 zcmaixy-LGy6vfd=g3`J;xR|9BMB#q@KW-_7YLit&7Y7kRg)R=YgIi}Og*<`}P^BRH z6fSN)g>PVu4l!3SOTsVba?d^Ie)v4RduV_|Pb6_0LK~p{`0}y53hoenhp1EH4#4bb z<#xg1V!yFv>FlO4Xf7(uUN_#Go8nXWdeX}@DL;@9(J%ro-k>#^zia=1VswTn#ySi7 z$1V<9*9oYdjZ*3+6{8>wWQHZw&4YL`1|_07L^RaHn5XZcvg4?X$T5Zr{AQw6;WzWn zT<0PiGaitOGYZ9&)+Q9qOhm1lop+>G$IkeQj;$`3h9Um6_$y%ro2HUC39GPKtR75_ z8lq`T)m2$B<@E#6dPa!el#OWbK#XLwQjW!vbWeB(G$)LG80>QHy$hJ-=pFR4e#n@6 eY4=~rankE0SL4&s_~j!~`j%!m!9n`AckZ&Y1i&F5cb{k$@-E#R>uE*i zv^{RR3>G#5`1E<)?-GeYg#;n}824)`$f6^{w9+;;s|Ah{LDPe_xvpB==Hw5l8B7ci z*<8)&)TgiQN=v|Mi7|Sy6Bx)@2Qjc(QjAiv37)Jai~%#Vc5Y2Dh_@c+#%KXz2wH-P zWL?V(&6Y?OR%Yj=@2@V_%--lj97t0=X}TIM!)&^ytx3NYUyKC@kPaqI?Zx6F)kx`Z za%nZ@bu{-eYOp>tMlb2i>+Dg?VwnkwTxd$sfjxzy&pA8_;lDIeEVqPWz?m$^simlx z+0T1KF&3N50ENyu3Pp+-vUmmz1DDwZcOvU&|NcX~y0rb~+Wi}zc%-di};aOOVFz2~0!@wT`Bu6MBC2akcs^Hqr30NbDQ*VAXg z-^DN?nrz?+!0PFl_a`izPxh}^bhXpJbE@ZQb+GmE@#Tm-g7q_cHmWu+49SVXB>8;r z<+ux8eF91@S}cLW2wc<{BLS*72p~4jQQE|n5{Ep&0g+7(dLD4-Q=TnS{;)VVK<`Wr zbN;dz41anKHyP%Uf?*XMXb~AhD6G0;$AQs1O`ox38-gKff{Y0@L5vA3 z8F5D<=AW<54>9RPg3=&gTGoSTu9b*m-d&D|-YkSzIOR{vdgw^cK|V;#-$M(I+_JIC-&-tL)cYeTH=^PSCKh9}QYstJJxKhT@^L*#%9)rZku8?k5)|WH z1~3L;z<*42>+ml@R$yRjU2tL!p?KWq`)_t0HaFXbu|b%Xw%CN&Vgu5Wa7)m@bacJ_ dU&w=MI<21XJ(};mnD0KRn1Etc*X`|D^&19H7x4f9 diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index fd5600aaf757fd0c42808de907ec04601361e89c..5d629e82fdbe27bb38d0d25310adf6b09d13be3c 100644 GIT binary patch delta 920 zcmah|u}T9$5XFEAdIHqSr&>lN7%?ptr!C@&CuEWl-$cF2`8RNo zV1@t+Y!Xz$$_T0p=Ef>&ny7EzWa&Xe)YOazZ!yvzGe;ZOo^6xJO!e1^-$o$RdU%>w z7>woE^hnwwG-J0ISxhJ$5;iZv>iP9n?)}|t2+DN_CWR}c48GYPB%MkuriIR^1S(4( Xq69k2CjU|DTiEYv?B)6S#jN)YNAtO> delta 969 zcmah{u}T9$5JeG$Lk$+OP7?zX&CJ})>~08w7!qv6##SVSmKM=MY|;uAidTvsA;H4$ z@H_kj!Obbg>=a&gynXX#-n_XTJ&zt9D)|(Jw1Zp;|#L<5l(r;Rz492mFH%-(nI+@96K$q${2o@v%Gt9=kv15-!E{rK+e{Q1nx{=V*9Tz7utTY&+AQ+###>uPT=@C(2r z;G-hUzu!LVY$^Gm`*=&3zwh2{ZX}+6eemSvA#e{CU++_|S^l7Ch)%O)`Oo;#jI}xf zR06N5bCDoQWrrF#RT^+C$!wKWqLr(VG@2=JQE=NEoV{0Yj?2p+?Mq zCkK63CKwAj4-uV~tVDc0-M>#IKz%_JG5?&l_sm>q2$b?wIg5zZJ=6q{?|j~$MRSzP zq15CNl6ay-uA#u8t^h&Ois%|BD#t}PW?Rz$>RYK2T=oHiPewd}5U3`G0d*sphpRD6 z=+Gh|hy46YJ4?86VP&tngPJ8*IVE2?QFQ)jN0C$zE!tKPt@lOETo49uRWhj448$!8 zqbJE|n{*c~0<;|{%cJ00W9Vryp%2XHW`|`t6D8cbVAk0VLyOL#Kyt}Q*?@ZT1=-=3 z=;uz7u9jfq89*V^e13ZTcEmig(aG>c2c#nWbU+*shJ&)!fXVv3h9{Q7y^Tvxh|*|46ufBu#eF7)R!PpgWPQ%xKs_OTB{r$T0^Sbjbbs-=Vd3^$L2r&F{@$2So z2(OSwM5DDl0$85k`r5H_I_N&O>hh?&U)*3^p5A-*`Vqw*tiIldUa|h6D9#&ABc!X@ z!zSiSKoU6H-fE(mMjnFXnxKSC19pVD=E1{SOJC+LUCsCJINJz^kcxWvg7f3l4&?GH zHE^yWp_v?%mD9(%f7h!iiR3t@%X)AirU16Sx%9hT!X1vEt%$PB^=MpMx3 z`&JeKFj?dno%!={2c>3}zT`u}>os>#Yh3WIMJggilS5+BYsVIk4H7zH- d@qf3xDJGL*ULViu#q8v?Aax|gU@$$Zia!y1Dw_ZR diff --git a/docs/releasenotes.html b/docs/releasenotes.html index f4b0ddea5d7..b065e1cea3a 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -264,12 +264,38 @@

    Changes Since ZooKeeper 3.3.0

    + + + ZOOKEEPER-1239 + + + add logging/stats to identify fsync stalls. + + + + + + + + + ZOOKEEPER-1208 + + +Ephemeral node not removed after the client session is long gone. + + + + + + ZOOKEEPER-784 - server-side functionality for read-only mode + server-side functionality for read-only mode. This is not thoroughly tested. + Avoid using it in production. This is also at risk of being removed from + the feature set later. diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 1bfb351b9a3e6e9cdfcd961f05d45f097aa1a11d..bc7f79d8a314884726d89c93a2b2f3cd5bf52fd0 100644 GIT binary patch delta 34119 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 delta 33547 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 F{{h;YwJrbv diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html index 53f0a31a97a..4bd6343e864 100644 --- a/docs/zookeeperAdmin.html +++ b/docs/zookeeperAdmin.html @@ -1158,6 +1158,21 @@

    Advanced Configuration

    the tickTime.

    + +
    +fsync.warningthresholdms +
    +
    +

    (Java system property: fsync.warningthresholdms)

    +

    +New in 3.3.4: A + warning message will be output to the log whenever an + fsync in the Transactional Log (WAL) takes longer than + this value. The values is specified in milliseconds and + defaults to 1000. This value can only be set as a + system property.

    +
    +
    autopurge.snapRetainCount diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index d5c5c466bb946d85e1d80224644b2457931f87e3..a39da1c5d3b494a6f0797d38b652d5082d968c3a 100644 GIT binary patch delta 18501 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 QTxNzQhFq$uuKsRZ0G^Ovw*UYD diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index a2cd4ba30707ec7aa425359b1a4a770842598c1b..79263094ebe87e49ee503d1c92e165c75996944a 100644 GIT binary patch delta 1723 zcmah}J!@1^5XJ4XM7OX=%7t}}sKv~kxpVJ?u;}VbqhO%D;fR4}v^4jTFJ|@aG+AI0wPaWVrtoOfN;#g0W+y17!^El46}yYe-A~ozfi=A&QkMh$WRTr0~RE! zIkCFeMXM2#m5VfJC}n?euRb8Q6l$03EY_GUEk-YTm6B6A*dMkTtu8EeTA0>YPHS}{ zRUH!UY$fb!1V;wX`%9RnURK*@LyAK78yA>K5gb8^KHDVO3dEG={GGAfEGm<07!I{oXmt4%bP7#+Pj^+E8^H;>1%gMb^W(6o~UpF4+MsIE^NrISF v$Jd*&=ndp?N~}%Oz^K3R-z^Wiqfz(8_M@%sms^jYbXgMR?r_-L+3fxW%zt89 delta 1762 zcmah}J!{iJ7{_c4+MptJ4AxkX3eWr9g(8+VU0s}=oCQC@e{{5ZyC0z7AO#yyI&|_g zIQk*ns$SFB#ODc^Dg5sK_x#@OKAiW?&U=Ti2cT#~Dvuy-0c?Fd{C52&=rgo2(Rc=r z0ghkXcylen_4oRAM32+p)ml%=@zLG&_YWC|aPb?y9QJ2_L{T(gl(GK!^q@0xOF&s9 zVktR{3sP~IsG>hlP{C6IL+07Hg9T3zK&wY5x&A!4v!QaiVQJmiq~4p3HmGDb7;<8& z&!_jFBo2!Rl0AFUY@SuaGS{;4+@IiNV!CgQPP?_c e&>p~C%<#a<|)T4qlG diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf index 81ea966b4b2d4d354119d4561265eb325efcb37f..795e4c7c1889e46a21ff69340b690376256bcce4 100644 GIT binary patch delta 884 zcmaixu}TCn5QaImaXGodLcwN>TP(Dg$xJ2_*;|!8*V0B11Qii271YA+w6gSIK_9|O z?hUlC@+Eu{R}t|N!BpYPpUMCKdB1MGUAKle(GI-`@k?`tV^y>%tuHUY5uQdFDZ)T;_~qP^6LEfBtc6oNtSJFRmlh6I=8(5 delta 937 zcmaiyze+>8zdz}z*re~lr(epPCyuG zLZ;DUhy>PWWF&bE*aVJ%0Dg4Gzcyz=@NEJKq5OhG$~ObVY+OGz?jc=YGR8PeYh{b> zC9UUVMG(k>_Jboz_Uobo@O5AXg6C&glv4X0KuH`j#X&ten_o~*+d`KwB$H+i7n!wz8N#5YNCE>hB;}u< zd^J8FT@1%jl0^Hbqw8TzCUWlwTV2*x0oyF=LY7&^UcX~9X)`G@piFn#aq^$U;bCo= Lb<}9|PWsU|u(-l+ diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index ea4ee3cb9dbadf8424c6e28739d0794b6feec3f2..c9952d3f98177770d0688ff480856a76f24f6a37 100644 GIT binary patch delta 752 zcmaiyy-Gtt5QR}J^b$!YDKD6$QOwN#%z@N(Jc{OdF`3!1Q|aer1Wo zYIS!BOb@GH>#NpHT`g|yK)@VkE5FLK`34caEin@$e(yCWp41VTU_+U)mIYzfwTO;O zwO&}a2P@*|J*o?zKzFsjE)b%+fp+itDPdS zpXoJ{t;KhMK&5v}Sz>&_&Y73K6y%*@W+BrHUIzR9f(kHH^bV+-cmBO{nsvL`&G>vY Sz8zg$W{85EH5$dyAo~RYQ=aPp delta 792 zcmaixzeeizI#qWEZ;dT+?Az+buiqIIf%r71nVuCgQZehfRFN@vraS?^{lsJ&BLH6)9;Q zF?g*Dzn!H#;f+K1#Nn7cQB*c0cM5M-RiB+l)xG L&nlJfMKAjSuPLSZ diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index e92427a03f659fb80a7cb50a334d8d0ddd7c4ce9..8566f1010e90dc6c82b4ac32e2cd87f09007b932 100644 GIT binary patch delta 1928 zcmah~&r4NN5a!u&;lmQNZE(mmq_CMeKgY7rmqN6NBoeZ1(59e8%Xo;32m*uf9W-p+ zR$90a`X5}h=nrWXMDx`5^vw~vaJk&?eDmv@nR~w;jDI*7|MYGIBM?NNp6z}7F*=Iz z3D6wGIRU<}UEFL8AfNwSx;X%Bi5JI6_3AW%5MQr{tfcdUX4=1C{W=*gGEEpvOmJ_?|6lK3GW z$Lfbdhvcw?j+q=LCV=v$VTJNkSYU zVc9EYqQT{7$6TF7mx5QOT`X%;ND*CmTg>&xs(6x0S(QMLvMyeD5p?FvD4PB%X0nYT z`udov83JjcQiqtS&RB**^UMI9=h#Y+o^;Q9e~XloYK`TOK#5CZ*UY!>FSKH-<$*E#1PiU@98!p_kQVdOf?tex?VbNo%KEOEXd;K^ K)YQ_W<>o(Drf0)u*oR?s-J6G_*vb?i%XU?2CbN9{m_^0jhw=c#ZU?d830eBi< z`s=Ij$4)@Fjd7M}b`PEf_;&O7+E59<`#E~PL|?YYw%TJuXYLL=dEHqW($BDdj%;<7 zs^2pgu|`)zeXCX1m##min(*+=)y^Njg_>n+d9AuPa?XR)&^+~W-D>@*LSjigu>j?yQ1w-JgE zl#fwTgt;h;=&b1wi6I}|(8W(h=Rw#Kz^K+WwJ>5VYB6$Ai?I;MCvy9Zj~*(iV91|0 zs)-S=*GI@Z8&&&GKsAi1EFMyy9D8c0v+Yhd>sI>IJLmF~kGi&v{K@!3F|=!@dGz+Q z9HL7l7K)XVs84a0HDBYRJ|9Wc?X!Ult~ktk6%=vg3Nq}x9ghz7Fs}FakAZ`|fJiw0 z<1l+WTV3!_OovOaNK*G3(x)Y%m0xw%3FO_)YVHB0Zs_>Mu0!<{1y5a0ovTT$t0Tk( jny;_?H{^b6e!lf|_2J6u^OdDXEzM0BTa%NEH@dAq70Q-> diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index 9a8d6f16a3325ff59337ac622ebbdd6837023e7a..fddf5ac3e352d1e09174e2d3237cf0e18c5487a7 100644 GIT binary patch delta 3828 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 Ds9pda diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index 298e1f3f91994bf9a4c67701a2e26a5f24588d06..35ffbc0c4b50c61671091b67ce79236598bd2ff6 100644 GIT binary patch delta 802 zcmaiyy-Gtt5QR~+@DfcSVtYYDjIo%V-I<*YArQm0Q=33Q5fRbSA_la}16U|*=Ud2q z4I3ZBmk@7)5VBLa)$+}mGiQH(M&F~imkq;!z>Y6I-al(K!$ZIhaHj(0)8^~S5{bL@ zqa{!d>tE}uR?6m1f4gl3bC|9CAV=hE2&fnlr3WEk}R2Hw8hEqzYnDGiOp45JIPV*|1_Bb<%1`4rj9uvAA)men#3HK*fh( zSnJTcc-!t?6BeFbF>uu@;Gj?7`vde+1XGw}vPCcN{wul4dcEv%ax{cd5!e*QUwW4&lLH8OR?GwCgWbx|$2Z8Ns*%~mbwMn} zOc+!@hejnLz;A8NRm=y~t)Z|E>4=m?U{)4VOc+OTF2rLP__dub#|49hR%SP5z`z*J zGI0Edi7@3CdppBI!4N5m9xg--*jP~vQ8P=L5yc+Bj$8`E`KauzHV%51%uYQGi!U0^ z_Y+XVff%0Ft^E9HC@w##H7?n=TA2ldrX9G`%ETgcd;rXj#2$OwOdJ1}oF~m@a^Jt| T^&fiIH;DlYAt{&Jr=8>r5^t=? diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index bfb8b3ec74101e83a149696bd6ef316200229390..d903f7615bdc79ddef4c5c085ac9cc6fe0d8ac6a 100644 GIT binary patch delta 1508 zcmah}J8M)?5N5Sk$9fJ82u$qh*U!gmYiLh^Ho-O< zFnf35LuUo4x7Y8k0JFXIH|NiY&3@eY{@@Pg9?YNKt6q2ULL&dbRHgph+nAa!2g(Yr zMZ+>XjEImtCP~d0W1q__pi=Y*i^4-hbH~BaH&o>d14Svfy6RdJszj)sEI#4|hq delta 1603 zcmah}&1zFY6ee9>BSjPou7Z#jY=!eXb0$b2jVXwWZrrLAOlnbF#4H30_Yo8Z+~^y) zl5Bhi5nLz~-^QD(U@}KAtMKv7obTt{d3VwIc+q*Aw?Q!wDcy#+2e9|)!;kJ2&<7X> zM1vL{04&chzv*~5?`$7|LSargpNq~o4Cen?39B+G*SLOjhSZG4zMxj zP8q8^6%yu0v)vu@gH46<`&qv>DV-yL6NO}8g?I$CawGz$QW}CfL&(5wGkO2CTQjJZ zmurMFR+5BHER$;=nO*J33T1W3p%t6IoZh@H(RtCNdFyGvmXzGl=vya3R|_>$owXQK zxq+nU>cg1YjOvYTSK@Mu&>)oMkTSTejC`ib=FA!Mn!we308$CgiAi*ATX35~s^ZeM zC~HI78E9tZeMI^5+0|P8$3q&DvrWPtQ}S5EGDd24+^rlXSn)PHU_eJ?q%v>fMoqET z&7-1D3^(tJft`&2-eT&<*51dLSjigreGnzeRXq5jsa4R zHD+D`H4wXdq~$mal2#Q#g!or)`(K%mOfB#BdRa8(JAFG;K=W#5kl*!ty9gk@QA9<{ z-^!P!Rk=Y-^3OHL$%$eTq>QuTsNgu#1eRaSZY)trv}S83hTt8_Olwz0_U(~M49){N zK*7cvJ4KG6YEctb8>eN@)W1{&rkT6v1 z7r~G}ygqnxSGz-#X-J7fcRZbPkkmow48qZ2`LQRXga2N6+)O6TZ2q*Lzv-VnZ%8d_ K)3)9HY4aOlOyf8J delta 1116 zcmah{v5Hes5G7X2Ly3a$&_V+4BOnShbMKv*8zivVB$Z%eBPb%QHiFznKNfjIURgI9envVgoI3#=mE$WV0`l9_thJa zo*++%Cg<=3V0(P+XyD-zhW9JEC=t# z8_NKxfT;$`5DbPIq8N-cxH?Gc6hQ?3JGb2FsT}k-TT!vXfkDbqzk3UErf6n?0%Q5v zcdbUN7XdMpomJ(~?uS&jDf7xna63h)6F;u*Zb(ybT~j725tVW_%19a9REEU9PA!Iv zxz4Q#xO_krw(BDYX9;plTAr=D_QKwa5TK?+<+ySjt+?t)lJcu^L6Q|&?=0o|Vb>x! zE+mfSQRQNGJrm_vz8>yw)Zd>pqjQZ}U7lFPW9rk)*$WrYGf|s=oD_7HlTfxnAcK#eSY8o$raxX0w=ZpI|wU%NpthES9zLboB7QCC{7bv{|iR U_UjM*tJh8SkaN>@i^t364@!dTX8-^I From 37650121dfca510dc34d2560c9f5354ce1700186 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 16 Nov 2011 07:15:55 +0000 Subject: [PATCH 027/444] ZOOKEEPER-1299. Add winconfig.h file to ignore in release audit. (mahadev) - Merging r1202557 from trunk git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1202558 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ build.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 7f0df39413b..c2ca40a6130 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -377,6 +377,8 @@ BUGFIXES: ZOOKEEPER-1239. add logging/stats to identify fsync stalls. (phunt via camille) + ZOOKEEPER-1299. Add winconfig.h file to ignore in release audit. (mahadev) + IMPROVEMENTS: ZOOKEEPER-724. Improve junit test integration - log harness information (phunt via mahadev) diff --git a/build.xml b/build.xml index 5c2097430b5..64c02e65350 100644 --- a/build.xml +++ b/build.xml @@ -1502,6 +1502,7 @@ + From 29003776ea3c8ca37634b0a7a41ed92d6522b933 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 1 Dec 2011 07:10:34 +0000 Subject: [PATCH 028/444] ZOOKEEPER-1311. ZooKeeper test jar is broken (Ivan Kelly via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1208977 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 9 +++++++++ build.xml | 16 +++------------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c2ca40a6130..60fb22eb33b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,12 @@ +Release 3.4.1 - TBD + +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-1311. ZooKeeper test jar is broken (Ivan Kelly via phunt) + + Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/build.xml b/build.xml index 64c02e65350..9d8e066840c 100644 --- a/build.xml +++ b/build.xml @@ -692,20 +692,10 @@ - - - - - - - - - - + tofile="${dist.maven.dir}/${final.name}-tests.jar"/> + + From 9ddd18ea1ada2342da3a7b339356259f4cd39473 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Thu, 8 Dec 2011 22:03:35 +0000 Subject: [PATCH 029/444] ZOOKEEPER-1305. zookeeper.c:prepend_string func can dereference null ptr. (Daniel Lescohier via mahadev) - Merging r1212153 from trunk. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212161 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/src/zookeeper.c | 2 +- src/c/tests/TestClient.cc | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 60fb22eb33b..b12d2ee7a20 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,8 @@ BUGFIXES: ZOOKEEPER-1311. ZooKeeper test jar is broken (Ivan Kelly via phunt) + ZOOKEEPER-1305. zookeeper.c:prepend_string func can dereference null ptr. + (Daniel Lescohier via mahadev) Release 3.4.0 - 2011-10-25 diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 64d7a4d0ec4..3e87aba331c 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -871,7 +871,7 @@ void free_duplicate_path(const char *free_path, const char* path) { */ static char* prepend_string(zhandle_t *zh, const char* client_path) { char *ret_str; - if (zh->chroot == NULL) + if (zh == NULL || zh->chroot == NULL) return (char *) client_path; // handle the chroot itself, client_path = "/" if (strlen(client_path) == 1) { diff --git a/src/c/tests/TestClient.cc b/src/c/tests/TestClient.cc index 2d45cecc371..ba12259583c 100644 --- a/src/c/tests/TestClient.cc +++ b/src/c/tests/TestClient.cc @@ -792,6 +792,10 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture zk_ch = createchClient(&ctx_ch, "127.0.0.1:22181/testch1/mahadev"); CPPUNIT_ASSERT(zk_ch != NULL); zk = createClient(&ctx); + // first test with a NULL zk handle, make sure client library does not + // dereference a null pointer, but instead returns ZBADARGUMENTS + rc = zoo_create(NULL, "/testch1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); + CPPUNIT_ASSERT_EQUAL((int) ZBADARGUMENTS, rc); rc = zoo_create(zk, "/testch1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); rc = zoo_create(zk, "/testch1/mahadev", data, 7, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); From b73b1f14628054c06da78f2d6a9cdd2a614a1cbc Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Thu, 8 Dec 2011 22:30:28 +0000 Subject: [PATCH 030/444] ZOOKEEPER-1316. zookeeper_init leaks memory if chroot is just '/'. (Akira Kitada via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212173 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index b12d2ee7a20..e6cf1f67d4f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,9 @@ BUGFIXES: ZOOKEEPER-1305. zookeeper.c:prepend_string func can dereference null ptr. (Daniel Lescohier via mahadev) + ZOOKEEPER-1316. zookeeper_init leaks memory if chroot is just '/'. + (Akira Kitada via mahadev) + Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 3e87aba331c..8e8ca14ea72 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -804,6 +804,7 @@ zhandle_t *zookeeper_init(const char *host, watcher_fn watcher, zh->chroot = strdup(index_chroot); // if chroot is just / set it to null if (strlen(zh->chroot) == 1) { + free(zh->chroot); zh->chroot = NULL; } // cannot use strndup so allocate and strcpy From 7be8837d34cfa8da501cb2edf3186d8acd8ce7c3 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 9 Dec 2011 00:21:27 +0000 Subject: [PATCH 031/444] ZOOKEEPER-1315. zookeeper_init always reports sessionPasswd=. (Akira Kitada via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212193 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index e6cf1f67d4f..f2635612976 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,9 @@ BUGFIXES: ZOOKEEPER-1316. zookeeper_init leaks memory if chroot is just '/'. (Akira Kitada via mahadev) + ZOOKEEPER-1315. zookeeper_init always reports sessionPasswd=. + (Akira Kitada via mahadev) + Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 8e8ca14ea72..088dd46882d 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -774,7 +774,7 @@ zhandle_t *zookeeper_init(const char *host, watcher_fn watcher, recv_timeout, watcher, (clientid == 0 ? 0 : clientid->client_id), - ((clientid == 0) || (clientid->passwd == 0) ? + ((clientid == 0) || (clientid->passwd[0] == 0) ? "" : ""), context, flags)); From 6043fda33e86a1129a9767483ab70df6f0490173 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 9 Dec 2011 18:37:30 +0000 Subject: [PATCH 032/444] ZOOKEEPER-1317. Possible segfault in zookeeper_init. (Akira Kitada via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212568 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index f2635612976..b090c466d33 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,9 @@ BUGFIXES: ZOOKEEPER-1315. zookeeper_init always reports sessionPasswd=. (Akira Kitada via mahadev) + ZOOKEEPER-1317. Possible segfault in zookeeper_init. + (Akira Kitada via mahadev) + Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 088dd46882d..bd2d980e726 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -802,6 +802,9 @@ zhandle_t *zookeeper_init(const char *host, watcher_fn watcher, index_chroot = strchr(host, '/'); if (index_chroot) { zh->chroot = strdup(index_chroot); + if (zh->chroot == NULL) { + goto abort; + } // if chroot is just / set it to null if (strlen(zh->chroot) == 1) { free(zh->chroot); From 09112fa12a462ea0be8a1637c1db60615f40002f Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 9 Dec 2011 19:05:16 +0000 Subject: [PATCH 033/444] ZOOKEEPER-1319. Missing data after restarting+expanding a cluster. (Patrick Hunt and Ben Reed via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212580 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/quorum/Leader.java | 2 - .../zookeeper/server/quorum/Zab1_0Test.java | 39 ++++++++- .../test/FollowerResyncConcurrencyTest.java | 80 ++++++++++++++++++- 4 files changed, 117 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b090c466d33..c7fe3bef4fc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,9 @@ BUGFIXES: ZOOKEEPER-1317. Possible segfault in zookeeper_init. (Akira Kitada via mahadev) + ZOOKEEPER-1319. Missing data after restarting+expanding a cluster. + (Patrick Hunt and Ben Reed via mahadev) + Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index ff761720b94..26360f77929 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -314,11 +314,9 @@ void lead() throws IOException, InterruptedException { zk.setZxid(ZxidUtils.makeZxid(epoch, 0)); - /* synchronized(this){ lastProposed = zk.getZxid(); } - */ newLeaderProposal.packet = new QuorumPacket(NEWLEADER, zk.getZxid(), null, null); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index aa8d5475774..e82ee0d4515 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -18,6 +18,8 @@ package org.apache.zookeeper.server.quorum; +import static org.junit.Assert.assertEquals; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; @@ -36,8 +38,8 @@ import org.apache.jute.OutputArchive; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.Watcher.Event.EventType; +import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.ByteBufferOutputStream; @@ -506,7 +508,8 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); - Assert.assertEquals(Leader.DIFF, qp.getType()); + Assert.assertEquals(Leader.SNAP, qp.getType()); + deserializeSnapshot(ia); readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.NEWLEADER, qp.getType()); @@ -517,12 +520,29 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.NEWLEADER, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(1, l.self.getAcceptedEpoch()); + Assert.assertEquals(1, l.self.getCurrentEpoch()); + + qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); + oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.UPTODATE, qp.getType()); } }); } - + + private void deserializeSnapshot(InputArchive ia) + throws IOException { + ZKDatabase zkdb = new ZKDatabase(null); + zkdb.deserializeSnapshot(ia); + String signature = ia.readString("signature"); + assertEquals("BenWasHere", signature); + } + @Test public void testLeaderBehind() throws Exception { testLeaderConversation(new LeaderConversation() { @@ -545,12 +565,23 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) qp = new QuorumPacket(Leader.ACKEPOCH, 0, new byte[4], null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); - Assert.assertEquals(Leader.DIFF, qp.getType()); + Assert.assertEquals(Leader.SNAP, qp.getType()); + deserializeSnapshot(ia); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.NEWLEADER, qp.getType()); Assert.assertEquals(ZxidUtils.makeZxid(21, 0), qp.getZxid()); + qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.NEWLEADER, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(21, 0), qp.getZxid()); + + qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); + oa.writeRecord(qp, null); + readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.UPTODATE, qp.getType()); } diff --git a/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java b/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java index 535baf37aa6..5b9d3cfe195 100644 --- a/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java +++ b/src/java/test/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java @@ -40,6 +40,7 @@ import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.Assert; import org.junit.Test; @@ -48,7 +49,84 @@ public class FollowerResyncConcurrencyTest extends ZKTestCase { public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; private volatile int counter = 0; - private volatile int errors = 0; + private volatile int errors = 0; + + /** + * See ZOOKEEPER-1319 - verify that a lagging follwer resyncs correctly + * + * 1) start with down quorum + * 2) start leader/follower1, add some data + * 3) restart leader/follower1 + * 4) start follower2 + * 5) verify data consistency across the ensemble + * + * @throws Exception + */ + @Test + public void testLaggingFollowerResyncsUnderNewEpoch() throws Exception { + CountdownWatcher watcher1 = new CountdownWatcher(); + CountdownWatcher watcher2 = new CountdownWatcher(); + CountdownWatcher watcher3 = new CountdownWatcher(); + + QuorumUtil qu = new QuorumUtil(1); + qu.shutdownAll(); + + qu.start(1); + qu.start(2); + Assert.assertTrue("Waiting for server up", ClientBase.waitForServerUp("127.0.0.1:" + + qu.getPeer(1).clientPort, ClientBase.CONNECTION_TIMEOUT)); + Assert.assertTrue("Waiting for server up", ClientBase.waitForServerUp("127.0.0.1:" + + qu.getPeer(2).clientPort, ClientBase.CONNECTION_TIMEOUT)); + + ZooKeeper zk1 = + createClient(qu.getPeer(1).peer.getClientPort(), watcher1); + LOG.info("zk1 has session id 0x" + Long.toHexString(zk1.getSessionId())); + + final String resyncPath = "/resyncundernewepoch"; + zk1.create(resyncPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk1.close(); + + qu.shutdown(1); + qu.shutdown(2); + Assert.assertTrue("Waiting for server down", ClientBase.waitForServerDown("127.0.0.1:" + + qu.getPeer(1).clientPort, ClientBase.CONNECTION_TIMEOUT)); + Assert.assertTrue("Waiting for server down", ClientBase.waitForServerDown("127.0.0.1:" + + qu.getPeer(2).clientPort, ClientBase.CONNECTION_TIMEOUT)); + + qu.start(1); + qu.start(2); + Assert.assertTrue("Waiting for server up", ClientBase.waitForServerUp("127.0.0.1:" + + qu.getPeer(1).clientPort, ClientBase.CONNECTION_TIMEOUT)); + Assert.assertTrue("Waiting for server up", ClientBase.waitForServerUp("127.0.0.1:" + + qu.getPeer(2).clientPort, ClientBase.CONNECTION_TIMEOUT)); + + qu.start(3); + Assert.assertTrue("Waiting for server up", ClientBase.waitForServerUp("127.0.0.1:" + + qu.getPeer(3).clientPort, ClientBase.CONNECTION_TIMEOUT)); + + zk1 = createClient(qu.getPeer(1).peer.getClientPort(), watcher1); + LOG.info("zk1 has session id 0x" + Long.toHexString(zk1.getSessionId())); + + assertNotNull("zk1 has data", zk1.exists(resyncPath, false)); + + final ZooKeeper zk2 = + createClient(qu.getPeer(2).peer.getClientPort(), watcher2); + LOG.info("zk2 has session id 0x" + Long.toHexString(zk2.getSessionId())); + + assertNotNull("zk2 has data", zk2.exists(resyncPath, false)); + + final ZooKeeper zk3 = + createClient(qu.getPeer(3).peer.getClientPort(), watcher3); + LOG.info("zk3 has session id 0x" + Long.toHexString(zk3.getSessionId())); + + assertNotNull("zk3 has data", zk3.exists(resyncPath, false)); + + zk1.close(); + zk2.close(); + zk3.close(); + + qu.shutdownAll(); + } /** * See ZOOKEEPER-962. This tests for one of the bugs hit while fixing this, From fba536ba82fa3678d93ada4f501f22f3d3104ce9 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 9 Dec 2011 22:24:41 +0000 Subject: [PATCH 034/444] ZOOKEEPER-1269. Multi deserialization issues (Camille Fournier via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212667 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../org/apache/zookeeper/server/DataTree.java | 33 ++++++++++++++ .../server/persistence/FileTxnSnapLog.java | 43 ++++++------------- .../zookeeper/test/LoadFromLogTest.java | 30 +++++++++++++ 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c7fe3bef4fc..e70a7a7fd05 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -21,6 +21,9 @@ BUGFIXES: ZOOKEEPER-1319. Missing data after restarting+expanding a cluster. (Patrick Hunt and Ben Reed via mahadev) + ZOOKEEPER-1269. Multi deserialization issues (Camille Fournier via + mahadev) + Release 3.4.0 - 2011-10-25 Non-backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index facfb388c86..ee10a4d29ac 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -928,6 +928,39 @@ record = new ErrorTxn(ec); if (rc.zxid > lastProcessedZxid) { lastProcessedZxid = rc.zxid; } + /** + * Snapshots are taken lazily. It can happen that the child + * znodes of a parent are created after the parent + * is serialized. Therefore, while replaying logs during restore, a + * create might fail because the node was already + * created. + * + * After seeing this failure, we should increment + * the cversion of the parent znode since the parent was serialized + * before its children. + * + * Note, such failures on DT should be seen only during + * restore. + */ + if (header.getType() == OpCode.create && + rc.err == Code.NODEEXISTS.intValue()) { + LOG.debug("Adjusting parent cversion for Txn: " + header.getType() + + " path:" + rc.path + " err: " + rc.err); + int lastSlash = rc.path.lastIndexOf('/'); + String parentName = rc.path.substring(0, lastSlash); + CreateTxn cTxn = (CreateTxn)txn; + try { + setCversionPzxid(parentName, cTxn.getParentCVersion(), + header.getZxid()); + } catch (KeeperException.NoNodeException e) { + LOG.error("Failed to set parent cversion for: " + + parentName, e); + rc.err = e.code().intValue(); + } + } else if (rc.err != Code.OK.intValue()) { + LOG.debug("Ignoring processTxn failure hdr: " + header.getType() + + " : error: " + rc.err); + } return rc; } diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index fec70de1922..eb4297d6e77 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -197,39 +197,20 @@ public void processTransaction(TxnHeader hdr,DataTree dt, rc = dt.processTxn(hdr, txn); } - /** - * Snapshots are taken lazily. It can happen that the child - * znodes of a parent are created after the parent - * is serialized. Therefore, while replaying logs during restore, a - * create might fail because the node was already - * created. - * - * After seeing this failure, we should increment - * the cversion of the parent znode since the parent was serialized - * before its children. - * - * Note, such failures on DT should be seen only during - * restore. - */ - if (hdr.getType() == OpCode.create && - rc.err == Code.NODEEXISTS.intValue()) { - LOG.debug("Adjusting parent cversion for Txn: " + hdr.getType() + - " path:" + rc.path + " err: " + rc.err); - int lastSlash = rc.path.lastIndexOf('/'); - String parentName = rc.path.substring(0, lastSlash); - CreateTxn cTxn = (CreateTxn)txn; - try { - dt.setCversionPzxid(parentName, cTxn.getParentCVersion(), - hdr.getZxid()); - } catch (KeeperException.NoNodeException e) { + + if(rc.err != Code.OK.intValue()) { + if(rc.err == Code.NONODE.intValue()) { + int lastSlash = rc.path.lastIndexOf('/'); + String parentName = rc.path.substring(0, lastSlash); LOG.error("Failed to set parent cversion for: " + - parentName, e); - throw e; + parentName); + throw new KeeperException.NoNodeException(parentName); } - } else if (rc.err != Code.OK.intValue()) { - LOG.debug("Ignoring processTxn failure hdr: " + hdr.getType() + - " : error: " + rc.err); - } + else { + LOG.debug("Ignoring processTxn failure hdr: " + hdr.getType() + + " : error: " + rc.err); + } + } } /** diff --git a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java index d92f2d38142..2e78193895f 100644 --- a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java +++ b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java @@ -18,12 +18,15 @@ package org.apache.zookeeper.test; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.MultiTransactionRecord; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; @@ -43,9 +46,13 @@ import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.DeleteTxn; +import org.apache.zookeeper.txn.MultiTxn; +import org.apache.zookeeper.txn.Txn; import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import java.io.FileInputStream; +import java.nio.ByteBuffer; import org.apache.jute.BinaryInputArchive; import org.apache.zookeeper.server.persistence.FileHeader; @@ -157,6 +164,14 @@ public void testTxnFailure() throws Exception { LOG.info("Attempting to create " + "/test/" + (count - 1)); doOp(logFile, OpCode.create, "/test/" + (count - 1), dt, zk, zk.stat.getCversion() + 1); + + LOG.info("Attempting to create " + "/test/" + (count - 1)); + doOp(logFile, OpCode.multi, "/test/" + (count - 1), dt, zk, + zk.stat.getCversion() + 1); + + LOG.info("Attempting to create " + "/test/" + (count - 1)); + doOp(logFile, OpCode.multi, "/test/" + (count - 1), dt, zk, + -1); // Make delete fo fail, then verify cversion. // this doesn't happen anymore, we only set the cversion on create @@ -193,6 +208,21 @@ private void doOp(FileTxnSnapLog logFile, int type, String path, System.currentTimeMillis(), OpCode.create); txn = new CreateTxn(path, new byte[0], null, false, cversion); } + else if (type == OpCode.multi) { + txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, + System.currentTimeMillis(), OpCode.create); + txn = new CreateTxn(path, new byte[0], null, false, cversion); + ArrayList txnList = new ArrayList(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); + txn.serialize(boa, "request") ; + ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); + Txn txact = new Txn(OpCode.create, bb.array()); + txnList.add(txact); + txn = new MultiTxn(txnList); + txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, + System.currentTimeMillis(), OpCode.multi); + } logFile.processTransaction(txnHeader, dt, null, txn); int newCversion = parent.stat.getCversion(); From f9ca21c802aa85294f499efbe8a0e6ee976bcddc Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 9 Dec 2011 23:58:00 +0000 Subject: [PATCH 035/444] Preparing for release 3.4.1 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1212693 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- docs/bookkeeperConfig.pdf | Bin 13786 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147553 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24944 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17101 -> 17122 bytes docs/bookkeeperStream.pdf | Bin 13179 -> 13200 bytes docs/index.pdf | Bin 13475 -> 13496 bytes docs/javaExample.pdf | Bin 33781 -> 33802 bytes docs/linkmap.pdf | Bin 12424 -> 12445 bytes docs/recipes.pdf | Bin 31022 -> 31043 bytes docs/releasenotes.html | 116 +++++++++++++++++- docs/releasenotes.pdf | Bin 84059 -> 86699 bytes docs/zookeeperAdmin.pdf | Bin 72877 -> 72898 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6634 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48813 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16461 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12852 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302473 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133738 -> 133759 bytes docs/zookeeperQuotas.pdf | Bin 11241 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27542 -> 27563 bytes docs/zookeeperTutorial.pdf | Bin 30483 -> 30504 bytes src/c/configure.ac | 2 +- src/c/include/zookeeper_version.h | 2 +- .../content/xdocs/releasenotes.xml | 85 ++++++++++++- 25 files changed, 198 insertions(+), 9 deletions(-) diff --git a/build.xml b/build.xml index 9d8e066840c..2cfeaaf1e9c 100644 --- a/build.xml +++ b/build.xml @@ -24,7 +24,7 @@ - + diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 5f373cdcf45ff860bb943353a361b0c89f8b73bd..5b4ed2c36236319e996e78efa893be4950b7f64b 100644 GIT binary patch delta 977 zcmah|u}T9$5XFGap+ty{U~!-^5D3i7?(EKn5HaMgR0J(WMYIVB#uQfO3j|?nTPqvM z2Uz$47IyxGwVjuUV0I7OYI*baz1g?3AA`@qy1CSCHi*IyVNQ&H!>zI9padjr zW6XsTV8w1r2i$WfB8q7cCv$^I6y5<*sqm=~b;$s; ziJC!U!EbZtxR0u5fij(w(%DX)_eD&v_d#fpFTDlc!)dz(*f=TXnMc( zuohwUL9-n}(_!;@W8G!b#`eojo3a#UC%s9N`2|soSD0t2zi_|e_z8>8duvMTWLxAjtIvOZ1)GJ#kv)QYIi zWHrw*D;_hYaQZiTW30BC`8YQqZF55IEuf7Kx*n}$f?O0 zo(-hV-EIXeajFOcssw!4y!qNK?H@p#79#5O(SWjw7H7ePu8VOZx)lq(!auU}^?pF diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index 3b5ccc84f3432a0633b0c8c704e23f90795c6a3f..37f1a8cf9c599b8bb3bc8c43ee1a1928c4106094 100644 GIT binary patch delta 1191 zcmah|J!>055XIZ+a6UlDQ7M8IgTOGeGqXD@U1TC;wZY)l;6M^clfrS4PKH3@#$c0T ze?^MmBDIPHQshVEPozy&wn5ykAlKaS-h8}yGxz(f_2;a0`SGEJfFhn~7jy?;=j-K< z&9>czaELM#KfED^0M{??U2Lo)KL5QxSchNx2j?xr;lrZ)Y0=&Iw&U=1wi=+6aHrri6ft=NVHeS`H-N`0HBJvV{)u#W*QEmyPIYMkbHT^B? zGigX(ZJZ`_r`#@g6CbpS1&E7MD_ZAm^=VSiof6rVV_s)-dUrriC1rN&j?EAN$H*euR z)Fr=TI9%-qhBxNP*g;l^H*P-Q;pKUnzdN4GYHx;E9S z7_s(}%gwc7%5A6`GW*IS7OFgF<;o5YvcdAjzVk2Sp@apd0FUO%3thDAwY*{OTW)DS ipADz~O?lqTX3e{!mxo914`01*kQFgBqtW7V+gt%gwDWEN diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 9bbc9ed0291f735eeba11ed042d220829e8a12a4..8f2037134176b63b1c9a0d1d1d7848ca52075861 100644 GIT binary patch delta 1405 zcmah}J&RLO5G7m5Ly4fW+6DA&KwOwPb3elpki>08u#rt%EwmI|x3JMyZ7B%1)gK}G z2l^A#!hhjU@aBoTVJ6_K9y4=i&YW}Z{rz=t_HFR7Tm@zJJ__wq8WWAbeExp<8ulqa zWN8m@ztZ@k4rlNtMScdb`QiD(fS*`8a4e7z*}N)sVtB7 zy69CoA^B)ynN*AeB&itSM4#o9LsoSZi2lMHUf?804y9eB@_KQ7H)bEqDMvq@Z6AG z7c!fNXhDT!DGxcBevx?%zTI>^$LerBI7&i!a@=;DOfIvz3Lr5igw&|&TVn=Pt%^n> za-ok^C!Di^dKt8_P#M(>ff8%b^-&Qsgvnfw`U3b(*?K2d^IDhlmW|m3voZU)*>Ei| zxay^u;h4=LoOj5TpU)1C=R8ZZESda#p4Q5G%W-$mIW=uy1|1DWI(`obO^!dr@ S+mjbB8;A%sZTsNiVe3cIFH~H z$i9JsFQ8A6Dv#hvf^3)xc&p2I=gjvrbMEJ#z4ITvueC!IAdr+_e*gCS&OLmL@*HRn zWUoQ%)4N}K4yzBgA3A7#y#43)Rx0c9{cisen&AL0p27P;clkmDH(*4iE?J;*)=iq6I>sp-%=Gk|#k)fc4Ys-IZ#IA8)WO5><)D$i;H# zSHL#vuf$_%c{K5Mef6=h1oejO4-=r2IzJk&m_y8r>fUd zZnRC<+*>FXmZ)9j&=7%wAw>4Z3ES;-c9Z%bN6HmM4Spe@hO#$)x*m@^*Rhary4JBWAE}IGjD3 GcYgtZ`xP4i diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index bd42e9941df875f78d0074c8971cdc09807c470b..51374962401f7f0b805bc2697e16a070f58f2ac8 100644 GIT binary patch delta 1141 zcmaiz%}QHQ5XXtT23tibQdfbR3W6}-b50PWvAG);B6QPDAEA%XtlS6%;jDd#kmu;i zy@*fJBzF-rC*WNj=JPi*|NHN*^Lp2LD7yq1Rh{`1(lNmC^zmtXpH3nFKpm(LmW~0y z?9a!)olTDNW#?p*eRjL^(%mwe@!|CAiRB(FzTWR%clkhZNtG=nDWA^2O2B7uOkxz{UBFtNxv52LD}2*k!923Yonsr(v#J`lnH2IlJJet0xd zREf@H7@Humafm~uYGOz(u^uW{^&q9RfS#;fMmbdzXF#hOi$&W)Ock_h1p}GV8$fC$ zp+?mTvtSLQYUTN&KLL*bNXAho$W#HK`dp05wkt`h`-WV51KM&jx7V7lZI#Sg-51#~ z4utF&b%30(Zk(waPGD`gp9>rmtQ*e-8n=eRYsqb{Nc;;>)TGep?27#|Uyd7V5X{8K gh~FD(WHhoyOtYW&&fW_t)6xD1@@~IBzG=Gu0f$WG3jhEB delta 1111 zcmaizze-#|6vkPxy7;HC2v{wc4JZib|I8TKbvNsp&calI5G)ic3l+56$|6NTm`BJH z$Ua7}$_t23;@z9V>m0$UF5ms;e7`eu=IOTcaND`d6`}!wiG08R^KWSdx6!@P7CdNBD;h#3aiCl;=hJ$M8@!iUW=%~) z^|7@?OWqig4^H5k6hP+_^ua>VIyI;r04V&%W+69hqQd!LwEJ^x@MSzynKQI^;qCwc diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index 655945f7417c5904637cd9286466f6fa10760185..e43340b8a97ff5fb6ecb2d5f60c4df2a1364ac21 100644 GIT binary patch delta 845 zcmah{OG*Pl5XHCyv_&AutVwskv;bOfFP|F?>VTXhcUgDK zLKk3uxB0wMRj}*zb`?G!)*set%=7o%)%~`LLnuyo8wTYCQ4xC>$7mNPt;{$TAj9Be zCUP-}lyWdc?e}sVc^WEL`R%o@Kz0E;d!#*8HnG@kJgyAJyK)XR8F zN_)Eou4s-cFfG~>7VzdF;Iiq1A}&OXyfBd>W51|(i6$AqC>=_Qq6W#SaB5hgu)#D* gFz$!Fe<>G1zaQLBuO`#S$@MruBH*CeOwUHa52$;uk^lez delta 825 zcmaiyy-EW?6orWj!lDUSC}Oi9ix3d+x$_?+FvLx3VgDm#6oYCU&8Zh&!Y^HLC7b9~uEG zw_5E0RmZKH+TN-Q;?7J{;W+ PF9r!1Rwh}NANG@PO-rk> diff --git a/docs/index.pdf b/docs/index.pdf index 4849d3f59e4d103b3561b917e761911203baa4ff..4d3995e8b891d88279d073aa8f365672cf4d2114 100644 GIT binary patch delta 954 zcmah`yGjF55Jf9llmr9|8@CC8KwxI>%)NI)NRh|3BHCDp2r8tph$-w+TL{8_f*%kh zAo>+Hc7BAvVBA=&I{|MqmosOWGv_>wU&oL0I)S1{pwNcc1ZY0LeQobj8}tOFBcW3? zIsl88orjHejr^$AT8A(0Ys31c!s7klv)*E#!p}=@(xmtxA)>(uT>b%#*|Ju8KrxEJ z6k`<&x@MOPTIDgQosC@ZW+kD(4#*61sEP;nYy@&da|mmwngN0Wr5&e*MdC1&z!kUf+%tX|x*kwj)c}|S3D9-YNNf_d9ihmqdux=`O; iqKtLWOS}J4&XQg)xt(52rgxL8>ja4zlSX57m?htZw8XUl delta 926 zcmah|u}T9$5Jkb}P?H}Juel~5%*^iW&V&%rFjoTahV}`f~+XYqfzz^Z1=yy diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index cd563f7194840e9296fcfa0375f11f91b74de409..7ac0a98b9a6fe800e221ef6720b766b1b2aa91d7 100644 GIT binary patch delta 1357 zcmah}J&P1U5XA)p2jv8D=K_O;bN1jwsgLfi?&VxLk6l9%#6S=cJOcxR^#YUIaW(K1 zgu@Qa{0FkJKfpg==)Vv#vv=h@uj;^wW?olSzj{?~e!S_t+wUB_Jx`F(dttf?aUEd& zbN+hvLitE{(GR=~mIniX`O|anPn~p-Pp@p8gwL<9+&RYuc|N=_~{aF^9IP$!Qiy6gf}Y=0+l25}jk+GKK`E zh6@z)w|1>%^bizhEMwDyC<4PDk&ZMfxzR8e3Wk+9&_iSlp|EO?EeDEsnm%L6HUxuc zf{YP0L6iuVOmas<%s*dVsxj(Bf^tFLm^EE7+e*ZecV_+C8$pN@$NXv5)Q)r?@CgTc{fRL}fTFfIAw=AvlcNYsLT0crMjHvkBD!256*ks m7KX63HIiX(^S>bvs^PGDzWZph`(m>5q+$Y+s%b{I$JKAY;u$>v delta 1306 zcmah}J8M)y6vjmmuf{aG*TP~U-YufSdCZ(S6T(W?Y%IlKB}fp|MlcW~*krG(g%1#g zsl?jGPWC5QiN8V+|AD>ZM&0C0;jJ#`o-^}3&UatE==^-%dHb?L6d({MH$T1ma`F^j zLb(C70FQXI zDl(_-anoh6uo1wgkK=xqNDL|@2hYH^#BKcwa` zF+gNvKxks^jHo&m$aWj4Vb$oh%D{}8V(Z9iMx8eQ?kBV8M=JK7l1 sXeeQ{?vW!YtqJI$xBB0fTh(AtJ>0#$v-^1G?!79D2~su9=-Nj03-b>V761SM diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index 5d629e82fdbe27bb38d0d25310adf6b09d13be3c..1d1d4af05a91300841194a14bb1f11165fc6700a 100644 GIT binary patch delta 976 zcmah{u}VWx3`G%yhgw1E+@%(ZwMpJha#Ko&))p6WaTV#HqeJZ=E?or?lsAeWA;rPZ z@I(9r!Plo)d#~V~E+@%J&Pi^DPs96%O#(w^O`!q422gu`d|TV12G}uji?vk*S^(4Q z^}D6N0$I6K{|lcUmR`!s0@Ic1?P}e)0<%}Ss3gS)5d6=OB$RL;R|p%;yA$77~I>B`S6t6FLP6h5_d#;$v_N;wCeTn2^HayvIUf zk;fbw39N)st&f(FuRtQFgY5XFFqhZ;dF><-~l1V3hGXJ&Upu*jWhZLI9#5AZJ>7J?vxaLt(sbkIW6yY0ud1Z#?J zKY^y#-S_Uqw; zfrA7y1W;g;pc2+bP+c%LR#DSL{pw9-9yCNv&3Nz@BmFURxODB=Hi^vCe4Y4p1VU|k zr$vpySdMLvq%A@_c8ih4gwi2l`yQ;GUu@;>-_3@g+;m`Ax>Bm(+xX<5^ Zs`4R9ptF4VAEmj4y-kf-QJi0lvu_Wjy7mA7 diff --git a/docs/recipes.pdf b/docs/recipes.pdf index e1035cd92f3a70d03fda3d705a9d37148ade43b0..1aa9f7e7b01e2ab38ea62431ce7d50004daab9ea 100644 GIT binary patch delta 1446 zcmaiz!HN?>5Qd322W6FYUA;)a#K6L0RdrW&RSZHx%ymJ%cob1s5QS`%6@)Rgwf)bfC zFf|X~?PYMWPl8+8V*dR2^~-rR8ddk79Otvs-b92a;~7sSVVEHxj3;x^V- zU3DON1m8e>0Kqr7CvcM#$?XLERX@HvJAXbivp+97=NFx?-x@>)0*>MNkJDe*Zr}st zr$7h52Sr+Zxqi~Aw7lQ#SJL85_f~Ty^5WCohtK-JJy?Fd_q}HIL!ua*X3_GW$^AKN zbp)gdyr#}Ygdmk2DRQhd;82peoDxJpTWQ@*o`nKy{yf#I7E;J()3%=otD8s|Yj321 z^55zHz?BKclAMP?PK#C|zL@RZNhLsiL6j(epS8EmTxh@)^Y3yNh^l*}5g_0Eus09p zD3?R2$rD8JL{oAN1;)Ap0*Py)8=#~dmfe_Nng&qcN)>e3BcM-4JORX16C)(uO6J~r z3?n(Tl!!4u`Pj}QZCzN~tL~s?(N&(JuRNvT{G}a5rh;hMwt{H0FKXt3Fo3I)L7iqG zZc&&#NhaH-yKE7V?Lb+c2G<%RPm2@zz)Ws-SXMJp!fgs>o$oNT;2a7>myDDRs3%{D z9exRZ?j-7Z3AUaAl4P3Cjt^grna4Ih8Xf5{sR%zBCPsqMu&gyuv_bFiza}r6!JwJW VUQcGnleh00vH;Sw?db8i`3DWVAcp_| diff --git a/docs/releasenotes.html b/docs/releasenotes.html index b065e1cea3a..395b7b81913 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -5,7 +5,7 @@ -ZooKeeper 3.4.0 Release Notes +ZooKeeper 3.4.1 Release Notes @@ -202,12 +202,15 @@ PDF -icon
    PDF
  • -

    ZooKeeper 3.4.0 Release Notes

    +

    ZooKeeper 3.4.1 Release Notes

    @@ -233,8 +236,113 @@

    ZooKeeper 3.4.0 Release Notes

    - +

    Changes Since ZooKeeper 3.4.0

    +
    + + + +Changes Since ZooKeeper 3.4.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.0
    IssueNotes
    + + ZOOKEEPER-1311 + +ZooKeeper test jar is broken +
    + + ZOOKEEPER-1305 + +zookeeper.c:prepend_string func can dereference null ptr +
    + + ZOOKEEPER-1316 + +zookeeper_init leaks memory if chroot is just '/' +
    + + ZOOKEEPER-1315 + + zookeeper_init always reports sessionPasswd=hidden +
    + + ZOOKEEPER-1317 + + Possible segfault in zookeeper_init. +
    + + ZOOKEEPER-1319 + + Missing data after restarting+expanding a cluster. +
    + + ZOOKEEPER-1269 + + Multi deserialization issues. +
    +
    + + +

    Changes Since ZooKeeper 3.3.0

    diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index bc7f79d8a314884726d89c93a2b2f3cd5bf52fd0..882a2068deccfdcd0c119f160335caf8e6eca567 100644 GIT binary patch literal 86699 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�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 z z6eMCBCIE(z)kAK>1o?-_f6qgb@xX!a>2AXWEa+rR0E8i@ZNmgb#smt+1Npu3#Fb)e z{`)fpqmW?oCl$XufM_H%6q0+)kDf-wxI#o@cVhcaSaknzA7WJ#r3-<@NJmDw_yU*gLH91L;nWwzia~a8ycFNh64)`*#ptrFhOs_1O!Qt-@6SHu)!mRmEd!9aH3>HL6`rC9|I>t@F)xt zh9%t4hlt}y*B%rQF6r_Cx0Q*164(X-6Uy`fxO9fj$lzH+i9Tx+D-nR5YYg% zlfD@=Xfs*ofUSXS3ycQ4NwS`RF+kQ6u$v^KA)(vQ0N5dwAo9uasebK)lv+U94!N5guFZ{UI1wA_xSj4 z7-OL1Xc<^b$j~wdKyuQKfEPlBmN5X4|L)v>cmV@6h^*Dhfx+#`oC3dfgyVLi9pAIz%4U0*ybSNyG2DcjkR|F4|*si;s5{u diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index a39da1c5d3b494a6f0797d38b652d5082d968c3a..561a27b8356e0c2ed3ac07803ff61a29994fc2b1 100644 GIT binary patch delta 4031 zcmai1yKZDf5Y=66g1vqNP8KwQv2dR}cTVdied1+l4xtRpDc5Pl=ws z{`t>Gd*zt==WHidPp&^WA-eo#0vYB>CK~|PP#F?JGz;t zKx*@dD3H=wsXV>6IXoGykVkM$)ny3`ywwXwn^14@ zE|c^aE8`$ySPFs3I^qYt7ny0@B1lZNWm_+8`h9aa+^;3lfJIcNzcz=15l_VWF?ue# zbE_qtbG#d3c569b5H{k%E;_$^xR@KeNj_MWrmHRFAt@^E%6sJNu|Em~yy8!p~2sXGf$o@P74!IyxM zlDDQ3O90kLin3Xav@fk{M7FeU)2}U za9ESr`SkA=CV{k``oq0idaGwGaUc+jD4ml=0NFH21QZ8+<8qu`Ys3@@C4qqqaRk!6 z)Yvo9Yw8c_t=%(fd^(p*yrV^j$xbX=&GQb_^2XUb#JrBUaY9Z6!8&_v-8cS|av zR5@m>)RN?5{Pp{cZxY6uz;lYt@xL3!hCvXysEyP7(}uPT0cwLjB%4{}hhi))oV%_+ zY0E^?){|CgXM{Va%ze<>OKLRjQWqY%zMwrfkoFM^Q*xM_MSCytVJjt(3$`@%F>#Yj z8C3Tg!GncNOg*(7Y*3V)eROIhJoIOQ$Twxh;JER-=(bXgoW(3o)$&RSaIbtTzxO*E zE+^Ts?n%$B*AyTeCV4P`8>@w^g9L&pj>!~t(TP{siX|C=9Ot{1=;T#WhdY4LG0?;@ zm{Qc(akH%@E;^R0SWYO~To9MI?R-Bl#=8~28s~$O7Ac7MdEpL{*LQJ_v?HkD% z^D8;*C2EitN+ZoH=oRl4?y>p22qgq-#oBzM6n9$X^L->btPrMXE4Ct-p1Q;f3L5X$ z-tLkcZ@*!cyy19T(K-d&Xmp6=Qh>sN=^%PzjlqoA$ui*@Hk$N^=G~@ATFd4mFYyCS zlFB6b;ua^hV;{+xbiae^7A@#pwsHEhiFrv0E!Bl7zkd4#7aBY5JRY8(ou5Nr&QH%y qPfyRy&)Rt{o#evt!HWz0^V#gv|2s1M@OapJr}5h1;l;}rYySZ$K@61u delta 4090 zcmai1ziVby5ansA&rMmbN2weoKZ5D}i}f#uVEg8+&BI_i|9JCg^Ck83@7{d+&tXnGY5wh;?JO?- zaILM8trwflUKtKXkN-=hS-bJfVgPk(e&&)oR+3D=+#X&#)DrHe?mKn&$6f#2K8Pn5^VfW=$%kbr z(Tf%XyETgh%+7DmE+wALXooq$ejU^7l^C4?LOfjXygL1`Iqbh@DOTScw*+{tOOAd; zn%MOB=CFGgf2K&RlIul+*YiUkl~|?XqB@|nBU{To;^>~TP2X;#$+SB4_FV9oj5KZW8fi`< zimQ7prL;;q=^yl4#_b=hs?j--(xplNR>S!y4$OW6m7n`nf;R1hy zu15&522k6NmLiSdSuwkpAAO-73^sALO?RonlzecMkMnv|J9jQ$ms}R??&%4Gd;Yy{Y14EIy zg(c?&-^S$^9u^vUF_ys>j!VCm3ne4MglmOS5RFUDn7kFv8oLsaOIVOxsuJO_z3@XY z%OhIzWZQWdAk<9XvaY2O3W^nkF0_=VgExjdAD%p@cG~V)%O@xI{WRalQYljWo@u>n mwIA=i`@b$9ERK&CpFR2L@srOVfAZd~+m!AS;uZ+2%DONz7UnMhXTXppd7)1!fqST9_DMh*=sNO}3CwPqjps zF|sf-#1ykMK^HSMG{msU%)rtVU7d-6slntM64rqjrkWa?V2YWTV!GAT48tZn7XUjKVLSi; delta 428 zcmexw{K|O3bS^_fGXo<7GZT}jjmxj|@EPhFnuQn`Ss9pF0fjbC7n@S(ut)idk5o zi|MsCkp`OPJakpzAR;!J^011j9a4GgGt4sggEvW`+h9 zCT5P#CPt=4W=_t=21bTXmac{_P8Jq!rlzirb_zBGmBez{*>M$@Bo>ua6s4wdnHd@x MaH*=g`nz!f00uT*SpWb4 diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index 79263094ebe87e49ee503d1c92e165c75996944a..e8b38021172b877f6240e87837c2dd5bd6768875 100644 GIT binary patch delta 1769 zcmah}y=qiJ6vpkcXd(uQSnI(wXHMTa^R@Q<~a63C8I=v4Nu#kX>C@E6- z5SBiKUDU~D&93Jxc&p1fGjqPbnGff^v-94;>zf1xRTIk&s;mcWsUn1kmc?ajrj)Z*QnvE?DRro7B4}!wo9g z4ThYU>hqI(j}nI_f@F`LG@D1&YMJX<8;}DtN}5ysbu#Rfy_gJu9uTvbIgkw0Z28w5 z2F_kF1~pHfAzjYR3(j&&lqbfBOKOa{e*e7Li4jMrNxT$XfP=Zl7$|X0UJfxXK0w^O zZIn6}Ov~XwLGhGCQ`AygfSs12F}W-)e^=|ezc->#9IO%<14Jek!XyUKM9DNCJe&K# z&_V*Fs2|JFj%-m<4lY9 zNMs2Sv+z!~0CpM>3I)?eIK*5;;{9ql56Vb_I3v&Q>%t=|QV8p<)7{CwK$D%uI+^g~ jv1KgdvNuM`koS{PemL0u-;(mKK`lSB{({pFK delta 1730 zcmah}J!@1^5XJ3$XtuCO%7t}}M1+|;bLZX(VbRr1qhOon$a*bJo}GVv#?(r;yJkw0+ui49JH(T3D@$+J5PER<^`7>c*$+ny>}o-v?0>q{ zMlnkeGG$*&XlkSgb{|JpyN{8xr38W)X7}~yJb_fB0Raga%Ddg%$+7(2U0oKncOit7 zN_RR~X12#eqPli#dhuG!Y!8N*<%q@3!pEiFbbdX+20$q8LciXby}F#SWaqn zEL9y6?rbIOY6OP{pZ1q9&AhC(&xaI+>^CkjlOi~R6n(ZyvK5Fao$_}UyBe4U#>F&G z$8pEJppz=DBh5&TAO~kUGhKEu=Q>4Pia3_6&e7Ov(>x8qJsZgt1aS_-Sz%Tk@Vzd`!TJNP9Z<(Wtz+`NJgoMu@QgNtY#ck zK#7)^SqvB=feRUlrhoxUdb1H<;CXiAlY)E86GeFGZdoA$~t<5D7@z^RA z9r~A6IU}c{Kn`pe98q)F7Y#tD1E&xIPg9*l!JVf3T{pK2gB*o)Br#)T|J1H*p+r+g zM)IHSa%L38PBhxkoz^y-0BYix8IJnt>HeaA*5$fvE}51YoM+BOW(cE^A}Nf_kk)^G z^3~{kcrh3y&1SN9I=mjFB#D3D-{~B7QFR@6OpnQG2MmU~2J@d|*@|ta&Hp4G4@%2y LlS-v~(o4Pp_CXzEr)m z>q8_C$Cig0<_3@IgQw7fM7ceg&=OkYD57VXGC%)4;H?iIMzk(05(!0%hpe$pB#|kD zu~Fltc+cuX;;1krOVI_AC>fHISf_t5?;=wSvql^9Y0FZ4R237N;$ydUUOHw^d2YHL s(@}6%tR<_Ryweqzqs`LRzmn4=%aXgnMSpPLzq(G)5=+u(9317zH+cEE2mk;8 diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index c9952d3f98177770d0688ff480856a76f24f6a37..6ee191a0e36a2012450eff9998304c42ecfedcc6 100644 GIT binary patch delta 798 zcmaixze)o^5XR9;5C5Se={(RFP&6~QJ3BK$1G$*AHns|ZXlW5s*rl@*0tqrH&}?U0EL?S;l1 zAV66|4biHGK2nW%7SNSG|5!JETHma&Fol^e%g8Un&Ipm>`T{QED$@`5_xBw|WI>gn zS}`SK0u98Ng10_1PY}!C-(h!zf*52VibUihJ)ANEXsl&6>;S<~?6c*DdNW%FqNo{g z4#Eb3IR=VEs|3}|KjFz`PMwxvbh$vDLraiP+fK0E>N>=ZX{N{YpXDNHHj~?7Z!o+Y QTwN!KY9y&vJ7?YG3#d4!K>z>% delta 758 zcmaiyy-Gtt5QR}JxLfoZZjC+u-3YLnJUj&YiqIeXOkEHqtSeHc&gE$@S{} zf?_exHx-&3=3mQ;)=gY3u5UrW0;a3DDze!I5xuQ26C{4`G{>IQ5tv{@m9druVb-;X zjw`iZShoi&;%7bbxlbT;7#~|J+>FzbmI%r<2`` V&WEGh;l*Y8;)0wt8s$+x`vp^JpaB2? diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index 8566f1010e90dc6c82b4ac32e2cd87f09007b932..f51b668450270576f88a31e03f991a4554003345 100644 GIT binary patch delta 1991 zcmah~ziU)M5GL^j5+J;oIOFo*D-q8peFTdHU_hun+Hljb9uVe_5Z{sZWfYzdMTjy16nU z@2I>dcA6{2@A*kvS}09vxO@OcUCuwdt;ZJ;nGx3xmwn$U(3;3 z%woZF`tzZf?Rh)+O-@8~c`VRi%z~$oV=)QC>N&zUj1HoT8DtIzvHEeMaSk#v&N)hZ zAB!vV3cnhG84|Ocu5A|eWdxhG#u)Swuxu+)`zcp3hw1BPF>}`Gz5zjars-5%=vl|! z9`^CC4eNoNDrh+vR;`JLoHGg-F?dxEkbKBv!9bawrDAr`57w}T0x4Z8EYKb>6k|yH zsi^mu!DVGQI0Y*s4?*X#=$DmdimElsj6!)pXQ9QFHR}E1SBa#31t$I6E~a|yg02Au zqUOo$F)x8O=M+Fo1;%-0f1wn8)pt*b9{JjW@!3pS$ZIjc5Gr7lI*>OKLk=9cPw;Lf zlmH7Kr6e;^1`L4ObO4r2I=Ll_AG6LqlO_N`tgEmfK*+END8nMi1ai^hVdLY+N=neD zFI&a*n3Lk|#ed-V~J^3VSTge}lKO_cv&7_Xp zo|;3zn8$p!avbEz&XUxtT-4_y4zhhZkql;sNv?t{&tgV~(CP8WV2|^9M}G|D^#x>^ z!jQw{^lW9peKsA+y~0u4uSlQc2wHy8StgM7w~NIG&}p^1ri-oSavNgPwOnkOPPfxW nh>lzBrSj^3Q|{N6mTFJeAFi!GUt4)pliC(SZDyu(qg(q6FZq@Z delta 1912 zcmah~J!@1^5M|>H=%NWEDHR^ZM4~7&b3Z2I zCDyjVVvG1EQdsyeY^-dYO?FpyZomhR2j`xdGoSZ;IhuNRH1+XK9WekwaPisU`|lSg z@D_$AK#LF-1^Tve=|fFnpZ-kTP|!}A?54@um8WCm-mY)fPVq#GJ&f{zt>vgN5XhfkaTq9ov#dk}bEE@gbma2!p1HGB9(hnPiM|h~T0<$c zNEWkio7r(<0?4o0W_m$YNm!P7G;*}oxsdnUX6}AKH+o1;vR6{%F_J0?ae#>VFq`Qb z=O1lz^)}k*oigoVNt;3pX!GlAu0K}ElVr*&1%jA#^1=(CBWFgb>CbFtTj+x;kExg; zkQS=y5Hpn#%TTEP3(tDhCqe)#?Qe|^FFU78fPh6#%pOaJqN6$_Jl1tX89dpjzHY0( zHJq%j0)mffKt(|+H#9!AMTk%eGna`Z_fNR5yTu`v!W_#IMqdb{JUSR@tPTb}Tgupx z`3w2?zL{UM1x2R2a39eZ?jsg8aY>q)x5fR8cHtzu{Iz4QjZ42$%Euj3EkEqSub+BT3^wpO|u)PM+vy`9SsKbxv0OKC$<IvM-n1tlP15lpmHq#E5BJde7a9K#v5A~Go5^TFRDCMu2PW*HC%yadJgaY9{V z`6E!|TstsJ%?FKUXf_=%CKsX6Tunau(DZRRtu)+nxL4i%A0=-enCUvQug}e`JX)>) E1z1vt`~Uy| diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index fddf5ac3e352d1e09174e2d3237cf0e18c5487a7..1e8257279140ecf21e85aa273e3f9c6655a1ef85 100644 GIT binary patch delta 3851 zcmai1J&Rps5az5P$wgTueq_5 z*8pa*;_|Fof!{KkxX|#=c&O}OK}*ePc==&>@h$Uts-iYs?nV|c zQswHCQAN!RKRz5I5)Xqh8|ns!&yj}1RC##iQFoySlI(C`(Hu~c9WUA;+C&OLy z&t)}p(5tlpr~YK8lhu-4k|?59+-2z1=9&e18XCx|vJYCS(OT9oCZ0G!w57{_rfK_#36DEbI7M<-^Lr8qAmEV#Jxx<;WBt8WFj>V0gRR7OpYC^1!7G^!3S11AT!xS zuXL?NeG zzq;_aiMLlyfxqAb4|+D~_aZrDS5mBW?2B#q?d-;D@0rZe z)i%F6-%mB$Rmu2wJ~z7ddfA_A-rIile^uU@?d{Dzx%vK$o1fqK;KLb4&UF91i#O)ya{Bz<<)7|dp8fd8el9!I z|8_pvnfL!JMJokcGabMDd$u=J=T9Up>(sy7n!kf8=Qj0Yfz6zadj`%Xl;ywshIOK( z94;iPDTM?xahhJ({m3Oll-Yum z?6qsfYV+9?Q*z}2ndIR@f|bg6^Pk!7%c?&8S}E!HZT~3bBh;318-ME`3)g9OK)h%k zFFcyPy66B3Prp-bHAGWR$!DM`wKASO>Oo5QSPXRezJKJv7j231_@IBxDlREAes?E=Zv}EJ6PiI#KD(30JeF$TSWR9Ka zKN#)mF_>J9@YCeZlHn}s9=7QY#}>h;Y7UI<*PIO(0wSM`f`d~V&RX604NILu&xBkh zEDE8R9)@2^E-?IfI@T!3H0vhFsTRfw2KAu!%juXCs^SI-Y9(-6qjHxnO(S;KRWXHLU~ zc<7v3L;jk7i4kHYx|xYf>D;y!{GbCMwNhk7KQ z7<=Ff=?WLN?1{-W$~WUJ4rf(jn>5+5O@?K~TD(>R{Th3xjY2YNITV7?4zCZ@)!H(w z*)J`;{TQ8^{Ds(BC0?lFlM!&*^GKiqQ)C9f-)i$H2>2Q<1mA+KH_x>R>#Bb@uo|T( z^9|!P_`{sVi;8ebpqT7 z>=!91z;A&I1A^4;2ccgAo5r6K()@-8Kd8D7oPc$(A#HCn#_QSjZ#!#j`~iLw|9rBf zTtdY-8UyP@ZkNR+tbD6vhSd+HWI-$>V@_6%D?gy`#YZw0w&0gzOCVgHtb=4oQaG%B z(`GQi?}V_O0e|7}qDm1C1crzL=@+N)W9_cvpU<6p>;1#U0$Xi6CeDj;DDAqoYsW{6 sQWtW#xSo#=cHaD7mv`p}2lG#FeQGvXhijX{&qGi_nvb2R(_0+JmR=T@ZxzMWk=x z&ByR1#BEXAP72Ox_>w<=l9|u`SO4W{2SE&xN8bRh094*y-#1Ix0690*B;T9{ngHXw zt>=|xfqZ?twv3ODwm;TZ$&PpTJEgh<8^h$p_i;46fVhMeOTgu$!%CKf5oD*bk=cZK zUaSk5FsOQjMs?%>f3!JQF&|a;GGW~$Ij1ZZJpnab@tJv+xAFVaP$)mGx2{}5p3*e!_ADM-^t}#{K-YkUE~ZTFxb<(!MbvIb W55w!h@NsZ+8%?x6Dwj{r(&!uQDXujD delta 809 zcmaiyy-Gtt5QR~+xoA>|*j~^OlOW8@?##}H5QyR0sZAiDh=^!u5d+$#7X;yUzJ=s1 z?0gJgLc9rrxKp^*^39nuXMeuNpW~OO3{ikUyqvwgzHij<0A&Q!1?g63{;>JHlCW~8 z-cHbbRR36Cbvkcu54Ji^DTl?%@A7Q9f#`iOjMbIpesczCh144d(!i-MM06>OL5+?v z8b1&4(_8zE$xfhjEnSQ1LwVlXn*|kP7b6+Q%;^EKI=YF+5;2Kf=`>})x}0(R=N37N zF;s|lFmg0Q$VnO6yE9M%O{$=dEc2MUfHHa0D=3BqvPr8UIh-s$#P5x3^)u4u2qr#6 z6sx1)%j-_>lBo#di=nSxfrbNyFc?zMB4UAu?23Ng`&V+2_50cV^lCDFoZQ@I2&9rV J8pY{R_6;=4rpf>S diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index d903f7615bdc79ddef4c5c085ac9cc6fe0d8ac6a..17fcc2f259d9cc0ac2f36a0fe9461ae9477114ac 100644 GIT binary patch delta 1610 zcmah}O>0v@6s280BNZwZTm>O5vWR` zSCWmtK?Fe;#ed_=Q)n_bU{){Zotb;iJ@?%Y=Nq5TH{QL!LXc23GTj2(1K9iW>1SuV z`I8=^_BHm~7kz;F+2ywz>t5uYt^IZQ{AlaVrOklz@7I6d-{;hY#jpFaTeS})vIf(r z`Nz}UI!ZwYG7TOxcP$Zf!6S(=78ryOsKf*WTI|7v&W1?nh=hNMOT5NTSr|kU#=@Hs;(ZV|AxO!u)u$yJNnvsZjnn>D4Bsa|CdrkPNI4kAPN|MBr3PLr`Z38MsX* zADnb*2G#O%jZn6gB%u?_aXCe35Xx{!8CMpqZ8T5#_I^+qL?ShcqN-n}j{4yLbH*%CQ(Jfr5_h|>V%Ug2GJBqER(h0Cg}=l zqR=HXQ~P^z?J`uqfwyF6?@re*m5>iorrf@n4(_L%oLmGnFAagslOt3`{#yoYFYFIy zYS~*M$;HnJnZj6+S^b=lCHt8mThyclrD{6g-5pG#)PC6?Tr~0-k(AW_s)Fktc!Q*% zi)O36Qc{PJRvQk(RM49sCG`B5K^6m;bxCSr4oPKq%Bwi6s$GLGSK3@_H7fw<)sy+8~Ygc!)j4kQ5 xz%3!Mk|>wa*hbqpy3J!5_P739@~9aOo9Bm*4-Q`(Or{Oh^G?lRFuu3b`~kY2G=~5H diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index b991154fcc648354f9118c28844c29cb350c1df9..85b0b74aec2bc421dabb76096a57bfd6598504d4 100644 GIT binary patch delta 1123 zcmah{y=qiJ6lSf$MM*&6qJ=D&%R(S<&Y3xXi)^vGZY9{*2#N^NMi9Ka5xW%|D^Zvx zg+C;J+fK7dbD%x_sYZ&i8ZD>EPsa@b!3yz>!%4?t_g0MnAs)zJ8PT z!5<@!S;yzZ7+`aBq_m?6e= z@OHE=86Z!`-#j{gXLXsfk#1Poi35R$sGtEnLCQM6LfxQn8Ahl)O9y z(gNf$9g0MCz1H!Zx zf{mrJRZ9PfzaU*oA+sVv?i9Xh9_P$E=YE{`?P8EG2A{q(hztZA^vRF!ziw^fKJp`= zG4QxRtFO1u1`f+dTe}WgecZaUaf4y?dANGKOE`k{GkQI0{=AS_5|1fZ$nW3WTasgd zlw*yVS3nKK?jC754uhmsMGztW)!Y16CL~kK+r8ctP5FM`4i(V6ni=E|{mwQ5h;I~8 z(eiou(zGf!h)KR&bDW$gCPB(LD~<||BTZoW)$HyPl|*Z{c47$Lq0F>)Wn|wTsl?zs zkOLHKd~K)5F;p#T!m4~Ilg=mec1Tzig-nu93s(Z-OeusE^UuO1@*AZD8uRb9W~w-j zBH0%09>+>k>m-n>X*#qqkO{O6w4&Cu|%0W^Gp)&|ahvoO4j1K;L2;52wu)>bvBF diff --git a/src/c/configure.ac b/src/c/configure.ac index 4e1a8d5c22b..1afed5a02f7 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.0,[zookeeper-user@hadoop.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.1,[zookeeper-user@hadoop.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 789653f0c16..16ed56870dc 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 0 +#define ZOO_PATCH_VERSION 1 #ifdef __cplusplus } diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index 886318265df..52211521390 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -18,7 +18,7 @@
    - ZooKeeper 3.4.0 Release Notes + ZooKeeper 3.4.1 Release Notes @@ -43,8 +43,89 @@ These release notes include new developer and user facing incompatibilities, fea Changes -
    +Changes Since ZooKeeper 3.4.0 + +
    +Changes Since ZooKeeper 3.4.0 + + + + + Issue + Notes + + + + + + + ZOOKEEPER-1311 + + +ZooKeeper test jar is broken + + + + + + ZOOKEEPER-1305 + + +zookeeper.c:prepend_string func can dereference null ptr + + + + + + ZOOKEEPER-1316 + + +zookeeper_init leaks memory if chroot is just '/' + + + + + + ZOOKEEPER-1315 + + + zookeeper_init always reports sessionPasswd=hidden + + + + + + ZOOKEEPER-1317 + + + Possible segfault in zookeeper_init. + + + + + + ZOOKEEPER-1319 + + + Missing data after restarting+expanding a cluster. + + + + + + ZOOKEEPER-1269 + + + Multi deserialization issues. + + + + +
    + + +
    Changes Since ZooKeeper 3.3.0 From 49d44d241111dbe0fd1c672db739aec0434eb6a6 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 12 Dec 2011 19:14:03 +0000 Subject: [PATCH 036/444] Fixing the rc date for 3.4.1 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1213372 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index e70a7a7fd05..15f9598f752 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Release 3.4.1 - TBD +Release 3.4.1 - 2011-12-12 Backward compatible changes: From 5309ab461cc817ad878b6d6379d00b39c8365945 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 14 Dec 2011 23:16:46 +0000 Subject: [PATCH 037/444] ZOOKEEPER-1323. c client doesn't compile on freebsd (michi mutsuzaki via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1214538 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 10 ++++++++++ src/c/src/zookeeper.c | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 15f9598f752..21b32e2aa5f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +Release 3.4.2 - TBD + +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-1323. c client doesn't compile on freebsd + (michi mutsuzaki via phunt) + + Release 3.4.1 - 2011-12-12 Backward compatible changes: diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index bd2d980e726..f117b18b83e 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -442,7 +442,8 @@ static void setup_random() static int getaddrinfo_errno(int rc) { switch(rc) { case EAI_NONAME: -#if EAI_NODATA != EAI_NONAME +// ZOOKEEPER-1323 EAI_NODATA and EAI_ADDRFAMILY are deprecated in FreeBSD. +#if defined EAI_NODATA && EAI_NODATA != EAI_NONAME case EAI_NODATA: #endif return ENOENT; @@ -578,7 +579,12 @@ int getaddrs(zhandle_t *zh) // ai_flags as AI_ADDRCONFIG #ifdef AI_ADDRCONFIG if ((hints.ai_flags == AI_ADDRCONFIG) && +// ZOOKEEPER-1323 EAI_NODATA and EAI_ADDRFAMILY are deprecated in FreeBSD. +#ifdef EAI_ADDRFAMILY ((rc ==EAI_BADFLAGS) || (rc == EAI_ADDRFAMILY))) { +#else + (rc == EAI_BADFLAGS)) { +#endif //reset ai_flags to null hints.ai_flags = 0; //retry getaddrinfo From 779f15dc05487b02ffe56fe2f2c9d1f0e5a977b4 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 21 Dec 2011 18:39:09 +0000 Subject: [PATCH 038/444] ZOOKEEPER-1333. NPE in FileTxnSnapLog when restarting a cluster. (Patrick Hunt and Andrew Mc Nair via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1221837 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 +- .../org/apache/zookeeper/server/DataTree.java | 25 ++--- .../server/persistence/FileTxnSnapLog.java | 23 ++-- .../zookeeper/test/LoadFromLogTest.java | 106 ++++++++++++++---- 4 files changed, 112 insertions(+), 46 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 21b32e2aa5f..8787eb142f0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Release 3.4.2 - TBD +Release 3.4.2 - 2011-12-21 Backward compatible changes: @@ -7,6 +7,8 @@ BUGFIXES: ZOOKEEPER-1323. c client doesn't compile on freebsd (michi mutsuzaki via phunt) + ZOOKEEPER-1333. NPE in FileTxnSnapLog when restarting a cluster. + (Patrick Hunt and Andrew Mc Nair via mahadev) Release 3.4.1 - 2011-12-12 diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index ee10a4d29ac..cfe14989cda 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.PrintWriter; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -30,21 +31,17 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.nio.ByteBuffer; - import org.apache.jute.Index; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.KeeperException.Code; +import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.Watcher.Event; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; @@ -54,20 +51,17 @@ import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.data.StatPersisted; +import org.apache.zookeeper.txn.CheckVersionTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.ErrorTxn; +import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.SetACLTxn; import org.apache.zookeeper.txn.SetDataTxn; -import org.apache.zookeeper.txn.CheckVersionTxn; import org.apache.zookeeper.txn.Txn; -import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.TxnHeader; - -import org.apache.zookeeper.proto.CreateRequest; -import org.apache.zookeeper.proto.DeleteRequest; -import org.apache.zookeeper.proto.SetACLRequest; -import org.apache.zookeeper.proto.SetDataRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class maintains the tree data structure. It doesn't have any networking @@ -815,6 +809,7 @@ public ProcessTxnResult processTxn(TxnHeader header, Record txn) debug = "Set data transaction for " + setDataTxn.getPath() + " to new value=" + Arrays.toString(setDataTxn.getData()); + rc.path = setDataTxn.getPath(); rc.stat = setData(setDataTxn.getPath(), setDataTxn .getData(), setDataTxn.getVersion(), header .getZxid(), header.getTime()); @@ -823,6 +818,7 @@ public ProcessTxnResult processTxn(TxnHeader header, Record txn) SetACLTxn setACLTxn = (SetACLTxn) txn; debug = "Set ACL transaction for " + setACLTxn.getPath(); + rc.path = setACLTxn.getPath(); rc.stat = setACL(setACLTxn.getPath(), setACLTxn.getAcl(), setACLTxn.getVersion()); break; @@ -928,7 +924,8 @@ record = new ErrorTxn(ec); if (rc.zxid > lastProcessedZxid) { lastProcessedZxid = rc.zxid; } - /** + + /* * Snapshots are taken lazily. It can happen that the child * znodes of a parent are created after the parent * is serialized. Therefore, while replaying logs during restore, a diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index eb4297d6e77..4fc2cf0decb 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -34,7 +34,6 @@ import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.txn.CreateSessionTxn; -import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -150,7 +149,7 @@ public long restore(DataTree dt, Map sessions, processTransaction(hdr,dt,sessions, itr.getTxn()); } catch(KeeperException.NoNodeException e) { throw new IOException("Failed to process transaction type: " + - hdr.getType() + " error: " + e.getMessage()); + hdr.getType() + " error: " + e.getMessage(), e); } listener.onTxnLoaded(hdr, itr.getTxn()); if (!itr.next()) @@ -197,20 +196,22 @@ public void processTransaction(TxnHeader hdr,DataTree dt, rc = dt.processTxn(hdr, txn); } - - if(rc.err != Code.OK.intValue()) { - if(rc.err == Code.NONODE.intValue()) { + /** + * This should never happen. A NONODE can never show up in the + * transaction logs. This is more indicative of a corrupt transaction + * log. Refer ZOOKEEPER-1333 for more info. + */ + if (rc.err != Code.OK.intValue()) { + if (hdr.getType() == OpCode.create && rc.err == Code.NONODE.intValue()) { int lastSlash = rc.path.lastIndexOf('/'); String parentName = rc.path.substring(0, lastSlash); - LOG.error("Failed to set parent cversion for: " + - parentName); - throw new KeeperException.NoNodeException(parentName); - } - else { + LOG.error("Failed to set parent cversion for {}", parentName); + throw new KeeperException.NoNodeException(parentName); + } else { LOG.debug("Ignoring processTxn failure hdr: " + hdr.getType() + " : error: " + rc.err); } - } + } } /** diff --git a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java index 2e78193895f..aefc0eb8e7c 100644 --- a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java +++ b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java @@ -20,42 +20,41 @@ import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.MultiTransactionRecord; +import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; -import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.DataNode; +import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.persistence.FileHeader; import org.apache.zookeeper.server.persistence.FileTxnLog; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; -import org.apache.zookeeper.txn.TxnHeader; -import org.junit.Assert; -import org.junit.Test; -import org.apache.zookeeper.server.DataTree; -import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.Txn; -import org.apache.zookeeper.ZooDefs.OpCode; -import org.apache.jute.BinaryOutputArchive; -import org.apache.jute.Record; -import java.io.FileInputStream; -import java.nio.ByteBuffer; - -import org.apache.jute.BinaryInputArchive; -import org.apache.zookeeper.server.persistence.FileHeader; +import org.apache.zookeeper.txn.TxnHeader; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LoadFromLogTest extends ZKTestCase implements Watcher { private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); @@ -100,8 +99,8 @@ public void testLoad() throws Exception { ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); // now verify that the FileTxnLog reads every transaction only once - File logDir = new File(tmpDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); - FileTxnLog txnLog = new FileTxnLog(logDir); + File logDir = new File(tmpDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); + FileTxnLog txnLog = new FileTxnLog(logDir); TxnIterator itr = txnLog.read(0); long expectedZxid = 0; @@ -368,4 +367,71 @@ public void testRestore() throws Exception { (children.length == NUM_MESSAGES)); f.shutdown(); } + + /** + * Test we can restore a snapshot that has errors and data ahead of the zxid + * of the snapshot file. + */ + @Test + public void testRestoreWithTransactionErrors() throws Exception { + // setup a single server cluster + File tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(10000); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", ClientBase + .waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); + ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); + + long start = System.currentTimeMillis(); + while (!connected) { + long end = System.currentTimeMillis(); + if (end - start > 5000) { + Assert.assertTrue("Could not connect with server in 5 seconds", + false); + } + try { + Thread.sleep(200); + } catch (Exception e) { + LOG.warn("Intrrupted"); + } + + } + // generate some transactions + try { + for (int i = 0; i < NUM_MESSAGES; i++) { + try { + zk.create("/invaliddir/test-", new byte[0], + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); + } catch(NoNodeException e) { + //Expected + } + } + } finally { + zk.close(); + } + + // force the zxid to be behind the content + zks.getZKDatabase().setlastProcessedZxid( + zks.getZKDatabase().getDataTreeLastProcessedZxid() - 10); + LOG.info("Set lastProcessedZxid to " + + zks.getZKDatabase().getDataTreeLastProcessedZxid()); + + // Force snapshot and restore + zks.takeSnapshot(); + zks.shutdown(); + f.shutdown(); + + zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(10000); + f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", ClientBase + .waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); + + f.shutdown(); + } } \ No newline at end of file From ddab2eea198eb098bc419108cec6efeb4ad6d9df Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 21 Dec 2011 20:17:31 +0000 Subject: [PATCH 039/444] ZOOKEEPER-1333. Fixing the logging - Patrick Hunt via mahadev git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1221865 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/zookeeper/server/persistence/FileTxnSnapLog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index 4fc2cf0decb..3e516c71af9 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -205,7 +205,7 @@ public void processTransaction(TxnHeader hdr,DataTree dt, if (hdr.getType() == OpCode.create && rc.err == Code.NONODE.intValue()) { int lastSlash = rc.path.lastIndexOf('/'); String parentName = rc.path.substring(0, lastSlash); - LOG.error("Failed to set parent cversion for {}", parentName); + LOG.error("Parent {} missing for {}", parentName, rc.path); throw new KeeperException.NoNodeException(parentName); } else { LOG.debug("Ignoring processTxn failure hdr: " + hdr.getType() + From 97979b7f150b137aedecb2d3560f87ec545d8912 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 21 Dec 2011 20:43:25 +0000 Subject: [PATCH 040/444] Preparing for release 3.4.2 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1221869 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17122 -> 17122 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13496 -> 13496 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12445 -> 12445 bytes docs/recipes.pdf | Bin 31043 -> 31043 bytes docs/releasenotes.html | 56 ++++++++++++++++-- docs/releasenotes.pdf | Bin 86699 -> 88064 bytes docs/zookeeperAdmin.pdf | Bin 72898 -> 72898 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133759 -> 133759 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27563 -> 27563 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes src/c/configure.ac | 2 +- src/c/include/zookeeper_version.h | 2 +- .../content/xdocs/releasenotes.xml | 40 ++++++++++++- 25 files changed, 93 insertions(+), 9 deletions(-) diff --git a/build.xml b/build.xml index 2cfeaaf1e9c..dbb40c0edab 100644 --- a/build.xml +++ b/build.xml @@ -24,7 +24,7 @@ - + diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 5b4ed2c36236319e996e78efa893be4950b7f64b..97512715c9aa7c447e0cf99d0aafb3d2049d0e0a 100644 GIT binary patch delta 165 zcmaE#{XTob6b>UpLnC8z1B;Dw?(lFM8HN}dSs9yK8CYy?=AFujEHGJ_-w2t%If36m zE#A@D)YRF{#mvOg(Za&O%+=W3#lYFX*ww(?#Ms%u(cDhKhM6*?(lFMScVvyTA7+#8Ch;_=AFujEHGJ_-w2t%If36m zE#Ae@*vZ_$$koKn$k@cy(ap`p!oti6XrPg$k&~;1xt)RyK_#(Vc6MCFC5c5P6-B9O RT!uzwmPTBvs;>TSTmX-oDo_9b diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index 37f1a8cf9c599b8bb3bc8c43ee1a1928c4106094..0231dd1d2909bbc35e26edd8f23e6bdcff470ffb 100644 GIT binary patch delta 148 zcmey?!1=9#b3!}2k)ffHvAN;Kz6(5DMus7VMpnk=R)(81c&9NU@h5Ze8zXU>UHRKx z`5Cvn@-tmDaCUREv~V^zHFmZ%uyk^DH8wLfb#!xfGjwruay76tH?&i*A*5t_qyv*I E0IeS-y#N3J delta 148 zcmey?!1=9#b3!}2fu*6Tsk!mSz6(5D29_a)rdForR>qq%c&9NU@h5Ze8zXU>UHRKx z`5Cvn@-tmDa5gbDaWXV^GqkicF}JXAF?X~uu{3hBa5FIhGMp@2>=bMWDVZMWz$6O* DqiZEO diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 8f2037134176b63b1c9a0d1d1d7848ca52075861..fcfab37f25f79cff8e9d3d563f01e64ce55999c0 100644 GIT binary patch delta 139 zcmZoY%-DLEal#aKBSS+YV{^lev+we785xEc8d({eTN!R{;+@8b#GkChZ;Zs<9M69| v(AmVz(#h1>$i>9f*}%fZ)Y#F?)zHz=z{T0X&BV#f(Zx=|hLDoUnTaw0X;UOj delta 139 zcmZoY%-DLEal#aK14~0wQ*+~uv+we78CZrGnp&BfTN!U|;+@8b#GkChZ;Zs<9M69| v(AmY%*}~b<*wVn%#l^zR(!#*h$i>mz$i&&)%-F!q(#TH1hLDoUnTaw0X1XK7 diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index 51374962401f7f0b805bc2697e16a070f58f2ac8..8552aeada2c328cbc73857b62ade853e026de58d 100644 GIT binary patch delta 167 zcmaFV%J`_2al#Z1BSS+YV{=2JjdSksa2pwh7#dj_n_C$gZEohB%7`p5S()DmnZG%K zU)ea`($Lw`&D_w!)Y-z-#njT!)y>(|)YZ(%+1bp^(b3G(PQiwtl2|S~JFeoA#G;al UqSQ1lLsJVQb1qd?SARDy0GqlhrvLx| delta 167 zcmaFV%J`_2al#Z114~0wQ*&eEjdSksa2r^L7@As{np+tgZ*JzD%7`p5S()DmnZG%K zU)ea`*xb?5(bdh&&CtNi%*5Q)(9p%r$j!vb$;{2f%+%7{PQiwtl2|S~JFeoA#G;al UqSQ1lLsJVQb1qd?SARDy0H4e%X8-^I diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index e43340b8a97ff5fb6ecb2d5f60c4df2a1364ac21..8e243d3ad5583efde55f2cd97b72cc9b6a087a47 100644 GIT binary patch delta 164 zcmbP`J|TTVFNcw#p^>q9K`=X zInK?=*~rzx!qC{o(8$Tn*}&1&!qwc=(8bcp*v!z$(oVs~kdTsCE;~D};*!Lol8U0# SG%iCUBLh<|RaIAiH!cA8P${AS delta 164 zcmbP`J|TTVFNcApp{c34k@?1{S9!P%EJF-UtxV0WjLbKe@=j$$7MLu`Z-mU>9K`=X zInKhx$-vFk+0xa}(!|Nd(bCk_!qmjj($vDx)y2io%ud0^kdTsCE;~D};*!Lol8U0# SG%iCUBLh<|RaIAiH!c7cxGCTO diff --git a/docs/index.pdf b/docs/index.pdf index 4d3995e8b891d88279d073aa8f365672cf4d2114..d546053b23412ecb77ebade6c486cdbe4a3007bb 100644 GIT binary patch delta 137 zcmdmyxg&GJ26iJuLnC8zgN@sN@^Bd$h8P-I8Jk-fY~IB?jS-1IIf~yHiMx3^{~;A; uLrY_4GdD{&Q&V$8XA4UgCreXHLjwZ?7fUw_XG3RGI|Um;N+!=SkpTdF5F|7J delta 137 zcmdmyxg&GJ26h8WLsL_8qmA2t@^Begh8UV!nVMS}ZQjK@jS-1IIf~yHiMx3^{~;A; u12Z!xH#bWYOG7793rkZ|R~Hv^Q)44HLpN6=Hxo-|I|Um;N+!=SkpTdjc_gv` diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index 7ac0a98b9a6fe800e221ef6720b766b1b2aa91d7..aa8812de7ba788f7d242049f44bf323dcc1c1694 100644 GIT binary patch delta 139 zcmeC`VCw2%n$X2=WN2t)Y;L%5;$=kgjgh#Uz4;SL voSj@P98C<23=B=pT^-#_3>}S}-OODq-Ha@p-3(k!T=kgjgh#Uz4;SL voShBK4Nc7~P0Y*;-CT`~olH#)OiWD;oz0C6%p4tE&FvIy2q~HTu2}{E=i4I} diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index 1d1d4af05a91300841194a14bb1f11165fc6700a..fd1f3db2728bb2c05e7628b521ad388e5336ce99 100644 GIT binary patch delta 137 zcmbQ6I5%;^Bz7Z1LnC8z!;Ld<^Kcm%h8P-I8Jk-fZm#2<#)!n9EXQw*#N8at|5(A< t(cIO|)ZEF<*}%}k)WX%x#n{Zn(8<``(a6Nq+{oP3PQiwdlF5w*G5`bxBPsv@ delta 137 zcmbQ6I5%;^Bz6N!LsL_8zPXWiDkHMMWJP`>Wd7zj z{`Sl`O9KldOG9S^0|Rp-7e@nQ6Eg!7BPT;=b0=d*Qwujc1sgL$N@BU}?6`_c5{pVI Uic-_Kj4X|fEVxuvUH#p-0LAhuZ~y=R delta 166 zcmX^7iSh6!#tD--3@i;zP0fugHqO4y!);(0VrXh*YHnp@vAL0VDkHMMWJP`>Wd7zj z{`Sl`GgBu=H&Yigb3;M1& diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 395b7b81913..7c5367e56fd 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -5,7 +5,7 @@ -ZooKeeper 3.4.1 Release Notes +ZooKeeper 3.4.2 Release Notes @@ -202,12 +202,15 @@ PDF -icon
    PDF
    -

    ZooKeeper 3.4.1 Release Notes

    +

    ZooKeeper 3.4.2 Release Notes

    - +

    Changes Since ZooKeeper 3.4.1

    +
    +
    + + +Changes Since ZooKeeper 3.4.1 + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.1
    IssueNotes
    + + ZOOKEEPER-1333 + +NPE in FileTxnSnapLog when restarting a cluster. +
    + + ZOOKEEPER-1323 + +c client doesn't compile on freebsd +
    +
    + + +

    Changes Since ZooKeeper 3.4.0

    diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 882a2068deccfdcd0c119f160335caf8e6eca567..0f3dfa6cafdfd00515cf2cf28fcfd99ccba6982d 100644 GIT binary patch literal 88064 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 diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index 561a27b8356e0c2ed3ac07803ff61a29994fc2b1..b5c2b594aaffd99072034d49104ce492b3ed24a1 100644 GIT binary patch delta 171 zcmX@KljYD(mI;$Nj0_EpjLi)VHqO4y!);_3VrXP#Y;I*}u(^?UDkHMMWJP`>WPWoT z|Mob3MpoWWPWoT z|Mob3MpoW<7Z-C2V;3i9CnqNtQ!{4^7jq*^17lY+XBQVYS0Kku!G@rcSS~v|uHur! XqLPZD)HE(L3u6mIE>%@me>W}wbU7>R diff --git a/docs/zookeeperHierarchicalQuorums.pdf b/docs/zookeeperHierarchicalQuorums.pdf index 36f4c55d6eced29ce1e9951b0ed8dd21532fabf7..33cb748f6536c5c30c11262c1c6a5b90db867df8 100644 GIT binary patch delta 160 zcmexw{NH%O40a%Y+-ES;^Ja#VrJ%SVrXRKrq#)!n9ti^AP#NC|EZ^aeo z>}KHRX5r}SWM*z`XldzUW?*P(qqy863u0RY1#DTn|7 diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index e8b38021172b877f6240e87837c2dd5bd6768875..df2b48abef4264818af40b3cd01f5565a0abb941 100644 GIT binary patch delta 167 zcmX@~m+8=7rU~sFMuvt)#^#2`8~e}ma2pwh7#dj_n_C$gZ_ebM%7`p5nUmiLnZMbM zUv_=Gg@u!uo3o*dp`(G5tD%Ldvx%FTp_!|RnUkZ56A;@e*br0_%VlTBRa}x-R8mot Tn#N^fW^QW9rK;-c@5TiH^2RF~ delta 167 zcmX@~m+8=7rU~sF29}1Vrsl>b8~e}ma2r^L7@As{np+v0Y|iAJ%7`p5nUmiLnZMbM zUv_=GtBHZJp{0?Nsk5`Ovx}L9p{0|hqm!YLg@J{uxv7zboq`QPC9zy~c3j0JiA5z9 UMX70ACT8ZQmRzc;uKsRZ0PtTb%m4rY diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf index 465f7a384cf7087e606739cb4bf9c49343cfad32..c23fa1477baf97f0f41911266f4c86e03d6bc921 100644 GIT binary patch delta 163 zcmaFV!1$1u50E>kWY-wt2YG`b3VQQyfLr_U9mz^C~aYp^>qtO5hK8Fnd8aZW3ryzZH$vubcH>{7 v{7 vgs50ZfNFYVc_Is;AmlJ?&54_Vd(7WW@c#UYHX)qLqN%79V1x)U<4yG diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index f51b668450270576f88a31e03f991a4554003345..9fa1f37d072fb9fa9185e22c49352c472609236f 100644 GIT binary patch delta 186 zcmbQYTWH>Hp$WC@Muvt)#^#0_TaNK?85xEc8d({eTN!Q+;hn~a#Gm|y&lri@Y{=hk z$j=DGOhC-M-H@L}tYLa+KZ^{9iGsPYf`LNNbjN-c5f)PgbIa|`{VcnAoy`qgEX)mE ojh);qTpW!|P0U@~ES+3TO`V*Kjm*u=OzaeF2q~G~IG05h00+r0TL1t6 delta 195 zcmbQYTWH>Hp$WC@29}1Vrsl>QTaNK?8CZrGnp&BfTN!T-;hn~a#Gm|y&lri@Y{=hk z$j=DGOhC-M-H@L}tbxNw!N^d-Kp|+lV?T?Mkg0;XC5XqRprG%WYocIoyuG=fWjC+0 vp{uitn}M6Lvy-EXp@ox)vx$YXfvcOVi?N}FiKVlPoq`P^CDR+{vd97ehPg3P diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index 1e8257279140ecf21e85aa273e3f9c6655a1ef85..87ca3910bacf376f30d9ea9a810d2e10d5ec0ae6 100644 GIT binary patch delta 171 zcmexAh2#GejtLVuj0_EpjLi*=HqN-g!);_3VrXP#Y;I*}w7HshDkHMMWNCgQWPWo5 z|Mm!e#uLBdUCrGrEu9Sv%v{YZ%uQV^EiIglEnSSwjh&s%&E1Uc6l^RBDT(E>v*Ri* aNh~UxQa^> Yi%KerQq#B$jVz5#47pTQUH#p-01>DyGynhq diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index 3018173dd973688f098a0b06e371d901b8b51bff..ef8af561ec690ca87cdd72323ff9ee6abe414358 100644 GIT binary patch delta 161 zcmewt{x5t&H@lIcp^>qJ)!&T^0LT$3SpWb4 delta 161 zcmewt{x5t&H@ktQp{c34(Z)$vc(@EKLkvx=OwFx~HW%9a Oq}QBFRn^tsjSB#})+o~e diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 17fcc2f259d9cc0ac2f36a0fe9461ae9477114ac..1ab66ef341961de0100463d067b5842aea9aac8a 100644 GIT binary patch delta 143 zcmZ2|opJSb#tD--j0_EpjLi)VH_pDz!);_3VrXP#Y;I*}xVe#cDkHMMWJP`>Wd7zj x{&&&Nh8AXq=H@{CmX=0tE>0!}=H>>@rp9KDM$RrSu1-#N3O0n4OzzK?0RR%lB|`uJ delta 143 zcmZ2|opJSb#tD--3@i;zP0fvsHqO4y!);(0VrXh*YHnq0w7HRYDkHMMWJP`>Wd7zj y{&&&N7S2YF7S86*PNpVq7RJuT78aIfuI7fumX?+-madk@b_zCxluYi=mH_}gS|zgp diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index 85b0b74aec2bc421dabb76096a57bfd6598504d4..86fed361504a564c21f85533a8161f51c9f9d186 100644 GIT binary patch delta 143 zcmZ4Sj&a31#t92Kj0_EpjLi)VHm-ch!);_3VrXP#Y;I*}uz4=;R7PZh$+rAP$o$P! y{Ql|Amc~Yw<}QwgmPUr=Zboiq7RIh-M#fIg=5B_TCQgn9b_zCxluYI+mjM7W-z3Wb delta 143 zcmZ4Sj&a31#t92K3@i;zP0fuBH?Dli!);(0VrXh*YHnq0xOpz`R7PZh$+rAP$o$P! y{Ql|ACT^A{PEO7y<_4A~25u%!7B0r7#xADLmQKzV&Td9#b_zCxluYI+mjM7;A|+`6 diff --git a/src/c/configure.ac b/src/c/configure.ac index 1afed5a02f7..94ff7c6e89c 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.1,[zookeeper-user@hadoop.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.2,[zookeeper-user@hadoop.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 16ed56870dc..adb493bb687 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 1 +#define ZOO_PATCH_VERSION 2 #ifdef __cplusplus } diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index 52211521390..fd54fdc6028 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -18,7 +18,7 @@
    - ZooKeeper 3.4.1 Release Notes + ZooKeeper 3.4.2 Release Notes @@ -42,8 +42,44 @@ These release notes include new developer and user facing incompatibilities, fea Changes -
    +Changes Since ZooKeeper 3.4.1 + +
    +Changes Since ZooKeeper 3.4.1 + + + + + Issue + Notes + + + + + + + ZOOKEEPER-1333 + + +NPE in FileTxnSnapLog when restarting a cluster. + + + + + + ZOOKEEPER-1323 + + +c client doesn't compile on freebsd + + + + +
    + + +
    Changes Since ZooKeeper 3.4.0 From afecb3a1af0260c1cacce52d8149d94360569125 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 28 Dec 2011 06:06:10 +0000 Subject: [PATCH 041/444] ZOOKEEPER-1089. zkServer.sh status does not work due to invalid option of nc (Roman Shaposhnik via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1225107 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 10 +++ bin/zkServer.sh | 5 +- .../zookeeper/client/FourLetterWordMain.java | 79 +++++++++++++++++++ .../org/apache/zookeeper/test/ClientBase.java | 39 +-------- .../test/FourLetterWordsQuorumTest.java | 1 + .../zookeeper/test/FourLetterWordsTest.java | 3 +- 6 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java diff --git a/CHANGES.txt b/CHANGES.txt index 8787eb142f0..52825ffdbfa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +Release 3.4.3 - TBD + +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-1089. zkServer.sh status does not work due to invalid + option of nc (Roman Shaposhnik via phunt) + + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/bin/zkServer.sh b/bin/zkServer.sh index ac426ff3bb9..e37bb4ea9df 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -153,7 +153,10 @@ restart) ;; status) # -q is necessary on some versions of linux where nc returns too quickly, and no stat result is output - STAT=`echo stat | nc -q 1 localhost $(grep "^[[:space:]]*clientPort" "$ZOOCFG" | sed -e 's/.*=//') 2> /dev/null| grep Mode` + STAT=`$JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ + -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain localhost \ + $(grep "^[[:space:]]*clientPort" "$ZOOCFG" | sed -e 's/.*=//') srvr 2> /dev/null \ + | grep Mode` if [ "x$STAT" = "x" ] then echo "Error contacting service. It is probably not running." diff --git a/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java b/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java new file mode 100644 index 00000000000..e41465ab93a --- /dev/null +++ b/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.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.client; + +import org.apache.log4j.Logger; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.Socket; + +public class FourLetterWordMain { + protected static final Logger LOG = Logger.getLogger(FourLetterWordMain.class); + + /** + * Send the 4letterword + * @param host the destination host + * @param port the destination port + * @param cmd the 4letterword + * @return server response + * @throws java.io.IOException + */ + public static String send4LetterWord(String host, int port, String cmd) + throws IOException + { + LOG.info("connecting to " + host + " " + port); + Socket sock = new Socket(host, port); + BufferedReader reader = null; + try { + OutputStream outstream = sock.getOutputStream(); + outstream.write(cmd.getBytes()); + outstream.flush(); + // this replicates NC - close the output stream before reading + sock.shutdownOutput(); + + reader = + new BufferedReader( + new InputStreamReader(sock.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line; + while((line = reader.readLine()) != null) { + sb.append(line + "\n"); + } + return sb.toString(); + } finally { + sock.close(); + if (reader != null) { + reader.close(); + } + } + } + + public static void main(String[] args) + throws IOException + { + if (args.length != 3) { + System.out.println("Usage: FourLetterWordMain "); + } else { + System.out.println(send4LetterWord(args[0], Integer.parseInt(args[1]), args[2])); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 84e17bd9750..fb34ca18bff 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -53,6 +53,7 @@ import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnLog; +import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -213,44 +214,6 @@ public static List parseHostPortList(String hplist) { return alist; } - /** - * Send the 4letterword - * @param host the destination host - * @param port the destination port - * @param cmd the 4letterword - * @return - * @throws IOException - */ - public static String send4LetterWord(String host, int port, String cmd) - throws IOException - { - LOG.info("connecting to " + host + " " + port); - Socket sock = new Socket(host, port); - BufferedReader reader = null; - try { - OutputStream outstream = sock.getOutputStream(); - outstream.write(cmd.getBytes()); - outstream.flush(); - // this replicates NC - close the output stream before reading - sock.shutdownOutput(); - - reader = - new BufferedReader( - new InputStreamReader(sock.getInputStream())); - StringBuilder sb = new StringBuilder(); - String line; - while((line = reader.readLine()) != null) { - sb.append(line + "\n"); - } - return sb.toString(); - } finally { - sock.close(); - if (reader != null) { - reader.close(); - } - } - } - public static boolean waitForServerUp(String hp, long timeout) { long start = System.currentTimeMillis(); while (true) { diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java index 6d739942267..d9a6713bede 100644 --- a/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java @@ -23,6 +23,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.TestableZooKeeper; +import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import org.junit.Assert; import org.junit.Test; diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java index 373914af21c..a4e46dbc500 100644 --- a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java @@ -25,6 +25,7 @@ import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooKeeper; +import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; @@ -97,7 +98,7 @@ public void testFourLetterWords() throws Exception { private String sendRequest(String cmd) throws IOException { HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); - return ClientBase.send4LetterWord(hpobj.host, hpobj.port, cmd); + return send4LetterWord(hpobj.host, hpobj.port, cmd); } private void verify(String cmd, String expected) throws IOException { From 0d9b77c62b029a29066a86048d0bdf501b3d5372 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 30 Dec 2011 21:32:21 +0000 Subject: [PATCH 042/444] ZOOKEEPER-1345. Add a .gitignore file with general exclusions and Eclipse project files excluded (Harsh J via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1225928 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 10 ++++++++++ CHANGES.txt | 3 +++ 2 files changed, 13 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..4dbed1a49b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.classpath +.eclipse/ +.project +.revision/ +.settings/ +build/ +src/c/generated/ +src/java/generated/ +src/java/lib/ant-eclipse-* +src/java/lib/ivy-* diff --git a/CHANGES.txt b/CHANGES.txt index 52825ffdbfa..99293091e91 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,9 @@ BUGFIXES: ZOOKEEPER-1089. zkServer.sh status does not work due to invalid option of nc (Roman Shaposhnik via phunt) + ZOOKEEPER-1345. Add a .gitignore file with general exclusions and + Eclipse project files excluded (Harsh J via phunt) + Release 3.4.2 - 2011-12-21 From 641e5bc8b82104e1d452a104ab922880d86f45c7 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 30 Dec 2011 21:36:07 +0000 Subject: [PATCH 043/444] ZOOKEEPER-1345. Add a .gitignore file with general exclusions and Eclipse project files excluded (Harsh J via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1225931 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4dbed1a49b2..a53fdb37778 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,5 @@ .revision/ .settings/ build/ -src/c/generated/ -src/java/generated/ src/java/lib/ant-eclipse-* src/java/lib/ivy-* From a64215dcf784f7dc52fc88d7b05281f47623d31d Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 30 Dec 2011 21:45:56 +0000 Subject: [PATCH 044/444] ZOOKEEPER-1345. Add a .gitignore file with general exclusions and Eclipse project files excluded (Harsh J via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1225935 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a53fdb37778..4dbed1a49b2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,7 @@ .revision/ .settings/ build/ +src/c/generated/ +src/java/generated/ src/java/lib/ant-eclipse-* src/java/lib/ivy-* From bacc7aad8006a26330a3a46d6eff6f587add8694 Mon Sep 17 00:00:00 2001 From: Benjamin Reed Date: Wed, 4 Jan 2012 00:27:21 +0000 Subject: [PATCH 045/444] ZOOKEEPER-1343. getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1227002 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 1 + .../zookeeper/server/quorum/Leader.java | 96 +++++++++---------- .../zookeeper/server/quorum/Zab1_0Test.java | 86 ++++++++++++++++- 3 files changed, 132 insertions(+), 51 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 99293091e91..ec5be929b77 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,7 @@ BUGFIXES: ZOOKEEPER-1345. Add a .gitignore file with general exclusions and Eclipse project files excluded (Harsh J via phunt) + ZOOKEEPER-1343. getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch (fpj via breed) Release 3.4.2 - 2011-12-21 diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 26360f77929..c0be75f752e 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -234,7 +234,7 @@ boolean isLearnerSynced(LearnerHandler peer){ */ final static int INFORM = 8; - ConcurrentMap outstandingProposals = new ConcurrentHashMap(); + ConcurrentMap outstandingProposals = new ConcurrentHashMap(); ConcurrentLinkedQueue toBeApplied = new ConcurrentLinkedQueue(); @@ -410,7 +410,7 @@ void lead() throws IOException, InterruptedException { } } - boolean isShutdown; + boolean isShutdown; /** * Close down all the LearnerHandlers @@ -767,54 +767,54 @@ synchronized public long startForwarding(LearnerHandler handler, } private HashSet connectingFollowers = new HashSet(); - public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException { - synchronized(connectingFollowers) { - if (!waitingForNewEpoch) { - return epoch; - } - if (lastAcceptedEpoch > epoch) { - epoch = lastAcceptedEpoch+1; - } - connectingFollowers.add(sid); - QuorumVerifier verifier = self.getQuorumVerifier(); - if (connectingFollowers.contains(self.getId()) && verifier.containsQuorum(connectingFollowers)) -{ - waitingForNewEpoch = false; - self.setAcceptedEpoch(epoch); - connectingFollowers.notifyAll(); - } else { - long start = System.currentTimeMillis(); - long cur = start; + public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException { + synchronized(connectingFollowers) { + if (!waitingForNewEpoch) { + return epoch; + } + if (lastAcceptedEpoch >= epoch) { + epoch = lastAcceptedEpoch+1; + } + connectingFollowers.add(sid); + QuorumVerifier verifier = self.getQuorumVerifier(); + if (connectingFollowers.contains(self.getId()) && + verifier.containsQuorum(connectingFollowers)) { + waitingForNewEpoch = false; + self.setAcceptedEpoch(epoch); + connectingFollowers.notifyAll(); + } else { + long start = System.currentTimeMillis(); + long cur = start; long end = start + self.getInitLimit()*self.getTickTime(); while(waitingForNewEpoch && cur < end) { connectingFollowers.wait(end - cur); cur = System.currentTimeMillis(); } - if (waitingForNewEpoch) { + if (waitingForNewEpoch) { throw new InterruptedException("Timeout while waiting for epoch from quorum"); - } - } - return epoch; - } - } - - private HashSet electingFollowers = new HashSet(); - private boolean electionFinished = false; - public void waitForEpochAck(long id, StateSummary ss) throws IOException, InterruptedException { - synchronized(electingFollowers) { - if (electionFinished) { - return; - } - if (ss.getCurrentEpoch() != -1) { - if (ss.isMoreRecentThan(leaderStateSummary)) { - throw new IOException("Follower is ahead of the leader"); - } - electingFollowers.add(id); - } - QuorumVerifier verifier = self.getQuorumVerifier(); - if (electingFollowers.contains(self.getId()) && verifier.containsQuorum(electingFollowers)) { - electionFinished = true; - electingFollowers.notifyAll(); + } + } + return epoch; + } + } + + private HashSet electingFollowers = new HashSet(); + private boolean electionFinished = false; + public void waitForEpochAck(long id, StateSummary ss) throws IOException, InterruptedException { + synchronized(electingFollowers) { + if (electionFinished) { + return; + } + if (ss.getCurrentEpoch() != -1) { + if (ss.isMoreRecentThan(leaderStateSummary)) { + throw new IOException("Follower is ahead of the leader"); + } + electingFollowers.add(id); + } + QuorumVerifier verifier = self.getQuorumVerifier(); + if (electingFollowers.contains(self.getId()) && verifier.containsQuorum(electingFollowers)) { + electionFinished = true; + electingFollowers.notifyAll(); } else { long start = System.currentTimeMillis(); long cur = start; @@ -825,8 +825,8 @@ public void waitForEpochAck(long id, StateSummary ss) throws IOException, Interr } if (!electionFinished) { throw new InterruptedException("Timeout while waiting for epoch to be acked by quorum"); - } - } - } - } + } + } + } + } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index e82ee0d4515..6cc68df7609 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -48,6 +48,7 @@ import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.util.ZxidUtils; @@ -75,7 +76,26 @@ public void run() { } } } - + + private static final class MockLeader extends Leader { + + MockLeader(QuorumPeer qp, LeaderZooKeeperServer zk) + throws IOException { + super(qp, zk); + } + + /** + * This method returns the value of the variable that holds the epoch + * to be proposed and that has been proposed, depending on the point + * of the execution in which it is called. + * + * @return epoch + */ + public long getCurrentEpochToPropose() { + return epoch; + } + } + public static final class FollowerMockThread extends Thread { private final Leader leader; private final long followerSid; @@ -144,6 +164,54 @@ public void testLeaderInConnectingFollowers() throws Exception { } } + /** + * In this test, the leader sets the last accepted epoch to 5. The call + * to getEpochToPropose should set epoch to 6 and wait until another + * follower executes it. If in getEpochToPropose we don't check if + * lastAcceptedEpoch == epoch, then the call from the subsequent + * follower with lastAcceptedEpoch = 6 doesn't change the value + * of epoch, and the test fails. It passes with the fix to predicate. + * + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1343} + * + * + * @throws Exception + */ + + @Test + public void testLastAcceptedEpoch() throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + LeadThread leadThread = null; + try { + QuorumPeer peer = createQuorumPeer(tmpDir); + leader = createMockLeader(tmpDir, peer); + peer.leader = leader; + peer.setAcceptedEpoch(5); + leadThread = new LeadThread(leader); + leadThread.start(); + + while(((MockLeader) leader).getCurrentEpochToPropose() != 6){ + Thread.sleep(20); + } + + try { + long epoch = leader.getEpochToPropose(1, 6); + Assert.assertEquals("New proposed epoch is wrong", 7, epoch); + } catch (Exception e){ + Assert.fail("Timed out in getEpochToPropose"); + } + + } finally { + recursiveDelete(tmpDir); + if (leader != null) { + leader.shutdown("end of test"); + } + } + } + @Test public void testLeaderInElectingFollowers() throws Exception { File tmpDir = File.createTempFile("test", "dir"); @@ -632,7 +700,19 @@ private void recursiveDelete(File file) { } private Leader createLeader(File tmpDir, QuorumPeer peer) - throws IOException, NoSuchFieldException, IllegalAccessException { + throws IOException, NoSuchFieldException, IllegalAccessException{ + LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer); + return new Leader(peer, zk); + } + + private Leader createMockLeader(File tmpDir, QuorumPeer peer) + throws IOException, NoSuchFieldException, IllegalAccessException{ + LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer); + return new MockLeader(peer, zk); + } + + private LeaderZooKeeperServer prepareLeader(File tmpDir, QuorumPeer peer) + throws IOException, NoSuchFieldException, IllegalAccessException { FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); peer.setTxnFactory(logFactory); Field addrField = peer.getClass().getDeclaredField("myQuorumAddr"); @@ -640,7 +720,7 @@ private Leader createLeader(File tmpDir, QuorumPeer peer) addrField.set(peer, new InetSocketAddress(33556)); ZKDatabase zkDb = new ZKDatabase(logFactory); LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer, new ZooKeeperServer.BasicDataTreeBuilder(), zkDb); - return new Leader(peer, zk); + return zk; } static class ConversableFollower extends Follower { From c85c3a1790f281f58e57e73a2a2e078ec2b31651 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Mon, 16 Jan 2012 02:24:30 +0000 Subject: [PATCH 046/444] ZOOKEEPER-1358. In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) doesn't sleep by checking that the latency of this call is less than 10sec (Alex Shraer via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1231807 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../test/org/apache/zookeeper/test/StaticHostProviderTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index ec5be929b77..5df4c3732a9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,9 @@ BUGFIXES: Eclipse project files excluded (Harsh J via phunt) ZOOKEEPER-1343. getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch (fpj via breed) + + ZOOKEEPER-1358. In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) + doesn't sleep by checking that the latency of this call is less than 10sec (Alex Shraer via camille) Release 3.4.2 - 2011-12-21 diff --git a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java index fa0128cd24d..4dec9767416 100644 --- a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java +++ b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java @@ -66,7 +66,7 @@ public void testNextDoesNotSleepForZero() throws UnknownHostException { long start = System.currentTimeMillis(); hostProvider.next(0); long stop = System.currentTimeMillis(); - assertTrue(10000 > stop - start); + assertTrue(5 > stop - start); } @Test From 7ac9e2593a943f64898413423250c6c476ac1ad9 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Mon, 16 Jan 2012 02:34:22 +0000 Subject: [PATCH 047/444] ZOOKEEPER-1351. invalid test verification in MultiTransactionTest (phunt via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1231810 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../zookeeper/test/MultiTransactionTest.java | 114 +++++++++++------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5df4c3732a9..e253aea655f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,8 @@ BUGFIXES: ZOOKEEPER-1358. In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) doesn't sleep by checking that the latency of this call is less than 10sec (Alex Shraer via camille) + + ZOOKEEPER-1351. invalid test verification in MultiTransactionTest (phunt via camille) Release 3.4.2 - 2011-12-21 diff --git a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java index fa9c8f3b486..11553980153 100644 --- a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java +++ b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java @@ -17,67 +17,47 @@ package org.apache.zookeeper.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import org.apache.log4j.Logger; -import org.apache.zookeeper.*; +import org.apache.zookeeper.AsyncCallback; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.Op; +import org.apache.zookeeper.OpResult; +import org.apache.zookeeper.OpResult.ErrorResult; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.SyncRequestProcessor; -import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.OpResult.ErrorResult; -import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.ArrayList; - -import org.apache.zookeeper.data.Stat; - -import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; - -public class MultiTransactionTest extends ZKTestCase implements Watcher { +public class MultiTransactionTest extends ClientBase { private static final Logger LOG = Logger.getLogger(MultiTransactionTest.class); - private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); - private ZooKeeper zk; - private ServerCnxnFactory serverFactory; - - @Override - public void process(WatchedEvent event) { - // ignore - } @Before - public void setupZk() throws Exception { - File tmpDir = ClientBase.createTmpDir(); - ClientBase.setupTestEnv(); - ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + public void setUp() throws Exception { SyncRequestProcessor.setSnapCount(150); - final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); - serverFactory = ServerCnxnFactory.createFactory(PORT, -1); - serverFactory.startup(zks); - LOG.info("starting up the zookeeper server .. waiting"); - Assert.assertTrue("waiting for server being up", - ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); - zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - } - - @After - public void shutdownServer() throws Exception { - zk.close(); - serverFactory.shutdown(); + super.setUp(); + zk = createClient(); } @Test public void testCreate() throws Exception { - List results = new ArrayList(); - - results = zk.multi(Arrays.asList( + zk.multi(Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) @@ -225,6 +205,52 @@ public void TestGetResults() throws Exception { } } + @Test + public void testWatchesTriggered() throws KeeperException, InterruptedException { + HasTriggeredWatcher watcher = new HasTriggeredWatcher(); + zk.getChildren("/", watcher); + zk.multi(Arrays.asList( + Op.create("/t", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), + Op.delete("/t", -1) + )); + assertTrue(watcher.triggered.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)); + } + + @Test + public void testNoWatchesTriggeredForFailedMultiRequest() throws InterruptedException, KeeperException { + HasTriggeredWatcher watcher = new HasTriggeredWatcher(); + zk.getChildren("/", watcher); + try { + zk.multi(Arrays.asList( + Op.create("/t", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), + Op.delete("/nonexisting", -1) + )); + fail("expected previous multi op to fail!"); + } catch (KeeperException.NoNodeException e) { + // expected + } + SyncCallback cb = new SyncCallback(); + zk.sync("/", cb, null); + + // by waiting for the callback we're assured that the event queue is flushed + cb.done.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); + assertEquals(1, watcher.triggered.getCount()); + } + + private static class HasTriggeredWatcher implements Watcher { + private final CountDownLatch triggered = new CountDownLatch(1); + @Override + public void process(WatchedEvent event) { + triggered.countDown(); + } + } + private static class SyncCallback implements AsyncCallback.VoidCallback { + private final CountDownLatch done = new CountDownLatch(1); + @Override + public void processResult(int rc, String path, Object ctx) { + done.countDown(); + } + } } From 28a72efde5ee00bccbfe3ebfed388cc921221baa Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 23 Jan 2012 20:33:28 +0000 Subject: [PATCH 048/444] ZOOKEEPER-973. bind() could fail on Leader because it does not setReuseAddress on its ServerSocket (Harsh J via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1234975 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/server/quorum/Leader.java | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index e253aea655f..4734cdcb255 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -17,6 +17,9 @@ BUGFIXES: ZOOKEEPER-1351. invalid test verification in MultiTransactionTest (phunt via camille) + ZOOKEEPER-973. bind() could fail on Leader because it does not + setReuseAddress on its ServerSocket (Harsh J via phunt) + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index c0be75f752e..573ae083d16 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.BindException; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; @@ -132,7 +133,9 @@ boolean isLearnerSynced(LearnerHandler peer){ Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException { this.self = self; try { - ss = new ServerSocket(self.getQuorumAddress().getPort()); + ss = new ServerSocket(); + ss.setReuseAddress(true); + ss.bind(new InetSocketAddress(self.getQuorumAddress().getPort())); } catch (BindException e) { LOG.error("Couldn't bind to port " + self.getQuorumAddress().getPort(), e); From 1b7c4fe9f3fbc4c0a16cc083792878b8938740f3 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Tue, 31 Jan 2012 06:50:06 +0000 Subject: [PATCH 049/444] ZOOKEEPER-1367. Data inconsistencies and unexpired ephemeral nodes after cluster restart. (Bejamin Reed via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1238177 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../server/FinalRequestProcessor.java | 19 +--- .../zookeeper/server/ZooKeeperServer.java | 37 +++++- .../server/quorum/LeaderZooKeeperServer.java | 6 +- .../zookeeper/server/quorum/Learner.java | 7 +- .../server/quorum/LearnerZooKeeperServer.java | 5 +- .../zookeeper/server/quorum/LearnerTest.java | 6 +- .../zookeeper/server/quorum/Zab1_0Test.java | 105 ++++++++++++++++++ 8 files changed, 162 insertions(+), 26 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4734cdcb255..79c19128d25 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -20,6 +20,9 @@ BUGFIXES: ZOOKEEPER-973. bind() could fail on Leader because it does not setReuseAddress on its ServerSocket (Harsh J via phunt) + ZOOKEEPER-1367. Data inconsistencies and unexpired ephemeral nodes after cluster restart. + (Bejamin Reed via mahadev) + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java index ab7e6c223d9..7e6b9ed193b 100644 --- a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java @@ -54,6 +54,7 @@ import org.apache.zookeeper.server.ZooKeeperServer.ChangeRecord; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.ErrorTxn; +import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.MultiTransactionRecord; import org.apache.zookeeper.Op; @@ -109,20 +110,10 @@ public void processRequest(Request request) { } } if (request.hdr != null) { - rc = zks.getZKDatabase().processTxn(request.hdr, request.txn); - if (request.type == OpCode.createSession) { - if (request.txn instanceof CreateSessionTxn) { - CreateSessionTxn cst = (CreateSessionTxn) request.txn; - zks.sessionTracker.addSession(request.sessionId, cst - .getTimeOut()); - } else { - LOG.warn("*****>>>>> Got " - + request.txn.getClass() + " " - + request.txn.toString()); - } - } else if (request.type == OpCode.closeSession) { - zks.sessionTracker.removeSession(request.sessionId); - } + TxnHeader hdr = request.hdr; + Record txn = request.txn; + + rc = zks.processTxn(hdr, txn); } // do not add non quorum packets to the queue. if (Request.isQuorum(request.type)) { diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index 2946030be0e..627e83f8e87 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -62,6 +62,7 @@ import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetSASLResponse; +import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.ServerCnxn.CloseRequestException; import org.apache.zookeeper.server.SessionTracker.Session; import org.apache.zookeeper.server.SessionTracker.SessionExpirer; @@ -69,6 +70,9 @@ import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; +import org.apache.zookeeper.txn.CreateSessionTxn; +import org.apache.zookeeper.txn.TxnHeader; + import org.apache.zookeeper.server.util.ZxidUtils; import javax.security.sasl.SaslException; @@ -111,7 +115,6 @@ public DataTree build() { protected int maxSessionTimeout = -1; protected SessionTracker sessionTracker; private FileTxnSnapLog txnLogFactory = null; - private ConcurrentHashMap sessionsWithTimeouts; private ZKDatabase zkDb; protected long hzxid = 0; public final static Exception ok = new Exception("No prob"); @@ -257,8 +260,7 @@ public void loadData() throws IOException, InterruptedException { // Clean up dead sessions LinkedList deadSessions = new LinkedList(); for (Long session : zkDb.getSessions()) { - sessionsWithTimeouts = zkDb.getSessionWithTimeOuts(); - if (sessionsWithTimeouts.get(session) == null) { + if (zkDb.getSessionWithTimeOuts().get(session) == null) { deadSessions.add(session); } } @@ -386,7 +388,10 @@ public void startdata() } public void startup() { - createSessionTracker(); + if (sessionTracker == null) { + createSessionTracker(); + } + startSessionTracker(); setupRequestProcessors(); registerJMX(); @@ -409,6 +414,9 @@ protected void setupRequestProcessors() { protected void createSessionTracker() { sessionTracker = new SessionTrackerImpl(this, zkDb.getSessionWithTimeOuts(), tickTime, 1); + } + + protected void startSessionTracker() { ((SessionTrackerImpl)sessionTracker).start(); } @@ -948,6 +956,27 @@ private Record processSasl(ByteBuffer incomingBuffer, ServerCnxn cnxn) throws IO // wrap SASL response token to client inside a Response object. return new SetSASLResponse(responseToken); } + + public ProcessTxnResult processTxn(TxnHeader hdr, Record txn) { + ProcessTxnResult rc; + int opCode = hdr.getType(); + long sessionId = hdr.getClientId(); + rc = getZKDatabase().processTxn(hdr, txn); + if (opCode == OpCode.createSession) { + if (txn instanceof CreateSessionTxn) { + CreateSessionTxn cst = (CreateSessionTxn) txn; + sessionTracker.addSession(sessionId, cst + .getTimeOut()); + } else { + LOG.warn("*****>>>>> Got " + + txn.getClass() + " " + + txn.toString()); + } + } else if (opCode == OpCode.closeSession) { + sessionTracker.removeSession(sessionId); + } + return rc; + } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java index 976726e9f79..575f73d41a1 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java @@ -77,9 +77,13 @@ public int getGlobalOutstandingLimit() { } @Override - protected void createSessionTracker() { + public void createSessionTracker() { sessionTracker = new SessionTrackerImpl(this, getZKDatabase().getSessionWithTimeOuts(), tickTime, self.getId()); + } + + @Override + protected void startSessionTracker() { ((SessionTrackerImpl)sessionTracker).start(); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index f4a77314721..e8d548ba153 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -352,7 +352,8 @@ else if (qp.getType() == Leader.SNAP) { } zk.getZKDatabase().setlastProcessedZxid(qp.getZxid()); - + zk.createSessionTracker(); + long lastQueued = 0; // in V1.0 we take a snapshot when we get the NEWLEADER message, but in pre V1.0 @@ -383,7 +384,7 @@ else if (qp.getType() == Leader.SNAP) { if (pif.hdr.getZxid() != qp.getZxid()) { LOG.warn("Committing " + qp.getZxid() + ", but next proposal is " + pif.hdr.getZxid()); } else { - zk.getZKDatabase().processTxn(pif.hdr, pif.rec); + zk.processTxn(pif.hdr, pif.rec); packetsNotCommitted.remove(); } } else { @@ -393,7 +394,7 @@ else if (qp.getType() == Leader.SNAP) { case Leader.INFORM: TxnHeader hdr = new TxnHeader(); Record txn = SerializeUtils.deserializeTxn(qp.getData(), hdr); - zk.getZKDatabase().processTxn(hdr, txn); + zk.processTxn(hdr, txn); break; case Leader.UPTODATE: if (!snapshotTaken) { // true for the pre v1.0 case diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java index 68caa31069d..5dd13320212 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java @@ -70,11 +70,14 @@ public long getServerId() { } @Override - protected void createSessionTracker() { + public void createSessionTracker() { sessionTracker = new LearnerSessionTracker(this, getZKDatabase().getSessionWithTimeOuts(), self.getId()); } + @Override + protected void startSessionTracker() {} + @Override protected void revalidateSession(ServerCnxn cnxn, long sessionId, int sessionTimeout) throws IOException { diff --git a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java index 7419510030a..63b15e3a392 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java @@ -50,8 +50,8 @@ public class LearnerTest extends ZKTestCase { class SimpleLearnerZooKeeperServer extends LearnerZooKeeperServer { boolean startupCalled; - public SimpleLearnerZooKeeperServer(FileTxnSnapLog ftsl) throws IOException { - super(ftsl, 2000, 2000, 2000, null, new ZKDatabase(ftsl), null); + public SimpleLearnerZooKeeperServer(FileTxnSnapLog ftsl, QuorumPeer self) throws IOException { + super(ftsl, 2000, 2000, 2000, null, new ZKDatabase(ftsl), self); } Learner learner; @Override @@ -67,7 +67,7 @@ public void startup() { class SimpleLearner extends Learner { SimpleLearner(FileTxnSnapLog ftsl) throws IOException { self = new QuorumPeer(); - zk = new SimpleLearnerZooKeeperServer(ftsl); + zk = new SimpleLearnerZooKeeperServer(ftsl, self); ((SimpleLearnerZooKeeperServer)zk).learner = this; } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 6cc68df7609..cd078bac747 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -52,6 +52,7 @@ import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.util.ZxidUtils; +import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnHeader; @@ -547,6 +548,110 @@ private void proposeSetData(QuorumPacket qp, long zxid, String data, int version }); } + @Test + public void testNormalFollowerRunWithDiff() throws Exception { + testFollowerConversation(new FollowerConversation() { + @Override + public void converseWithFollower(InputArchive ia, OutputArchive oa, + Follower f) throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile(); + File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); + try { + Assert.assertEquals(0, f.self.getAcceptedEpoch()); + Assert.assertEquals(0, f.self.getCurrentEpoch()); + + // Setup a database with a single /foo node + ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); + final long firstZxid = ZxidUtils.makeZxid(1, 1); + zkDb.processTxn(new TxnHeader(13, 1313, firstZxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1)); + Stat stat = new Stat(); + Assert.assertEquals("data1", new String(zkDb.getData("/foo", stat, null))); + + QuorumPacket qp = new QuorumPacket(); + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.FOLLOWERINFO, qp.getType()); + Assert.assertEquals(qp.getZxid(), 0); + LearnerInfo learnInfo = new LearnerInfo(); + ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo); + Assert.assertEquals(learnInfo.getProtocolVersion(), 0x10000); + Assert.assertEquals(learnInfo.getServerid(), 0); + + // We are simulating an established leader, so the epoch is 1 + qp.setType(Leader.LEADERINFO); + qp.setZxid(ZxidUtils.makeZxid(1, 0)); + byte protoBytes[] = new byte[4]; + ByteBuffer.wrap(protoBytes).putInt(0x10000); + qp.setData(protoBytes); + oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACKEPOCH, qp.getType()); + Assert.assertEquals(0, qp.getZxid()); + Assert.assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer.wrap(qp.getData()).getInt()); + Assert.assertEquals(1, f.self.getAcceptedEpoch()); + Assert.assertEquals(0, f.self.getCurrentEpoch()); + + // Send a diff + qp.setType(Leader.DIFF); + qp.setData(new byte[0]); + qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); + oa.writeRecord(qp, null); + final long createSessionZxid = ZxidUtils.makeZxid(1, 2); + proposeNewSession(qp, createSessionZxid, 0x333); + oa.writeRecord(qp, null); + qp.setType(Leader.COMMIT); + qp.setZxid(createSessionZxid); + oa.writeRecord(qp, null); + qp.setType(Leader.NEWLEADER); + qp.setZxid(ZxidUtils.makeZxid(1, 0)); + oa.writeRecord(qp, null); + qp.setType(Leader.UPTODATE); + qp.setZxid(0); + oa.writeRecord(qp, null); + + // Read the uptodate ack + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + + + // Get the ack of the new leader + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(1, f.self.getAcceptedEpoch()); + Assert.assertEquals(1, f.self.getCurrentEpoch()); + + Assert.assertEquals(createSessionZxid, f.fzk.getLastProcessedZxid()); + + // Make sure the data was recorded in the filesystem ok + ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); + zkDb2.loadDataBase(); + System.out.println(zkDb2.getSessions()); + Assert.assertNotNull(zkDb2.getSessionWithTimeOuts().get(4L)); + } finally { + recursiveDelete(tmpDir); + } + + } + + private void proposeNewSession(QuorumPacket qp, long zxid, long sessionId) throws IOException { + qp.setType(Leader.PROPOSAL); + qp.setZxid(zxid); + TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.createSession); + CreateSessionTxn cst = new CreateSessionTxn(30000); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputArchive boa = BinaryOutputArchive.getArchive(baos); + boa.writeRecord(hdr, null); + boa.writeRecord(cst, null); + qp.setData(baos.toByteArray()); + } + }); + } + @Test public void testNormalRun() throws Exception { testLeaderConversation(new LeaderConversation() { From 500e7b4943c138c4665b026aad542d4ba7d0161c Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 6 Feb 2012 07:58:35 +0000 Subject: [PATCH 050/444] ZOOKEEPER-1353. C client test suite fails consistently. (Clint Byrum via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1240910 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/tests/TestZookeeperInit.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 79c19128d25..e4dcecd4a30 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -23,6 +23,9 @@ BUGFIXES: ZOOKEEPER-1367. Data inconsistencies and unexpired ephemeral nodes after cluster restart. (Bejamin Reed via mahadev) + ZOOKEEPER-1353. C client test suite fails consistently. + (Clint Byrum via mahadev) + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/src/c/tests/TestZookeeperInit.cc b/src/c/tests/TestZookeeperInit.cc index ca55f3c7b26..eadf41d2d3c 100644 --- a/src/c/tests/TestZookeeperInit.cc +++ b/src/c/tests/TestZookeeperInit.cc @@ -230,7 +230,7 @@ class Zookeeper_init : public CPPUNIT_NS::TestFixture const string INVALID_HOST("host1:1111+host:123"); zh=zookeeper_init(INVALID_HOST.c_str(),0,0,0,0,0); CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(ENOENT,errno); + CPPUNIT_ASSERT((ENOENT|EINVAL) & errno); } void testNonexistentHost() { From 7e60b96abb558b77cee78d78442952ec1506638a Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 6 Feb 2012 08:36:36 +0000 Subject: [PATCH 051/444] ZOOKEEPER-1373. Hardcoded SASL login context name clashes with Hadoop security configuration override. (Eugene Koontz and Thomas Weise via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1240919 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../main/org/apache/zookeeper/ClientCnxn.java | 26 ++-- src/java/main/org/apache/zookeeper/Login.java | 7 +- .../zookeeper/client/ZooKeeperSaslClient.java | 118 +++++++++++++++--- .../test/SaslAuthDesignatedClientTest.java | 99 +++++++++++++++ .../SaslAuthFailDesignatedClientTest.java | 100 +++++++++++++++ .../test/SaslAuthMissingClientConfigTest.java | 98 +++++++++++++++ 7 files changed, 414 insertions(+), 37 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java create mode 100644 src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java create mode 100644 src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java diff --git a/CHANGES.txt b/CHANGES.txt index e4dcecd4a30..6f190d9a547 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,9 @@ BUGFIXES: ZOOKEEPER-1353. C client test suite fails consistently. (Clint Byrum via mahadev) + ZOOKEEPER-1373. Hardcoded SASL login context name clashes with Hadoop security + configuration override. (Eugene Koontz and Thomas Weise via mahadev) + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 1df1c9e2ccb..324d4fc696c 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -934,20 +934,14 @@ private void startConnect() throws IOException { setName(getName().replaceAll("\\(.*\\)", "(" + addr.getHostName() + ":" + addr.getPort() + ")")); - - if (System.getProperty("java.security.auth.login.config") != null) { - try { - zooKeeperSaslClient = new ZooKeeperSaslClient(ClientCnxn.this, "zookeeper"+"/"+ addr.getHostName()); - } - catch (LoginException e) { - LOG.warn("Zookeeper client cannot authenticate using the Client section of the supplied " - + "configuration file: '" + System.getProperty("java.security.auth.login.config") - + "'. Will continue connection to Zookeeper server without SASL authentication, if Zookeeper " - + "server allows it."); - eventThread.queueEvent(new WatchedEvent( - Watcher.Event.EventType.None, - KeeperState.AuthFailed, null)); - } + try { + zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); + } catch (LoginException e) { + LOG.warn("SASL authentication failed: " + e + " Will continue connection to Zookeeper server without " + + "SASL authentication, if Zookeeper server allows it."); + eventThread.queueEvent(new WatchedEvent( + Watcher.Event.EventType.None, + Watcher.Event.KeeperState.AuthFailed, null)); } clientCnxnSocket.connect(addr); } @@ -981,9 +975,9 @@ public void run() { } if (state.isConnected()) { - if ((zooKeeperSaslClient != null) && (zooKeeperSaslClient.isComplete() != true)) { + if ((zooKeeperSaslClient != null) && (zooKeeperSaslClient.isFailed() != true) && (zooKeeperSaslClient.isComplete() != true)) { try { - zooKeeperSaslClient.initialize(); + zooKeeperSaslClient.initialize(ClientCnxn.this); } catch (SaslException e) { LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e); diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index 294801ad846..54b4a2303c9 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -33,7 +33,7 @@ import javax.security.auth.callback.CallbackHandler; import org.apache.log4j.Logger; - +import org.apache.zookeeper.client.ZooKeeperSaslClient; import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.Subject; import java.util.Date; @@ -279,7 +279,10 @@ public Subject getSubject() { private synchronized LoginContext login(final String loginContextName) throws LoginException { if (loginContextName == null) { throw new LoginException("loginContext name (JAAS file section header) was null. " + - "Please check your java.security.login.auth.config setting."); + "Please check your java.security.login.auth.config (=" + + System.getProperty("java.security.login.auth.config") + + ") and your " + ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY + "(=" + + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + ")"); } LoginContext loginContext = new LoginContext(loginContextName,callbackHandler); loginContext.login(); diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 722538ea3bf..18f18e3b2f6 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -21,12 +21,12 @@ import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.Login; +import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetSASLResponse; -import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.auth.KerberosName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +40,8 @@ import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.RealmCallback; @@ -52,12 +54,12 @@ * allows ClientCnxn to authenticate using SASL with a Zookeeper server. */ public class ZooKeeperSaslClient { + public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.clientconfig"; private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class); private static Login login = null; private SaslClient saslClient; private byte[] saslToken = new byte[0]; - private ClientCnxn cnxn; public enum SaslState { INITIAL,INTERMEDIATE,COMPLETE,FAILED @@ -69,15 +71,87 @@ public SaslState getSaslState() { return saslState; } - public ZooKeeperSaslClient(ClientCnxn cnxn, String serverPrincipal) throws LoginException { - this.cnxn = cnxn; - this.saslClient = createSaslClient(serverPrincipal); + private String loginContext; + + public String getLoginContext() { + return loginContext; + } + + public ZooKeeperSaslClient(final String serverPrincipal) + throws LoginException { + /** + * ZOOKEEPER-1373: allow system property to specify the JAAS + * configuration section that the zookeeper client should use. + * Default to "Client". + */ + String clientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client"); + // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. + AppConfigurationEntry entries[] = null; + SecurityException securityException = null; + try { + entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection); + } catch (SecurityException e) { + // handle below: might be harmless if the user doesn't intend to use JAAS authentication. + securityException = e; + } + if (entries != null) { + LOG.info("Found Login Context section '" + clientSection + "': will use it to attempt to SASL-authenticate."); + this.saslClient = createSaslClient(serverPrincipal, clientSection); + } else { + // Handle situation of clientSection's being null: it might simply because the client does not intend to + // use SASL, so not necessarily an error. + saslState = SaslState.FAILED; + String explicitClientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY); + if (explicitClientSection != null) { + // If the user explicitly overrides the default Login Context, they probably expected SASL to + // succeed. But if we got here, SASL failed. + if (securityException != null) { + throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + + " section of the supplied JAAS configuration: '" + + System.getProperty("java.security.auth.login.config") + "' because of a " + + "SecurityException: " + securityException); + + + } else { + throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " + + "section '" + explicitClientSection + "' could not be found."); + } + } else { + // The user did not override the default context. It might be that they just don't intend to use SASL, + // so log at INFO, not WARN, since they don't expect any SASL-related information. + if (securityException != null) { + LOG.warn("SecurityException: " + securityException + " occurred when trying to find JAAS configuration."); + } + LOG.info("Client will not SASL-authenticate because the default JAAS configuration section 'Client' " + + "could not be found. If you are not using SASL, you may ignore this. On the other hand, " + + "if you expected SASL to work, please fix your JAAS configuration."); + } + if (System.getProperty("java.security.auth.login.config") != null) { + // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed. + if (securityException != null) { + throw new LoginException("Zookeeper client cannot authenticate using the '" + + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + + "' section of the supplied JAAS configuration: '" + + System.getProperty("java.security.auth.login.config") + "' because of a " + + "SecurityException: " + securityException); + } else { + throw new LoginException("No JAAS configuration section named '" + + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + + "' was found in specified JAAS configuration file: '" + + System.getProperty("java.security.auth.login.config") + "'."); + } + } + } } public boolean isComplete() { return (saslState == SaslState.COMPLETE); } + public boolean isFailed() { + return (saslState == SaslState.FAILED); + } + public static class ServerSaslResponseCallback implements AsyncCallback.DataCallback { public void processResult(int rc, String path, Object ctx, byte data[], Stat stat) { // processResult() is used by ClientCnxn's sendThread to respond to @@ -97,16 +171,21 @@ public void processResult(int rc, String path, Object ctx, byte data[], Stat sta usedata = new byte[0]; LOG.debug("ServerSaslResponseCallback(): using empty data[] as server response (length="+usedata.length+")"); } - client.prepareSaslResponseToServer(usedata); + client.prepareSaslResponseToServer(usedata, (ClientCnxn)ctx); } } - synchronized private SaslClient createSaslClient(final String servicePrincipal) throws LoginException { + synchronized private SaslClient createSaslClient(final String servicePrincipal, + final String loginContext) throws LoginException { try { if (login == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("JAAS loginContext is: " + loginContext); + } + this.loginContext = loginContext; // note that the login object is static: it's shared amongst all zookeeper-related connections. // createSaslClient() must be declared synchronized so that login is initialized only once. - login = new Login("Client",new ClientCallbackHandler(null)); + login = new Login(loginContext, new ClientCallbackHandler(null)); login.startThreadIfNeeded(); } Subject subject = login.getSubject(); @@ -163,7 +242,7 @@ public SaslClient run() throws SaslException { } } - private void prepareSaslResponseToServer(byte[] serverToken) { + private void prepareSaslResponseToServer(byte[] serverToken, ClientCnxn cnxn) { saslToken = serverToken; if (saslClient == null) { @@ -177,16 +256,17 @@ private void prepareSaslResponseToServer(byte[] serverToken) { saslToken = createSaslToken(saslToken); if (saslToken != null) { LOG.debug("saslToken (client) length: " + saslToken.length); - queueSaslPacket(saslToken); + queueSaslPacket(saslToken, cnxn); } } catch (SaslException e) { - LOG.error("SASL authentication failed."); + LOG.error("SASL authentication failed using login context '" + + this.getLoginContext() + "'."); saslState = SaslState.FAILED; } } } - public byte[] createSaslToken() throws SaslException { + private byte[] createSaslToken() throws SaslException { saslState = SaslState.INTERMEDIATE; return createSaslToken(saslToken); } @@ -234,7 +314,7 @@ public byte[] run() throws SaslException { } } - public void queueSaslPacket(byte[] saslToken) { + private void queueSaslPacket(byte[] saslToken, ClientCnxn cnxn) { LOG.debug("ClientCnxn:sendSaslPacket:length="+saslToken.length); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.sasl); @@ -245,9 +325,9 @@ public void queueSaslPacket(byte[] saslToken) { ReplyHeader r = new ReplyHeader(); cnxn.queuePacket(h,r,request,response,cb); } - - public void queueSaslPacket() throws SaslException { - queueSaslPacket(createSaslToken()); + + private void queueSaslPacket(ClientCnxn cnxn) throws SaslException { + queueSaslPacket(createSaslToken(), cnxn); } // used by ClientCnxn to know when to emit SaslAuthenticated event. @@ -268,17 +348,17 @@ public boolean readyToSendSaslAuthEvent() { return false; } - public void initialize() throws SaslException { + public void initialize(ClientCnxn cnxn) throws SaslException { if (saslClient == null) { throw new SaslException("saslClient failed to initialize properly: it's null."); } if (saslState == SaslState.INITIAL) { if (saslClient.hasInitialResponse()) { - queueSaslPacket(); + queueSaslPacket(cnxn); } else { byte[] emptyToken = new byte[0]; - queueSaslPacket(emptyToken); + queueSaslPacket(emptyToken, cnxn); } saslState = SaslState.INTERMEDIATE; } diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java new file mode 100644 index 00000000000..3cad9874e81 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java @@ -0,0 +1,99 @@ +/** + * 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.test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.junit.Assert; +import org.junit.Test; + +public class SaslAuthDesignatedClientTest extends ClientBase { + static { + System.setProperty("zookeeper.authProvider.1","org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperClient"); + + try { + File tmpDir = createTmpDir(); + File saslConfFile = new File(tmpDir, "jaas.conf"); + FileWriter fwriter = new FileWriter(saslConfFile); + + fwriter.write("" + + "Server {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_myuser=\"mypassword\";\n" + + "};\n" + + "Client {\n" + /* this 'Client' section has an incorrect password, but we're not configured + to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) to + use the 'MyZookeeperClient' section below, which has the correct password).*/ + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"myuser\"\n" + + " password=\"wrongpassword\";\n" + + "};" + + "MyZookeeperClient {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"myuser\"\n" + + " password=\"mypassword\";\n" + + "};" + "\n"); + fwriter.close(); + System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); + } + catch (IOException e) { + // could not create tmp directory to hold JAAS conf file : test will fail now. + } + } + + private AtomicInteger authFailed = new AtomicInteger(0); + + private class MyWatcher extends CountdownWatcher { + @Override + public synchronized void process(WatchedEvent event) { + if (event.getState() == KeeperState.AuthFailed) { + authFailed.incrementAndGet(); + } + else { + super.process(event); + } + } + } + + @Test + public void testAuth() throws Exception { + ZooKeeper zk = createClient(); + Thread.sleep(1000); + try { + zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); + Thread.sleep(1000); + } catch (KeeperException e) { + Assert.fail("test failed :" + e); + } + finally { + zk.close(); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java new file mode 100644 index 00000000000..f9f0e72b399 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.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.test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.junit.Assert; +import org.junit.Test; + +public class SaslAuthFailDesignatedClientTest extends ClientBase { + static { + System.setProperty("zookeeper.authProvider.1","org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperClient"); + + try { + File tmpDir = createTmpDir(); + File saslConfFile = new File(tmpDir, "jaas.conf"); + FileWriter fwriter = new FileWriter(saslConfFile); + + fwriter.write("" + + "Server {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_myuser=\"mypassword\";\n" + + "};\n" + + "Client {\n" + /* this 'Client' section has the correct password, but we're not configured + to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) to + use the 'MyZookeeperClient' section, which has an incorrect password).*/ + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"myuser\"\n" + + " password=\"mypassword\";\n" + + "};" + + "MyZookeeperClient {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"myuser\"\n" + + " password=\"wrongpassword\";\n" + + "};" + "\n"); + fwriter.close(); + System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); + } + catch (IOException e) { + // could not create tmp directory to hold JAAS conf file : test will fail now. + } + } + + private AtomicInteger authFailed = new AtomicInteger(0); + + private class MyWatcher extends CountdownWatcher { + @Override + public synchronized void process(WatchedEvent event) { + if (event.getState() == KeeperState.AuthFailed) { + authFailed.incrementAndGet(); + } + else { + super.process(event); + } + } + } + + @Test + public void testAuth() throws Exception { + ZooKeeper zk = createClient(); + Thread.sleep(1000); + try { + zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); + Assert.fail("Should have gotten exception."); + } catch (KeeperException e) { + // ok, exception as expected. + LOG.info("Got exception as expected: " + e); + } + finally { + zk.close(); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java new file mode 100644 index 00000000000..8642155d80e --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java @@ -0,0 +1,98 @@ +/** + * 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.test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.junit.Assert; +import org.junit.Test; + +public class SaslAuthMissingClientConfigTest extends ClientBase { + static { + System.setProperty("zookeeper.authProvider.1","org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + // This configuration section 'MyZookeeperClient', is missing from the JAAS configuration. + // As a result, SASL authentication should fail, which is tested by this test (testAuth()). + System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperClient"); + + try { + File tmpDir = createTmpDir(); + File saslConfFile = new File(tmpDir, "jaas.conf"); + FileWriter fwriter = new FileWriter(saslConfFile); + + fwriter.write("" + + "Server {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_myuser=\"mypassword\";\n" + + "};\n" + + "Client {\n" + /* this 'Client' section has the correct password, but we're not configured + to use it - we're configured instead by the above + System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) to + use the (nonexistent) 'MyZookeeperClient' section. */ + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"myuser\"\n" + + " password=\"mypassword\";\n" + + "};\n"); + fwriter.close(); + System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); + } + catch (IOException e) { + // could not create tmp directory to hold JAAS conf file : test will fail now. + } + } + + private AtomicInteger authFailed = new AtomicInteger(0); + + private class MyWatcher extends CountdownWatcher { + @Override + public synchronized void process(WatchedEvent event) { + if (event.getState() == KeeperState.AuthFailed) { + authFailed.incrementAndGet(); + } + else { + super.process(event); + } + } + } + + @Test + public void testAuth() throws Exception { + ZooKeeper zk = createClient(); + Thread.sleep(1000); + try { + zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); + Assert.fail("Should have gotten exception."); + } catch (KeeperException e) { + // ok, exception as expected. + LOG.info("Got exception as expected: " + e); + } + finally { + zk.close(); + } + } +} From 498ac802ef7e1051414925cf77fdcd7d248fb2bb Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 6 Feb 2012 08:44:24 +0000 Subject: [PATCH 052/444] ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1240922 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++ .../server/persistence/FileTxnLog.java | 12 ++-- .../server/persistence/FileTxnSnapLog.java | 12 ++-- .../server/quorum/FastLeaderElection.java | 63 ++++++++++--------- .../zookeeper/server/quorum/Leader.java | 42 ++++++------- .../server/quorum/LearnerHandler.java | 27 +++++--- .../zookeeper/server/quorum/QuorumPeer.java | 13 ++-- 7 files changed, 94 insertions(+), 79 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6f190d9a547..c26ab388007 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -29,6 +29,10 @@ BUGFIXES: ZOOKEEPER-1373. Hardcoded SASL login context name clashes with Hadoop security configuration override. (Eugene Koontz and Thomas Weise via mahadev) +IMPROVEMENTS: + + ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev) + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index 3a0f68084e1..64f3af7385f 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -19,7 +19,6 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; @@ -41,10 +40,10 @@ import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.TxnHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class implements the TxnLog interface. It provides api's @@ -602,8 +601,9 @@ public boolean next() throws IOException { long crcValue = ia.readLong("crcvalue"); byte[] bytes = Util.readTxnBytes(ia); // Since we preallocate, we define EOF to be an - if (bytes == null || bytes.length==0) - throw new EOFException("Failed to read"); + if (bytes == null || bytes.length==0) { + throw new EOFException("Failed to read " + logFile); + } // EOF or corrupted record // validate CRC Checksum crc = makeChecksumAlgorithm(); @@ -621,7 +621,7 @@ record = SerializeUtils.deserializeTxn(bytes, hdr); ia = null; hdr = null; // this means that the file has ended - // we shoud go to the next file + // we should go to the next file if (!goToNextLog()) { return false; } diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index 3e516c71af9..3ad62f2d406 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -175,7 +175,7 @@ public void processTransaction(TxnHeader hdr,DataTree dt, ((CreateSessionTxn) txn).getTimeOut()); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage(LOG,ZooTrace.SESSION_TRACE_MASK, - "playLog --- create session in log: " + "playLog --- create session in log: 0x" + Long.toHexString(hdr.getClientId()) + " with timeout: " + ((CreateSessionTxn) txn).getTimeOut()); @@ -187,7 +187,7 @@ public void processTransaction(TxnHeader hdr,DataTree dt, sessions.remove(hdr.getClientId()); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage(LOG,ZooTrace.SESSION_TRACE_MASK, - "playLog --- close session in log: " + "playLog --- close session in log: 0x" + Long.toHexString(hdr.getClientId())); } rc = dt.processTxn(hdr, txn); @@ -234,10 +234,10 @@ public void save(DataTree dataTree, ConcurrentHashMap sessionsWithTimeouts) throws IOException { long lastZxid = dataTree.lastProcessedZxid; - LOG.info("Snapshotting: " + Long.toHexString(lastZxid)); - File snapshot=new File( - snapDir, Util.makeSnapshotName(lastZxid)); - snapLog.serialize(dataTree, sessionsWithTimeouts, snapshot); + File snapshotFile = new File(snapDir, Util.makeSnapshotName(lastZxid)); + LOG.info("Snapshotting: 0x{} to {}", Long.toHexString(lastZxid), + snapshotFile); + snapLog.serialize(dataTree, sessionsWithTimeouts, snapshotFile); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index a584adc4fa2..25560ef8c37 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -27,14 +27,14 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.quorum.QuorumCnxManager.Message; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.util.ZxidUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -270,7 +270,7 @@ public void run() { n.peerEpoch = response.buffer.getLong(); } else { if(LOG.isInfoEnabled()){ - LOG.info("Backward compatibility mode, server id: " + n.sid); + LOG.info("Backward compatibility mode, server id=" + n.sid); } n.peerEpoch = ZxidUtils.getEpochFromZxid(n.zxid); } @@ -315,10 +315,10 @@ public void run() { if(ackstate == QuorumPeer.ServerState.LOOKING){ if(LOG.isDebugEnabled()){ LOG.debug("Sending new notification. My id = " + - self.getId() + ", Recipient = " + - response.sid + " zxid =" + - current.getZxid() + " leader=" + - current.getId()); + self.getId() + " recipient=" + + response.sid + " zxid=0x" + + Long.toHexString(current.getZxid()) + + " leader=" + current.getId()); } ToSend notmsg = new ToSend( ToSend.mType.notification, @@ -490,10 +490,10 @@ private void starter(QuorumPeer self, QuorumCnxManager manager) { private void leaveInstance(Vote v) { if(LOG.isDebugEnabled()){ - LOG.debug("About to leave FLE instance: Leader= " - + v.getId() + ", Zxid = " + - v.getZxid() + ", My id = " + self.getId() - + ", My state = " + self.getPeerState()); + LOG.debug("About to leave FLE instance: leader=" + + v.getId() + ", zxid=0x" + + Long.toHexString(v.getZxid()) + ", my id=" + self.getId() + + ", my state=" + self.getPeerState()); } recvqueue.clear(); } @@ -528,10 +528,10 @@ private void sendNotifications() { sid, proposedEpoch); if(LOG.isDebugEnabled()){ - LOG.debug("Sending Notification: " + proposedLeader + " (n.leader), " + - proposedZxid + " (n.zxid), " + logicalclock + + LOG.debug("Sending Notification: " + proposedLeader + " (n.leader), 0x" + + Long.toHexString(proposedZxid) + " (n.zxid), 0x" + Long.toHexString(logicalclock) + " (n.round), " + sid + " (recipient), " + self.getId() + - " (myid), " + proposedEpoch + " (n.peerEpoch)"); + " (myid), 0x" + Long.toHexString(proposedEpoch) + " (n.peerEpoch)"); } sendqueue.offer(notmsg); } @@ -539,10 +539,12 @@ private void sendNotifications() { private void printNotification(Notification n){ - LOG.info("Notification: " + n.leader + " (n.leader), " + n.zxid + - " (n.zxid), " + n.electionEpoch + " (n.round), " + n.state + - " (n.state), " + n.sid + " (n.sid), " + n.peerEpoch + " (n.peerEPoch), " + - self.getPeerState() + " (my state)"); + LOG.info("Notification: " + n.leader + " (n.leader), 0x" + + Long.toHexString(n.zxid) + " (n.zxid), 0x" + + Long.toHexString(n.electionEpoch) + " (n.round), " + n.state + + " (n.state), " + n.sid + " (n.sid), 0x" + + Long.toHexString(n.peerEpoch) + " (n.peerEPoch), " + + self.getPeerState() + " (my state)"); } /** @@ -553,8 +555,8 @@ private void printNotification(Notification n){ * @param zxid Last zxid observed by the issuer of this vote */ private boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) { - LOG.debug("id: " + newId + ", proposed id: " + curId + ", zxid: " + - newZxid + ", proposed zxid: " + curZxid); + LOG.debug("id: " + newId + ", proposed id: " + curId + ", zxid: 0x" + + Long.toHexString(newZxid) + ", proposed zxid: 0x" + Long.toHexString(curZxid)); if(self.getQuorumVerifier().getWeight(newId) == 0){ return false; } @@ -626,9 +628,9 @@ private boolean checkLeader( synchronized void updateProposal(long leader, long zxid, long epoch){ if(LOG.isDebugEnabled()){ - LOG.debug("Updating proposal: " + leader + " (newleader), " + zxid + - " (newzxid), " + proposedLeader + " (oldleader), " + - proposedZxid + " (oldzxid)"); + LOG.debug("Updating proposal: " + leader + " (newleader), 0x" + + Long.toHexString(zxid) + " (newzxid), " + proposedLeader + + " (oldleader), 0x" + Long.toHexString(proposedZxid) + " (oldzxid)"); } proposedLeader = leader; proposedZxid = zxid; @@ -726,7 +728,7 @@ public Vote lookForLeader() throws InterruptedException { } LOG.info("New election. My id = " + self.getId() + - ", Proposed zxid = " + proposedZxid); + ", proposed zxid=0x" + Long.toHexString(proposedZxid)); sendNotifications(); /* @@ -783,8 +785,9 @@ else if(self.getVotingView().containsKey(n.sid)) { sendNotifications(); } else if (n.electionEpoch < logicalclock) { if(LOG.isDebugEnabled()){ - LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = " + n.electionEpoch - + ", Logical clock" + logicalclock); + LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x" + + Long.toHexString(n.electionEpoch) + + ", logicalclock=0x" + Long.toHexString(logicalclock)); } break; } else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, @@ -794,10 +797,10 @@ else if(self.getVotingView().containsKey(n.sid)) { } if(LOG.isDebugEnabled()){ - LOG.debug("Adding vote: From = " + n.sid + - ", Proposed leader = " + n.leader + - ", Proposed zxid = " + n.zxid + - ", Proposed election epoch = " + n.electionEpoch); + LOG.debug("Adding vote: from=" + n.sid + + ", proposed leader=" + n.leader + + ", proposed zxid=0x" + Long.toHexString(n.zxid) + + ", proposed election epoch=0x" + Long.toHexString(n.electionEpoch)); } recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch)); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 573ae083d16..488799c4086 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -32,21 +32,20 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.BinaryOutputArchive; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ZxidUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class has the control logic for the Leader. @@ -465,14 +464,12 @@ void shutdown(String reason) { * @param followerAddr */ synchronized public void processAck(long sid, long zxid, SocketAddress followerAddr) { - boolean first = true; - if (LOG.isTraceEnabled()) { - LOG.trace("Ack zxid: 0x" + Long.toHexString(zxid)); + LOG.trace("Ack zxid: 0x{}", Long.toHexString(zxid)); for (Proposal p : outstandingProposals.values()) { long packetZxid = p.packet.getZxid(); - LOG.trace("outstanding proposal: 0x" - + Long.toHexString(packetZxid)); + LOG.trace("outstanding proposal: 0x{}", + Long.toHexString(packetZxid)); } LOG.trace("outstanding proposals all"); } @@ -485,31 +482,29 @@ synchronized public void processAck(long sid, long zxid, SocketAddress followerA } if (lastCommitted >= zxid) { if (LOG.isDebugEnabled()) { - LOG.debug("proposal has already been committed, pzxid:" - + lastCommitted - + " zxid: 0x" + Long.toHexString(zxid)); + LOG.debug("proposal has already been committed, pzxid: 0x{} zxid: 0x{}", + Long.toHexString(lastCommitted), Long.toHexString(zxid)); } // The proposal has already been committed return; } Proposal p = outstandingProposals.get(zxid); if (p == null) { - LOG.warn("Trying to commit future proposal: zxid 0x" - + Long.toHexString(zxid) + " from " + followerAddr); + LOG.warn("Trying to commit future proposal: zxid 0x{} from {}", + Long.toHexString(zxid), followerAddr); return; } p.ackSet.add(sid); if (LOG.isDebugEnabled()) { - LOG.debug("Count for zxid: 0x" + Long.toHexString(zxid) - + " is " + p.ackSet.size()); + LOG.debug("Count for zxid: 0x{} is {}", + Long.toHexString(zxid), p.ackSet.size()); } if (self.getQuorumVerifier().containsQuorum(p.ackSet)){ if (zxid != lastCommitted+1) { - LOG.warn("Commiting zxid 0x" + Long.toHexString(zxid) - + " from " + followerAddr + " not first!"); - LOG.warn("First is " - + (lastCommitted+1)); + LOG.warn("Commiting zxid 0x{} from {} not first!", + Long.toHexString(zxid), followerAddr); + LOG.warn("First is 0x{}", Long.toHexString(lastCommitted + 1)); } outstandingProposals.remove(zxid); if (p.request != null) { @@ -518,7 +513,7 @@ synchronized public void processAck(long sid, long zxid, SocketAddress followerA // We don't commit the new leader proposal if ((zxid & 0xffffffffL) != 0) { if (p.request == null) { - LOG.warn("Going to commmit null: " + p); + LOG.warn("Going to commmit null request for proposal: {}", p); } commit(zxid); inform(p); @@ -531,9 +526,8 @@ synchronized public void processAck(long sid, long zxid, SocketAddress followerA return; } else { lastCommitted = zxid; - if(LOG.isInfoEnabled()){ - LOG.info("Have quorum of supporters; starting up and setting last processed zxid: " + zk.getZxid()); - } + LOG.info("Have quorum of supporters; starting up and setting last processed zxid: 0x{}", + Long.toHexString(zk.getZxid())); zk.startup(); zk.getZKDatabase().setlastProcessedZxid(zk.getZxid()); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 9c28663ad40..781aaa2cdbc 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -35,8 +35,6 @@ import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.ByteBufferInputStream; @@ -47,6 +45,8 @@ import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.TxnHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * There will be an instance of this class created by the Leader for each @@ -261,8 +261,8 @@ public void run() { this.sid = leader.followerCounter.getAndDecrement(); } - LOG.info("Follower sid: " + this.sid + " : info : " - + leader.self.quorumPeers.get(this.sid)); + LOG.info("Follower sid: " + sid + " : info : " + + leader.self.quorumPeers.get(sid)); if (qp.getType() == Leader.OBSERVERINFO) { learnerType = LearnerType.OBSERVER; @@ -316,16 +316,18 @@ public void run() { rl.lock(); final long maxCommittedLog = leader.zk.getZKDatabase().getmaxCommittedLog(); final long minCommittedLog = leader.zk.getZKDatabase().getminCommittedLog(); - LOG.info("Synchronizing with Follower sid: " + this.sid - +" maxCommittedLog ="+Long.toHexString(maxCommittedLog) - +" minCommittedLog = "+Long.toHexString(minCommittedLog) - +" peerLastZxid = "+Long.toHexString(peerLastZxid)); + LOG.info("Synchronizing with Follower sid: " + sid + +" maxCommittedLog=0x"+Long.toHexString(maxCommittedLog) + +" minCommittedLog=0x"+Long.toHexString(minCommittedLog) + +" peerLastZxid=0x"+Long.toHexString(peerLastZxid)); LinkedList proposals = leader.zk.getZKDatabase().getCommittedLog(); if (proposals.size() != 0) { + LOG.debug("proposal size is {}", proposals.size()); if ((maxCommittedLog >= peerLastZxid) && (minCommittedLog <= peerLastZxid)) { + LOG.debug("Sending proposals to follower"); // as we look through proposals, this variable keeps track of previous // proposal Id. @@ -369,16 +371,25 @@ public void run() { } } } else if (peerLastZxid > maxCommittedLog) { + LOG.debug("Sending TRUNC to follower zxidToSend=0x{} updates=0x{}", + Long.toHexString(maxCommittedLog), + Long.toHexString(updates)); + packetToSend = Leader.TRUNC; zxidToSend = maxCommittedLog; updates = zxidToSend; + } else { + LOG.warn("Unhandled proposal scenario"); } } else { // just let the state transfer happen + LOG.debug("proposals is empty"); } leaderLastZxid = leader.startForwarding(this, updates); if (peerLastZxid == leaderLastZxid) { + LOG.debug("Leader and follower are in sync, sending empty diff. zxid=0x{}", + Long.toHexString(leaderLastZxid)); // We are in sync so we'll do an empty diff packetToSend = Leader.DIFF; zxidToSend = leaderLastZxid; 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 35df0c9bafa..3c3353f793d 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -31,14 +31,11 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.server.ServerCnxnFactory; @@ -48,6 +45,8 @@ import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ZxidUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class manages the quorum protocol. There are three states this server @@ -426,8 +425,10 @@ private void loadDataBase() { // pick a reasonable epoch number // this should only happen once when moving to a // new code version - LOG.info(CURRENT_EPOCH_FILENAME + " not found! Creating with a reasonable default. This should only happen when you are upgrading your installation"); currentEpoch = epochOfZxid; + LOG.info(CURRENT_EPOCH_FILENAME + + " not found! Creating with a reasonable default of {}. This should only happen when you are upgrading your installation", + currentEpoch); writeLongToFile(CURRENT_EPOCH_FILENAME, currentEpoch); } if (epochOfZxid > currentEpoch) { @@ -439,8 +440,10 @@ private void loadDataBase() { // pick a reasonable epoch number // this should only happen once when moving to a // new code version - LOG.info(ACCEPTED_EPOCH_FILENAME + " not found! Creating with a reasonable default. This should only happen when you are upgrading your installation"); acceptedEpoch = epochOfZxid; + LOG.info(ACCEPTED_EPOCH_FILENAME + + " not found! Creating with a reasonable default of {}. This should only happen when you are upgrading your installation", + acceptedEpoch); writeLongToFile(CURRENT_EPOCH_FILENAME, acceptedEpoch); } if (acceptedEpoch < currentEpoch) { From 93821df1aa83099b680ed9cda0b1fd1c8e073cc8 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 6 Feb 2012 08:49:51 +0000 Subject: [PATCH 053/444] ZOOKEEPER-1352. server.InvalidSnapshotTest is using connection timeouts that are too short. (phunt via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1240925 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/InvalidSnapshotTest.java | 114 +++++++----------- .../org/apache/zookeeper/test/ClientBase.java | 13 +- 3 files changed, 51 insertions(+), 79 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c26ab388007..a6673c926b9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -29,6 +29,9 @@ BUGFIXES: ZOOKEEPER-1373. Hardcoded SASL login context name clashes with Hadoop security configuration override. (Eugene Koontz and Thomas Weise via mahadev) + ZOOKEEPER-1352. server.InvalidSnapshotTest is using connection timeouts that + are too short. (phunt via mahadev) + IMPROVEMENTS: ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev) diff --git a/src/java/test/org/apache/zookeeper/server/InvalidSnapshotTest.java b/src/java/test/org/apache/zookeeper/server/InvalidSnapshotTest.java index 738f6656905..97ad7923ed0 100644 --- a/src/java/test/org/apache/zookeeper/server/InvalidSnapshotTest.java +++ b/src/java/test/org/apache/zookeeper/server/InvalidSnapshotTest.java @@ -18,95 +18,67 @@ package org.apache.zookeeper.server; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.RandomAccessFile; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.PortAssignment; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZKTestCase; -import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; -import org.junit.Assert; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * this test checks that the server works - * even if the last snapshot is invalidated - * by corruption or if the server crashes - * while generating the snapshot. + * This test checks that the server works even if the last snapshot is + * invalidated by corruption or if the server crashes while generating the + * snapshot. */ -public class InvalidSnapshotTest extends ZKTestCase implements Watcher { +public class InvalidSnapshotTest extends ClientBase { private static final Logger LOG = - LoggerFactory.getLogger(InvalidSnapshotTest.class); + LoggerFactory.getLogger(InvalidSnapshotTest.class); - private static final String HOSTPORT = - "127.0.0.1:" + PortAssignment.unique(); - private static final int CONNECTION_TIMEOUT = 3000; + public InvalidSnapshotTest() { + SyncRequestProcessor.setSnapCount(100); + } /** - * this test does the main work of testing - * an invalid snapshot - * @throws Exception + * Validate that the server can come up on an invalid snapshot - by + * reverting to a prior snapshot + associated logs. */ @Test public void testInvalidSnapshot() throws Exception { - File tmpDir = ClientBase.createTmpDir(); - ClientBase.setupTestEnv(); - ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); - SyncRequestProcessor.setSnapCount(100); - final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); - ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); - f.startup(zks); - Assert.assertTrue("waiting for server being up ", - ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); - ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - try { - for (int i=0; i< 2000; i++) { - zk.create("/invalidsnap-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - } - } finally { - zk.close(); - } - f.shutdown(); - Assert.assertTrue("waiting for server to shutdown", - ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); - // now corrupt the snapshot - File snapFile = zks.getTxnLogFactory().findMostRecentSnapshot(); - RandomAccessFile raf = new RandomAccessFile(snapFile, "rws"); - raf.setLength(3); - raf.close(); - // now restart the server and see if it starts - zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); - SyncRequestProcessor.setSnapCount(100); - f = ServerCnxnFactory.createFactory(PORT, -1); - f.startup(zks); - Assert.assertTrue("waiting for server being up ", - ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); - // the server should come up - zk = new ZooKeeper(HOSTPORT, 20000, this); - try { - Assert.assertTrue("the node should exist", - (zk.exists("/invalidsnap-1999", false) != null)); - f.shutdown(); - Assert.assertTrue("waiting for server to shutdown", - ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); - } finally { - zk.close(); - } + ZooKeeper zk = createClient(); + try { + for (int i = 0; i < 2000; i++) { + zk.create("/invalidsnap-" + i, new byte[0], + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + } finally { + zk.close(); + } + NIOServerCnxnFactory factory = (NIOServerCnxnFactory)serverFactory; + stopServer(); - f.shutdown(); - Assert.assertTrue("waiting for server to shutdown", - ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); - } + // now corrupt the snapshot + File snapFile = factory.zkServer.getTxnLogFactory().findMostRecentSnapshot(); + LOG.info("Corrupting " + snapFile); + RandomAccessFile raf = new RandomAccessFile(snapFile, "rws"); + raf.setLength(3); + raf.close(); - public void process(WatchedEvent event) { - // do nothing for now - } + // now restart the server + startServer(); + // verify that the expected data exists and wasn't lost + zk = createClient(); + try { + assertTrue("the node should exist", + (zk.exists("/invalidsnap-1999", false) != null)); + } finally { + zk.close(); + } + } } diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index fb34ca18bff..c24de55fbd5 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -18,14 +18,12 @@ package org.apache.zookeeper.test; -import java.io.BufferedReader; +import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; + import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; -import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; @@ -38,8 +36,6 @@ import javax.management.MBeanServerConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; @@ -53,10 +49,11 @@ import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnLog; -import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.sun.management.UnixOperatingSystemMXBean; @@ -299,7 +296,7 @@ private static int getPort(String hostPort) { return Integer.parseInt(portstr); } - static ServerCnxnFactory createNewServerInstance(File dataDir, + public static ServerCnxnFactory createNewServerInstance(File dataDir, ServerCnxnFactory factory, String hostPort, int maxCnxns) throws IOException, InterruptedException { From 2c2df31a65f4684488fead999687569dcb995a1d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 6 Feb 2012 08:57:37 +0000 Subject: [PATCH 054/444] ZOOKEEPER-1336. javadoc for multi is confusing, references functionality that doesn't seem to exist (phunt via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1240932 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../org/apache/zookeeper/KeeperException.java | 3 ++ .../org/apache/zookeeper/Transaction.java | 3 ++ .../main/org/apache/zookeeper/ZooKeeper.java | 48 +++++++++++++------ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a6673c926b9..6171c9a6fe5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -32,6 +32,9 @@ BUGFIXES: ZOOKEEPER-1352. server.InvalidSnapshotTest is using connection timeouts that are too short. (phunt via mahadev) + ZOOKEEPER-1336. javadoc for multi is confusing, references functionality that doesn't + seem to exist (phunt via mahadev) + IMPROVEMENTS: ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev) diff --git a/src/java/main/org/apache/zookeeper/KeeperException.java b/src/java/main/org/apache/zookeeper/KeeperException.java index 7c10d2ce284..2664411d36b 100644 --- a/src/java/main/org/apache/zookeeper/KeeperException.java +++ b/src/java/main/org/apache/zookeeper/KeeperException.java @@ -482,6 +482,9 @@ void setMultiResults(List results) { * If this exception was thrown by a multi-request then the (partial) results * and error codes can be retrieved using this getter. * @return A copy of the list of results from the operations in the multi-request. + * + * @since 3.4.0 + * */ public List getResults() { return results != null ? new ArrayList(results) : null; diff --git a/src/java/main/org/apache/zookeeper/Transaction.java b/src/java/main/org/apache/zookeeper/Transaction.java index b8dc6603040..c58e7328717 100644 --- a/src/java/main/org/apache/zookeeper/Transaction.java +++ b/src/java/main/org/apache/zookeeper/Transaction.java @@ -24,6 +24,9 @@ /** * Provides a builder style interface for doing multiple updates. This is * really just a thin layer on top of Zookeeper.multi(). + * + * @since 3.4.0 + * */ public class Transaction { private ZooKeeper zk; diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index e82eaa0c2b3..9fe765208bc 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -871,22 +871,31 @@ public void delete(final String path, int version) } /** - * Executes multiple Zookeeper operations or none of them. On success, a list of results is returned. - * On failure, only a single exception is returned. If you want more details, it may be preferable to - * use the alternative form of this method that lets you pass a list into which individual results are - * placed so that you can zero in on exactly which operation failed and why. + * Executes multiple ZooKeeper operations or none of them. *

    - * The maximum allowable size of all of the data arrays in all of the setData operations in this single - * request is 1 MB (1,048,576 bytes). - * Requests larger than this will cause a KeeperExecption to be thrown. - * @param ops An iterable that contains the operations to be done. These should be created using the - * factory methods on Op. - * @see Op - * @return A list of results. - * @throws InterruptedException If the operation was interrupted. The operation may or may not have succeeded, but - * will not have partially succeeded if this exception is thrown. - * @throws KeeperException If the operation could not be completed due to some error in doing one of the specified - * ops. + * On success, a list of results is returned. + * On failure, an exception is raised which contains partial results and + * error details, see {@link KeeperException#getResults} + *

    + * Note: The maximum allowable size of all of the data arrays in all of + * the setData operations in this single request is typically 1 MB + * (1,048,576 bytes). This limit is specified on the server via + * jute.maxbuffer. + * Requests larger than this will cause a KeeperException to be + * thrown. + * + * @param ops An iterable that contains the operations to be done. + * These should be created using the factory methods on {@link Op}. + * @return A list of results, one for each input Op, the order of + * which exactly matches the order of the ops input + * operations. + * @throws InterruptedException If the operation was interrupted. + * The operation may or may not have succeeded, but will not have + * partially succeeded if this exception is thrown. + * @throws KeeperException If the operation could not be completed + * due to some error in doing one of the specified ops. + * + * @since 3.4.0 */ public List multi(Iterable ops) throws InterruptedException, KeeperException { return multiInternal(new MultiTransactionRecord(ops)); @@ -921,6 +930,15 @@ protected List multiInternal(MultiTransactionRecord request) return results; } + /** + * A Transaction is a thin wrapper on the {@link #multi} method + * which provides a builder object that can be used to construct + * and commit an atomic set of operations. + * + * @since 3.4.0 + * + * @return a Transaction builder object + */ public Transaction transaction() { return new Transaction(this); } From 0b50488351af8395034f7ac28182d7984582307d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 6 Feb 2012 09:15:32 +0000 Subject: [PATCH 055/444] ZOOKEEPER-1327. there are still remnants of hadoop urls. (Harsh J via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1240942 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ README.txt | 4 +-- docs/bookkeeperConfig.html | 16 ++++++------ docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.html | 16 ++++++------ docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.html | 16 ++++++------ docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.html | 18 ++++++------- docs/bookkeeperStarted.pdf | Bin 17122 -> 17115 bytes docs/bookkeeperStream.html | 16 ++++++------ docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.html | 22 ++++++++-------- docs/index.pdf | Bin 13496 -> 13517 bytes docs/javaExample.html | 16 ++++++------ docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.html | 24 +++++++++--------- docs/linkmap.pdf | Bin 12445 -> 12462 bytes docs/recipes.html | 16 ++++++------ docs/recipes.pdf | Bin 31043 -> 31043 bytes docs/releasenotes.html | 16 ++++++------ docs/releasenotes.pdf | Bin 88064 -> 88064 bytes docs/zookeeperAdmin.html | 20 +++++++-------- docs/zookeeperAdmin.pdf | Bin 72898 -> 72882 bytes docs/zookeeperHierarchicalQuorums.html | 16 ++++++------ docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.html | 16 ++++++------ docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.html | 16 ++++++------ docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.html | 16 ++++++------ docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.html | 22 ++++++++-------- docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.html | 20 +++++++-------- docs/zookeeperProgrammers.pdf | Bin 133759 -> 133787 bytes docs/zookeeperQuotas.html | 16 ++++++------ docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.html | 18 ++++++------- docs/zookeeperStarted.pdf | Bin 27563 -> 27556 bytes docs/zookeeperTutorial.html | 16 ++++++------ docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes ivy.xml | 2 +- src/c/README | 4 +-- src/c/configure.ac | 2 +- src/c/include/winconfig.h | 2 +- src/contrib/huebrowser/README | 2 +- src/contrib/huebrowser/zkui/setup.py | 2 +- .../zkui/src/zkui/templates/tree.mako | 2 +- src/contrib/loggraph/ivy.xml | 2 +- src/contrib/monitoring/README | 2 +- src/contrib/rest/ivy.xml | 2 +- src/contrib/zkperl/README | 6 ++--- src/contrib/zkperl/ZooKeeper.pm | 5 ++-- .../apache/zookeeper/inspector/gui/about.html | 4 +-- .../content/xdocs/bookkeeperStarted.xml | 2 +- .../src/documentation/content/xdocs/site.xml | 20 +++++++-------- .../src/documentation/content/xdocs/tabs.xml | 4 +-- .../content/xdocs/zookeeperAdmin.xml | 4 +-- .../content/xdocs/zookeeperOver.xml | 6 ++--- .../content/xdocs/zookeeperProgrammers.xml | 4 +-- .../content/xdocs/zookeeperStarted.xml | 2 +- .../content/xdocs/zookeeperTutorial.xml | 2 +- src/docs/src/documentation/skinconf.xml | 10 ++++---- .../zookeeper/server/auth/KerberosName.java | 2 +- 65 files changed, 226 insertions(+), 226 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6171c9a6fe5..c8b5449ab0a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -39,6 +39,9 @@ IMPROVEMENTS: ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev) + ZOOKEEPER-1327. there are still remnants of hadoop urls. + (Harsh J via mahadev) + Release 3.4.2 - 2011-12-21 Backward compatible changes: diff --git a/README.txt b/README.txt index 3495f47cb41..d056f5afb91 100644 --- a/README.txt +++ b/README.txt @@ -1,10 +1,10 @@ For the latest information about ZooKeeper, please visit our website at: - http://hadoop.apache.org/zookeeper/ + http://zookeeper.apache.org/ and our wiki, at: - http://wiki.apache.org/hadoop/ZooKeeper + https://cwiki.apache.org/confluence/display/ZOOKEEPER Full documentation for this release can also be found in docs/index.html diff --git a/docs/bookkeeperConfig.html b/docs/bookkeeperConfig.html index 42a2c0925ff..fd925d3d193 100644 --- a/docs/bookkeeperConfig.html +++ b/docs/bookkeeperConfig.html @@ -20,7 +20,7 @@ |breadtrail +-->

    @@ -61,10 +61,10 @@ +-->
    • -Project +Project
    • -Wiki +Wiki
    • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
      diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 97512715c9aa7c447e0cf99d0aafb3d2049d0e0a..555ae9049a1675a1f7e36e5ec03f2e6484328e93 100644 GIT binary patch delta 183 zcmaE#{XTobL~bJkBLg!7LqlU@%Z+oc^9UH}8UT@5h=HM%p|O>*<>qGI35*z`la={( zF$6a!@EfSb8#o&om{_=(nHw7$I2$?}o0wY|S~{5;yE?hLnwXgw*eTc$R1(W&XUA1s Zl2}wyQIwj-WoTq(X~dgw;t1pw=dEb;&V delta 183 zcmaE#{XTobL~cVvBO@R-HaD=?IOjT#fT6CTk*<+ph@p{{vALCj#pY(-35*z`la={( zF$6a!@EfSbJ35=1I=i`;nOHhnSQwbO8k@TqI2#zd8kn0HI~zEf+bP%(R1(W&XUA1s Zl2}wyQIwj-WoTq(X~dgw;t1po~mEm{Bo diff --git a/docs/bookkeeperOverview.html b/docs/bookkeeperOverview.html index 59cd6ac9a4e..645f6515c59 100644 --- a/docs/bookkeeperOverview.html +++ b/docs/bookkeeperOverview.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
      • -Project +Project
      • -Wiki +Wiki
      • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
        diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index 0231dd1d2909bbc35e26edd8f23e6bdcff470ffb..17ea204b3edd7a6959b09ebc9254d8f71105a8de 100644 GIT binary patch delta 170 zcmey?!1=9#b3zNZk%5tcnSr69v5CpX{!=^xM!E(-WENszXk}<@Wn!{9lXn6mhUjEY zeq9W~W;gzJH-5(LZv0Fa44j<}E!_-VjT~Jp&CM+g4GfK)OkGXQjg8Em%nU4@jEw9Q NYzQft9_hd&3jluHDFpxk delta 170 zcmey?!1=9#b3zNZp`nox5F48t8gJ}B#Uo&-YiOivWEf&-WMyn_WoW!PlXn6mhUjEY zeq9W~W;gzJH-5(LZv0Fa44mEEEG?YPO^ux`4J@5pU5(95Ow-3(nEom>qp%?<4o NYzQft9_hd&3jm=wDeV9N diff --git a/docs/bookkeeperProgrammer.html b/docs/bookkeeperProgrammer.html index a030bc21f77..acb0b2b36f3 100644 --- a/docs/bookkeeperProgrammer.html +++ b/docs/bookkeeperProgrammer.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
        • -Project +Project
        • -Wiki +Wiki
        • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
          diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index fcfab37f25f79cff8e9d3d563f01e64ce55999c0..ff8f94993355052d4a5cf6fb0be2b69cb6040009 100644 GIT binary patch delta 161 zcmZoY%-DLEal%AyBLgD?GXq0IV-v%TbFT9U80i`Sky(g=p_QSrm5JfzX5I;m7^0Jv z`E@Y_Hz)8P3v@QKFt&7ZGIp^vb22e8H*|D0cX73FGqZ3qb+dFaGcvSOupy*ma%Q3o E03lB(AOHXW delta 161 zcmZoY%-DLEal%AyLqj7YAT~BPG}t)jI*)*%uAz~xkzt6Tk(IHzm7&4rX5I;m7^0Jv z`E@Y_Hz)8P3v@Pdvve|bHgYj>bvCeYF*SBHb2W5yG;nb?a5Hf-b9Av&upy*ma%Q3o E04;ndZU6uP diff --git a/docs/bookkeeperStarted.html b/docs/bookkeeperStarted.html index 17b1c4ea931..d039ef4360f 100644 --- a/docs/bookkeeperStarted.html +++ b/docs/bookkeeperStarted.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
          • -Project +Project
          • -Wiki +Wiki
          • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
            @@ -253,7 +253,7 @@

            Pre-requisites

            Download

            BookKeeper is distributed along with ZooKeeper. To get a ZooKeeper distribution, download a recent - + stable release from one of the Apache Download Mirrors.

            diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index 8552aeada2c328cbc73857b62ade853e026de58d..fc63aef8ef21d6c554f4ac03854b8d8e39ae0589 100644 GIT binary patch delta 1144 zcmai!u}T9$5Qd56si@!s1VltJGPAR@vnzU#lf+UGThS<|f?y~n`n{j2rgF~|NS@ne>1oD^;r9StUWzX8!+Giku1Hve9X+72E@x90B#~}S{gje zKGl?3EzLEQI5?i$pIW@{b}!5Fy6kOqt~;j}nIVqi)%a15q;%~xtzedM9f!77LIqBH@oH4{?JTh#T0LHXE^;QHfq9Ql;d zL5hHe*d;z>qwv8CrS*_$qF&rTF7HM6j(pl`2mmQDvgK5RfL4WOR$(BRzUYJw(=ZS$ z>M(56jnOzAkih-nqo0xV$38AHDj~89nnWa>Cz4?WLZrrt6lXj>x?%s39HhiZ0u#oO z&!gTrprW(zOE(4=8>s48W46<_#>xHhvbFjsGE22vY#os_3xkCZZET{G7Peh$rx0w$ kzl1HMmDjiawH#)7p55GbdbfAI@+`x6Iv`tKZtb_Kvm%+(*@ zHSB-z?9D@N{t{=`Kz6$VHVh1JYU+Jd^~GuL&uQ=M+YNxIdpLs5zyH2@8w1dQ@B*~U zaThfIdh4ub(DvTuz<~3k&F33;j>m7CX0kJ$jNcsBJI(a{<1bC~p{^(ObpCT|@aRG6 zyT$2$>=(-y_~_MOs=mEjzJAD*e-am5hPfavwLi-HJ#BxLX%-wzMM&OY5n?e8Ii{X$ zzbMhvi7w5}R^1825Nkkgp&LQqtRaMqeW%gq{UE&7ujB8Is@m*W^Qc{N$Q^f*Lyoxw zF``9`7?)_KR$-uAj7uI?j7Uxl3RWPycugJu8_8^CxtNSXNbCl*%;;(fCB}T7U<$?v zvl%B?*7swPKR6(FjP91@iv(6!;zY)scbD!~%z3O6FL*uClRQ>x D^0Mt* diff --git a/docs/bookkeeperStream.html b/docs/bookkeeperStream.html index 6d28a4dfd69..05663408969 100644 --- a/docs/bookkeeperStream.html +++ b/docs/bookkeeperStream.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
            • -Project +Project
            • -Wiki +Wiki
            • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
              diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index 8e243d3ad5583efde55f2cd97b72cc9b6a087a47..bfb0a485e14108d1e47da5eee34eb5eef50cfc5b 100644 GIT binary patch delta 183 zcmbP`J|TTV7q^jtk%5_kp`o#{`NpXicm#}e4S>ij#K6$X(Adh@d~+%91V#+e$)fza z7=oLF`0p#nn;JN}7?>EDIysvgnHjm585$Z|SU9=4IGP(98C#k<*(ul%R1(W&XUA1s Zl2}wyQIwj-WoTq%V9KSc>gw;t1pt6jESmrT delta 183 zcmbP`J|TTV7q_9Ikr5CZn;V#IoO*#rz);uFNY}_P#L&pf*xbs%Y;!5^1V#+e$)fza z7=oLF`0p#n8@f3;8@XCo7#h168acT+8#uaJxSE?9x>!0Hn;AM;+9}u&R1(W&XUA1s Yl2}wyQIwj-1$3f;DVM6MtG^o;0I2sZF#rGn diff --git a/docs/index.html b/docs/index.html index faed8001250..bffadd87de6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
              • -Project +Project
              • -Wiki +Wiki
              • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                @@ -221,7 +221,7 @@

                ZooKeeper: Because Coordinating Distributed Systems is a Zoo

                The following documents describe concepts and procedures to get you started using ZooKeeper. If you have more questions, please - ask the mailing list or browse the + ask the mailing list or browse the archives.

                @@ -320,11 +320,11 @@

                ZooKeeper: Because Coordinating Distributed Systems is a Zoo

                diff --git a/docs/index.pdf b/docs/index.pdf index d546053b23412ecb77ebade6c486cdbe4a3007bb..9788b1850dca4619705c48b40d7843c50897d9dd 100644 GIT binary patch delta 1013 zcmb7CO-mb56vdS=Rg_lT6@watf|+~o`@# zcH>XzqGV&~Z|D!vo%jcgHGVKxkUrkxaqd0uo^#$iOaG?FKVJ|KSU^;Yf1O>8P0}I^ zwp|A-qbe)z{~13X5ooqKu_(bG6Bkq8TCKe#X(ydh+>RT&NvYM@>7P!Xyu@zV_Zwf* zz4TGVZ?u}*`-e%hk@%m}ZhJpI@;B=BcVV~^u4Sup-_3us{OIlKfH~7I&7X|Du|WZD zR-sw&{vD7|k-&&_b`GDWl@lcmkj1#r%acHpGr4Zi+G>GW#R6ln0&_M$9Smx9+n0^f zcn|U%qj^}rHweZWq17-64Hl#ZMiTci>Y=9(4?`Gj-u&PXp%BQ(d83F#vQHVvF6otw znFA1!G6%wrgC{Uz5rxx6axD#`pM9|C7(^D%1uFZkiti0dTUKO;7|73te3$3_EJSBq z_F7jy)^OP^vx`V6puyv+8d6wW0uCWsiVRc>m1kSFylU0!9>kr4ubpJuL(-PKVlk{o F-ZiMQ=F$KF delta 998 zcma))ze)o^5XP}IMhprz79PeR2uWu4&+SYI3ZAC1u(8rAYA_+-1q&6Myh7wad#+1U*RUCw0s@M?M9>1I3L{RZszlqa{02=ZcP%S zn({#$?+(GFx3ZjE=CA(44uaK(?79g9`22#iAT#{IG!zO=fll7xXz>?ZJ5DZet;=2! z7!68j#`%|n35^vTp%xlD-z-?PFygx33BYP}ez7$?1}C`C3e%RfFcX3_lluF4UpJi| zp%v)}oj-;2)sSdR$bRB<5iB`vML4pWyMAJFVvj&XA{~QV39C>9VWUN34uQsf?bibW zcH}1pX6tK+#+l?v7dAujP7tMeL{f4>tSM2Z8l}^V!W!4Cbr=zEZ?R=eSF+7DiZHSp ULG>>r>6?XVgPB5MXRns|0D^Jet^fc4 diff --git a/docs/javaExample.html b/docs/javaExample.html index fdf4d87a276..22f3741c6ec 100644 --- a/docs/javaExample.html +++ b/docs/javaExample.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
                • -Project +Project
                • -Wiki +Wiki
                • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                  diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index aa8812de7ba788f7d242049f44bf323dcc1c1694..e7b214a317e19fe011b66deb1f4571ea477aa0cc 100644 GIT binary patch delta 161 zcmeC`VCw2%n$XT|WME`qW?*P&Y+}4|(peq>BV7X^G7B*^vNATeGBnzp&pUw;Lv%7f zzb=O0W*`3e5@#n@3r7fOE)76XEy^^6IVM08$wDZziXBO E0Ie4%>i_@% diff --git a/docs/linkmap.html b/docs/linkmap.html index f636e2b94e5..4e46d1a8e45 100644 --- a/docs/linkmap.html +++ b/docs/linkmap.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
                  • -Project +Project
                  • -Wiki +Wiki
                  • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                    @@ -209,7 +209,7 @@

                    Site Linkmap Table of Contents

                    • -Hadoop  ___________________  site +ZooKeeper  ___________________  site
                      • @@ -380,19 +380,19 @@

                        Site Linkmap Table of Contents

                        • -Wiki  ___________________  wiki +Wiki  ___________________  wiki
                        • -FAQ  ___________________  faq +FAQ  ___________________  faq
                        diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index fd1f3db2728bb2c05e7628b521ad388e5336ce99..a76b53387894037812452d4d0ea71f18af7db8b1 100644 GIT binary patch delta 2280 zcmb7_eLT}^AIA-cW3fp}x8q@9l*WF)J=(B5jAS{UC)HwUhBC`TG0tY`PIqcn^Hi#- zQ&~;xXp!Sk9+T>na_E6fiZVncHD%r1{dMm4$Njpm*Y(HsdS9RS^?iN6|6C`S3Z}St z5eC2j1ONoFII#9upQ$;<4fwhVQ2@aYq)5lfXi6OU~f#VCJAVhKAAt0>T0!B!{i+BpEhB{QqsD z1AW84*(6&ZL2sZr|HGx8NSeLt+_`2iXCIHA2j(LguU2t++j1rQZyq^3x>x(@u6pZ5 zdM&bZ&k|H#aka~=|+aQBU-m%sC5FixbljcRXa z@g!^Z#V)*ec#4z*#Sc0z)je-$bnSoCA2&;9!+AfXsG2mN9#@jKW%k{I&a?*|>o{;l zwzi^Mu!tzeju~5S?O-`P4ByiAhFvmvsXfFtw_*Q;>I|{3Q;zG-+N$rWpPjQfyTW5p z%lx%4VZ`OlOukj0$t1c>t&HVdr(P>eKB^$r4i#Qabug1ePwj-?CEVL}JFk4)+Sb#_ zA#aCY1S=@c#ALG@$}{E5nQ{A#X_<64CSN-x&?fUY3ELQ9z+YmEADmuWv*?9AXNC`$ ztO1N>xs#9y8H3_;o;v^LuO5>Q8u)R_y*9XfE!5W>o)NTZ=iVTf*~=TTxs|6xTDGmf z*{gF;X!6khUDZy&PuBoCCEQ|#(STiVOU#jF;ly-*NN2sQAYhJ?_V|hc$;?;OiuM-- zWqqd9TX^Z6cB0^A?}P~Qe8>t@zwij@YNl&n)uX{rfkTC-S-qkcRLfN#M}OQscy7uN zUM2GoTOjOBsrT;-o=u031*=7L>))7xdzYwD*0GS;wL` zBoC*tBINXQ*W1n&N8WU=xc!YE*FBNC6#x`u3n0z=NKfBB7qQGb_toMTT zT=}go)A>`sje75YLP#Rd6+VUX|Ix(&6Ny+4`v9Q5k($eYLn|09;EK1ef+1=W{1=dv1Ob z)s!7N&V`CUi&dQq@ALgvo87GVd019lc{Zs9rA-@ipi#2R()Eufm83VhzeX3{i6|)w zcWddp-mg*cCo?=hbNznbk!^=%F5+=fC7qdfem0!)*gQyNUwS^X&P(*rcELrIoe;Kn z?79o@QAmx~3~}U|z~CfnPqia2C6S12d?r#cTdeD2jM?I41D+{tc~(i(Vr|4PTR8RA zk!ucq*l(@KjF~*k-=|rr-u=F)2NSAsuDc2nF zLah3&skkftY?7(QQyudM4lactk#m-M1yOpXEvY#w%lOBFs`18rbK^nn$|c@LPV4Iu z$ocXw+IE#eYk^~CyC$Av!V6nnQeZ-oX1)7(jY2`y`W!mVi}{is-ZayFQd#iKu>NI^ zQ2h{osKJdYxSaFJ37LHDy3nC` z{Fs3V*1Et_PXWp<8PPa940iP*`@5n$+{Q6OsOt2BPlB8aJlPwGT+gqv--A! zERJk<5xRE5{|ZCx0b6q7wezbFP_cW}&a&}~2l8bj$+b2?)e7y)R}^cy9weaCG2Pjf zQfIi3r&i=*q>V89N-7M|dBc0j&iClM3M2i_I|VlIQxzA4DTW65TO%n3%%@(QHv73R zkdOB34{t0{9&(gI^zj}NcjP`xURv6A7K_VD|Jw(pyoc;HLV*B?K>+_*2o3`J?Er!R zk*LE!0!eZg#k9f#Se-j84lfCGbJoXUl^i!7L_qpp2Z(rp^qmHfa2`525+3l-$>A~I zyC8tU<8*U);2RVCtrq};(Jey6;lGnX0Eg8rg2U@FJb|F&5{Uoxp3T=Wex0FCcOXCl zFcN*hD-KV^5^)49=tuAmz!JbfD$Wm!@%8uh2M8D(2_R4qUjH+~fKn*Xj7(;FW_CI& R5ekAhfCxn*1409#{|2|Kvn>Ds delta 2277 zcma)-YdF-~9>-^Jk} z%;v9kKY7!7jzsTl9t;wjSeL~#O}tD9GA(K!KAayeU%CKWYZTA54b^$q9f`1!$ z*WT)IZ7=t0snQoMhrHHpZRgm3AX*AiM@L_bJel?%X<3hx7>_pfC|mv`Y;8w@L;jr) zm#jq9Ulv}9!smTf4-S23qdZ+NTZoT!^Nu&K#b!lZziBm)D1DbFo!P)a;+E#9+f4F+ zw4Qr?uTFS*>&1-E>bGRQOn4plYJb2&@Km&#wl}d@=`VhlyD$B~y>>Uw{Y2ISvy-t( z*2NHCy6O3uzw}J!2SUnXHTTyqJ4L&C-})uPYs15$yWG*y)`Yr$!pA>DBkJuU13^T( z#&D$eueGzMx-6!Rf02d@Q$IYr9Xr9yKHhlWnEaKxwd#xA27*2!Bh)Et@2!qg269nPq6J__&9K&<)AAQBzia;TK$UjWu#mGA82!>;d{4eiT3(XTew z$Fy9nj#94yhOClhHNz#7HwD+e_~ffiR0W`?J9A5o#ahVWk71IiVvgT@y|%Av@_ONp zA~aIvSx>DlLn|f!2G>{e-ux$NjF0H63%(z}EhqVBxR86+(_ic#XRs*+wnmLVuUZ`V zsVzMUZQ7iB`wJlY;EySZ7Z&WIm10)aJ%QN`DOa?Ve(J|NQa)c7vQ=dGk~KwG`&gZQ z2j#muq1VT(<@}GK92=wyug*y?#1SqDyUb3yrRoy4TU>E2uE*RVs3C`^Yka3H2R*5O zcc$sp<^{~V@Qn?h+$_CL-lGp>9;)o>HKflrEx+t)&P=G4R4-1$B+S-UbR9Fw~R7O&bH((cOr6N`*8 zbY6RG9d>t#S(UzyXq=QhGn*!MsoaWGJ3mI-VzGo`2)fjZ_{M}5y+oj66y zPE>7ts-nq(`STddo7HoSY*sb2bWWf0?CF7xn}-B7Tmv>Ark=x3tqcKf+&^m+B=zY& z*{JX7dd|UHBj4kaTh&ds5{(lUe|Ect?G&G9R!DRomoyDlX%C6@G7F!;?^TsIy-LO3 zmb*Tr>@uHy9d~!{{K32#ft#(Ye4nqBbg+LZTac;6Vf4Z=Ua^|2C?654Y6Q0jP93{0 zyxE5Sqfy5&twKexc*7$_);lbZ#zOBC0?b6|Ire5EiDdckgDrF=Vx`K9#pjUCn1gw9 zN1m?EWbUZ8UumIgmu=4DuJP=2L3>~9V9e#DwBJwM3jSpC?CtsO+9~q%u@U^U>m#-I zV^Qx>)%D^uG!~2B_3gjbJ{(wUZdfSh~^AebDhXW_)6PitEQCyhakYzOJfu<7!QHW}O!~FZvD`IEYWVsA;f$`Z@Llp?kDRk{ zw|+wRfJv5zPN#Ai6_NNLvbDoo+l4ThHehpzkFVak1eck`t%rKnEI9p8akFWEtid1uM@zmr&1i=UvfXqTDiE; zI}`z3d9}h&O0(h@zbiQRVsSJeOBus0mb9ztx&;=p+F^xb`A=zqTQB5I3KukCa^;9_ zk8KTCjjzCwLQVTN-%K6#|C2#~Po-!!``Gt5Y8W;Zuh;@mC}Xkx}qO z1&@L!;}tvtpzuWCTXZXW5b-!gbZDP>a(3hw diff --git a/docs/recipes.html b/docs/recipes.html index 763d09f43e9..1b0b79b775e 100644 --- a/docs/recipes.html +++ b/docs/recipes.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
                        • -Project +Project
                        • -Wiki +Wiki
                        • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                          diff --git a/docs/recipes.pdf b/docs/recipes.pdf index 763058cebd93c6175e384b3d6888d219df14ec81..fc6b318f5c0e6bcf33ed5aa8e326775dd0d0b00a 100644 GIT binary patch delta 185 zcmX^7iSh6!#t9R+jSP$o%nS?-jg2ig&c4PYV5DmRL}nodhE|5gR>qc_8+j)%Vu(&w zmKksAS$^1YHsdq;ppgSWMN=zmKkqmX<%VwY3OWVU|??K;%H!OVrF1s @@ -61,10 +61,10 @@ +-->
                          • -Project +Project
                          • -Wiki +Wiki
                          • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                            diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 0f3dfa6cafdfd00515cf2cf28fcfd99ccba6982d..dc16b4c77b3795429659cb310a39f0d7377d9214 100644 GIT binary patch delta 189 zcmZqJz}m2ZbwVGvk%5tcnSr69v5CRPnOArOjC2iv$SlOb(8|!*%EVxEE$;+I4AIH5 z{JI!|%~AZ@qxc!!J>rdA4Gml@U5%YwEi9Z3%?wNpOr4x8Tuofe49!d|+??zbYzQie f<+8KmDlSPZDyb++P2;jKv@kK}QdM>JcjE#8gS##d delta 189 zcmZqJz}m2ZbwVGvp`nox5F48tSZp#rO$}Y0oE)8842%rS-OSA`Ee+h9j2z8eEzL{~jm+#6YzQie f<+8KmDlSPZDyb++P2;jKv@kK}QdM>JcjE#8oFgtT diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html index 4bd6343e864..530c96cbd99 100644 --- a/docs/zookeeperAdmin.html +++ b/docs/zookeeperAdmin.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
                            • -Project +Project
                            • -Wiki +Wiki
                            • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                              @@ -475,8 +475,8 @@

                              Clustered (Multi-Server) Setup

                              - - http://hadoop.apache.org/zookeeper/releases.html + + http://zookeeper.apache.org/releases.html

                              diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index b5c2b594aaffd99072034d49104ce492b3ed24a1..aea0a7f4e0fa169ba400aadec13f90740913d335 100644 GIT binary patch delta 5974 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 delta 5960 zcmai13pAEl7w&VbaVzQK(in32UBY`k@B1o35h;x+8ijm?B)248{V#>mWK<@EB85sr zp(0e8E~ddu7fD4b8g!phWXAlbi!|rVTEDd{*8cW6d!J`N``P;|sgOHYA-8?I5=l}N z{G~aPIF@-sc@$|*QsxvnkEE<<&Wa>TV^y|GFl&n5NOR0s>NCOE$0t=+UhV(=!d?3fe!;)C zyF7~@{`Y^q(yH#S(qE?EKJxh9=%zo|^<95GYKboI?Rt9uTJKF>rN8jt?yE}rJ(ZoJ z{^*+DRHIt6$B&(UWc}(Fzn&o~pZ-2_=4h$LvaV3CH7CQrDNxzFOnv7td6S<@ZvG@u zl>M5 zPtq12eD!mXQT9!(3Y)h~kNWGmdFfZ}>P=i`7)*0Z^>=>k{4m6|VRinNDYL8=mODPL zRyMJWy!`jM$1TInwF-S&j#gJh=+_x6*YXKTO5Y)Yo$*4@w3&BqrG+rPqcyT#>wrjm| z(DbveySG;RB#FoQdGR@qDs;l8W~3Z2PSWi6O}l+1)mg4)%C`YcOn+fxw?sL%`-FXk zapg#M;qGhQV~cM*Hd}Y_jqHK9)#B=MGu<7FEmz5XZJTOpVXXJf#4(qom&&3R9NX>a zc4AmAW4AAY?J9O^sI+=QES-P1Bp`HeP5orYHx~!~%-kdwx>3BVn)SS+!>2aEBWk(q zw{H!~SImgc>9>}PdC@7o^s;5b;g|r6Tc>!7UhV!bk8W6`-kA|%=&jA>_c`k)d-UaO zuDF@O&a!`-F(bs{fM3Ox_QY}KG^exiP7>w8^%Um)a(|4i!j;{x)ADwynRM;X+jS%2 z?v-;vZ9mDbG(IwFbF&z7`&PiQ@$!~^ZFgc7zAU-imGe`0>)GPAiAxSQ(LQsnxiPqOd#{^AR@tSt?r^cCXzJabce z*B)sjB(FBDt6A%l<#_AI%-B$?{8GI)t-VrRm&_#P_?zv|Ct2U)wQ5Aqm)E{8iodCU zYwQT8+9%zaqmo<3&fhs!x%aSRW&WgrucqDOV}o~^W=R|z$cf8_ebKr{OxPd3b6BR~ z*7{TH6MogOiGQ%`-m0q`^fg2BU&z*+xOdwl%Kdv%!!p%r=Im^+Ue)HfQ9&f_4VC&QXfNy4ZdRDNU0PV(=DxC{T>gUe{tV4t{Te&_ z#jKNW(a8Ag`lmPM96nh^==vx$7CkFmqp`JkR@1&B7pG3O(fbJbXUhm?idgl)urgus zwd|mr8^dF=n4gu`soYgJj9mFm|SL7_h;ySG{zy11nH9$2%8n7BbjvCT}T(o6pN5@$-~ ztlXjGO*;(ScD7gfIokeZP|7_vlr_|>Ge~n&^Yz}=X`QNAtWx~2$MxsHj`IbEjTcP< zH%A)y-rC+Y`l8X5(_iP@*iKaT6eNmDR)^JARD1HB2{RWQKJWGS)C@(LDZhDOHdEQMyL;uD$6KSFz;xauKhUZ{`LA1iEbLwv8LB%_ZPjm9x`GK?L4PoZjIW&9GxuRxD^LF zMN_d=*+7;)d*}Y46J_qt9J{?`{*1_p5UmxzTYJ=h&H1>pWd>)$GS?o~u~xX*`jbcb zwp)9bOgQ$&sCA|Ly1K74v$p2up2%!6s?9eV;S`e-`p2%ZM)NI`yFChm$4{5*bDNS< z5)$5V{-smF?s|LC%k*`sXZLA?!oCdi=aqSTeQun7acJgQ<%;gqxG5P)hoTLeOS8ht z&CA3i@hv{fD{>cJP1t5OT`Bve-Gl;Tms2{yr(%l&8giQFm-rd2DQb?{s!YuZcFFJD zy)<1nQi#;uY~vJ=Hz{&qRcL_0QWuvI3oFYt7HhX_T`tr+=-^(r=gQGyCC>{9F$v!# zNkaA3o4Lx`nP>*+Yw;sv2!-zsPNgfZTyP|~WHEog>&d3Rtkk>ro^6wan9oZr(j;n4 zgTT_zM;Rr@i=$4Q3^W_a^H%mfINy7EVO^Bb?1oTq`S#?X{*VWMKFvGVnlBIm;8JOI z&PFTk3(nWi5MAZlvJ)4#txXGT)&7r=ytZJ6jYUXM|CGk3i&So>dj~h)Ugf5)G)pPl z#<8eD9KLj1^4Y(tQ=>KZp0`-}(pB33?#UXdcZH67IAWuM?QGLWU6VHqRF!d&QQe+KjbE!pEN_)bnEYtnLl-l@g}HND zinTAchsnXs_)9|+Q%2LTWCXf&1T(40lqPkFcR#wJ zx{B}r0Sr$Fm`z|nnvar1I-VzZ^h1_p*|Zx<5+fO!KRKWzF_p*wKFw4`VkHAl1*pIe z1S0Kw6^W}2Adtf>!vGqwkpe}cHkuH4B27#6-CbOoJ^U-OXd)!R@wiQ(FdI!%==~(i zQ7oDVO*0H?V|n4jS#bz5k`+kIQ6RAd0Stoq0hl!s1h71h0!0D@6cJX71T=#lCIL%h zuLB%|T`153dKQMjk|qU)MEO9GEF}UYYZd^3LTv&~qka_4vzU!!84fuM{U|&zH~2X5 z6JsfcU{DrQfCgwip&vjKgnl%J0z=VgnJB=r|3nbtPw^Ck1%XAzlHnPQ(6Gh?mJH8e zfg$l}35K*{zjzQ~A@dAt5Xc966IZM+79+)I~v`nyZJ|*onC>S)0lLk6sEk?5} zgc)d;S2|8 zl%F(Db7=06+9Vo;<^jeM07y(RXrAN$LA!Sy4gkRDMDqeBBhU@uCqd8xf$1%PV4-xP z1yVo@1B*b2Xk*Z@n=x1g27?;DPQcI-;FFQk0YGtRU|Qe?%RbcMP^oy7#{fV=Ar~@` z9%#}G0ksA5qp$=4K${QZ50wQIP5@Y}n;8POGZF;)VS30A9ErY|A$Y7z3?~RQ+Tn08 zljw_CI4n`98J=WOs2QH(aX%<@NYV_?vV;5(2RoeM!N;LDH1il9ClbR8n6xkg373oa zo5d%=W&{drJ4T>6BpH!l1iYOX0d6SRg-Gkbc#I%m{mxRe%#1dj^I!RvjU6N4sZkpKU5EF zD!{XtAh80##*D}c9F`%x5EBa)LXFgpfEZxv2;j&>2@DAGUu+Z$b>=<(pX&5Zs`0}t zMlQh7Sib`Z42mFJi3V4R&tgEG|HLiv<3ddEC^Mn13Org*sBf4$0&;Me40R4vY7$jZ zK+@Ri3PcX>m7lWtm<-8c6A+O9BCRBcDIp-?Xv6Lku%Qen;{C9DhyM^X9HbX|m?E(i z5>RlLK>aw5L0b$^xcC4Ls?^Zg5B&!y78|{Q6NcJk@OujdD>`)eeeXg)Y=;39zIXxt zUpj{p;R=sl2p{)Zh982^B+hi6#J)PfO%o>^TuLysXy(IC1m8adKTm(!yarY3-2lY+ z0hVk6 JbeQ9)_& @@ -61,10 +61,10 @@ +-->
                @@ -653,7 +653,7 @@

                Reliability

                The ZooKeeper Project

                ZooKeeper has been - + successfully used in many industrial applications. It is used at Yahoo! as the @@ -667,7 +667,7 @@

                The ZooKeeper Project

                All users and developers are encouraged to join the community and contribute their expertise. See the - + Zookeeper Project on Apache for more information. diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index 9fa1f37d072fb9fa9185e22c49352c472609236f..966f8740c32b6131a542c0c367228354743cf231 100644 GIT binary patch delta 856 zcmah`&1(}u6er1esoL0B1A>(iRM67yyf?G+5mZRi2x6fc^x`e7tA@5oXccKeq!cN5 zuq`65Q7rf;h=)ab@#@+CLA(fh(6h5eY`ZBq%a_38K8o8>9NcX!bZ483!JX;p>-GALdL#KdTBz|8OP$V49CzdSy*~Oe zS~$HLO%;-8iv1SD2Z@r0PuQ2CGH{Sw@Wue=!L_-N8<+m5y2ge}$Psoq#f?l>*4Vd0 zc`K8-SL{WuEWI~9yv+yFh#?qdX^7bQe5l9~dwvm3Mk9>#j#R$u6HZG<-@UJ_vmAul zIJ&Uub2>#g(MXfX%OB+3QmVp4I zj~;0#ov8%?t;%Ad)7d^iJ9mK2T7uW?_~m{%n7-BwqyyhK3NYXtE&CKtZH7`7`=+Cu z5q?YiQg{q8gN5KA+o{K7woIpl`|BP2w^pYBRC4AG8_CyTV$xXYs={c2Rg3a!OP`B_P>I3o0mXrmLnu7Zr9~17Z#u^x|zXgV}^Q%OZpXf`lLe z4<>L^A`ipeg#*d#|gjws(fNcZN5XjsYP06B>XI z&psbN33b3a!acx7Xc_NL&k@GjyiPccioSc@h|JeOX|3qe zmiM%R?q(S*EoNCEz29ONDquBz$p94eW49pbOy3ujLu&SYAr#cfvX->jXXlx4&eA83 zOof>1&#&BwJf@t={{ytMrpPmr;59pStze-+7c2~z!t(}v(<|QMsh|FV$w`ZaE~L}W zZ}1?`8BjS)ffKHXh50s}6!wpHaDQb^f-vd1H*BnY!-$qOa5Ic1Y^#Y%n%0DiT1rJx Rh(>F9onQSQg;w6OvELdt)%O4Z diff --git a/docs/zookeeperProgrammers.html b/docs/zookeeperProgrammers.html index e24343e09d7..ee4522de0de 100644 --- a/docs/zookeeperProgrammers.html +++ b/docs/zookeeperProgrammers.html @@ -20,7 +20,7 @@ |breadtrail +-->

                @@ -61,10 +61,10 @@ +-->
                • -Project +Project
                • -Wiki +Wiki
                • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                  @@ -2162,7 +2162,7 @@

                  Gotchas: Common Problems and Troubleshooting

                  -Barrier and +Barrier and Queue Tutorial
                  @@ -2174,7 +2174,7 @@

                  Gotchas: Common Problems and Troubleshooting

                  -ZooKeeper +ZooKeeper - A Reliable, Scalable Distributed Coordination System
                  diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index 87ca3910bacf376f30d9ea9a810d2e10d5ec0ae6..1d54f7d94d922ab3f1b4a6fa3ee8ae450ee09542 100644 GIT binary patch delta 2001 zcmb7F&uddb5a#WxqEV@M6(k7pAeijT{ti;GZ360{A|fIPh8VS2YDw#F=mWh8ilAXd z?;>7AJoKjE!J`NLFT8mXEJBqjG+&APaa_4ol}!*rzW%fB`h}^jqbJiOY`;4Qm1>d zSHIl9(CA)XJilpwE+4KJoTRZ$V+uhtBbXAt0 z>SDE0T0rAvU|7&L-v5?e6~V{N>)AeG!&-ek+az*}YR!JnD!Cw+>0uUfgF3Dbvs&&X z#Aictb|HQ!t#R>dX_Xo55q^UHSA)}Hp9nCxO~!`73o-@k|afO;3_7j7g{fZ9Fi{>69|CCiv9PT9gTfQrQ#(>we`eHu?hA2TX#& zNo0u~rDlFgDu!vzG2_P0D@YLF9Pk6Z;g<=4RpJ3t7HDRSO)i7p%Je+KJk?){a)Sj5 zcKkr+Sg`cYrLiDYS_pclQWg-v{yMMN7zGc1MSAgv7mVb z<5|VjcpmKKcyJjTSnH4|Wb`9=#tdR`MOrax9vmc7KhhRq^oPG_0ZG*?hZ`-WVziYo zAfu2(&5|haj2VTF^*Es9u{ve20LtqbT}lUbYZ_yo*m;&%`SB4eSFQ?dS0JsTP^I}B zQ@=*ef>vC?TJx6B;$i~UPO{>(hmnqo-!BCna0LFw%wOm?U|7Y5nw<97s6^@Ii%0({ z!j*n!ej$F@D(Yw3RzrJM&)PQVLugD%Ax_W2jB+#6tyXKQdhnkvXDU-umF1OAf8~0= RcfLY>RW?*=wbop_@&k4GsUH9U delta 1953 zcma)7ziU)M5a#WRlAKD!!X_X=NeUs!o7tV6-9yvlM-dA_M1r6g^kPKFg)>Ca;uUtH z63}x+!CGu22wtaP;Xhz+VQFD+5aYfm7jGw^2gkfOd^5B2?KktDxBIsq_pd+L2Y?v= zngKVSe%gN!#sJ3<&H~mo*8$($Ik27y@1vo?G2y&BKiHWc%<|XA>Jt+;uU)@3zOb@z zd8skpY+apLTDa0|u1uV7HcvGgD~(pT|1Nv^Kfq4oeKu4-ZtZYU{f3u^Yn25gb|8En zTzK_8TT@(wqnp{NGSHdZ%=W09Jax|e$ZENYkc(TH&yBa?*;Y1^Th`&jRs<7-ua!mL zKBPuf!R@+oebzf_uf=z(+b^Oq(Eb<=>h`w^1mSR|Msm$QOlHwRE-Y47b9h);u;J~t zrSQ1|7T$!tDn39gJFcn-8_p?pPiYEYl=8Xt6prK(!AxN`uMiZLD;p6{aXj5y1?i^<`QFjSTjMP(LD(px^JJ1+=zDaMc1r?k=UWyOq#+L7^@R zQA7g?W;7ac#~xV5>Xg9}&?5=Dv|({-P4Y@KlPIiR?}$|$YcF;Mnj)^@IDb8(V?eTC zv{bM$@=D_35@I!$MRSH-S4ZLYfT}I}?tAweu?zDQKOL!DD7GPdIuUs!fDCc?IiOBn z@-sZ^{ZvudnYean+Iu&p{j_F1HN`xuCx=h`6LO(8Ia#~0y3ks^)oLu(;vH%OwULpT H)3dc-fDxJ` diff --git a/docs/zookeeperQuotas.html b/docs/zookeeperQuotas.html index 57fc36eddd6..567cd696799 100644 --- a/docs/zookeeperQuotas.html +++ b/docs/zookeeperQuotas.html @@ -20,7 +20,7 @@ |breadtrail +--> @@ -61,10 +61,10 @@ +-->
                  • -Project +Project
                  • -Wiki +Wiki
                  • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                    diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index ef8af561ec690ca87cdd72323ff9ee6abe414358..14bc80f618f903be66107794da4dd007abde23f5 100644 GIT binary patch delta 183 zcmewt{x5t&2e*-dk%5_kp`o#{#m33!cm#}e4S>ij#K6$X(Adh@Vsjzy1V#+e$%6d4 z7=oMq_yc9)T}>Q~3?1EEjZBOUoh)6=jLZ$q%}tC<3{1=o%$yx9?G$VXDv9N?v*Ri* ZNh~U=bMWDv9N?v*Ri* ZNh~U @@ -61,10 +61,10 @@ +-->
                    • -Project +Project
                    • -Wiki +Wiki
                    • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                      @@ -264,7 +264,7 @@

                      Pre-requisites

                      Download

                      To get a ZooKeeper distribution, download a recent - + stable release from one of the Apache Download Mirrors.

                      diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 1ab66ef341961de0100463d067b5842aea9aac8a..2c3f3ed1c844b83c4d4cc30b5590760c3dbd55ca 100644 GIT binary patch delta 1681 zcmai!&uUXq5XMQDSEca*ga}qpXq`W2&Y2TRpw<+@MM2!EM7=6hOh~#YZZ5t-;BExB z?p#UU!UwP~;0yQ$p47ClGlG}9xO~jq`OSCc%-7S-m($LCeuaP_0wM?c_W8%vE^R}3 z;}gI>azD`G-zV{cc=%;r}w^BoqbRU>@|tn{(SY~E=3of5*Q6)6lo{*?F|g=r@G&$xCjkC zOC$O?wJ<0tu#Hm=mJDKsKXJ{vJ5a%3le2nFg&lOx9)>hHz~bX2z@`XD3bs20OqLpf z^-}UO7erkg10~Z4G6c1n#N60UC}echOzzx~Ir<_}rsZr@E@lQfdT7bCf@#))DMZ(a z3rG^(%_&+wFTASTKuKmJ z7EQ=&39PL|$-70GT^s{AR~dhX>Vf(U((p delta 1603 zcmai!J!=$U5XZUeTwkIb+Spu*2o@LT<(cQ@2nV@b(pU&0SQ$CZ#tQ-OxTFx9CEws= zODn<7DqQW?FqMU$!q!L0ZE!|#VPTp5@6K;#{_}jkXnnb8Ef&`iiSo?>@%-br8@I8C z)FXU`)aTGgT%O%rv`n;KU+bCRa)0e{`~GBnFrCge$FuS4Nx3LaP%(txItgyVX<*Q5@B0hn>o#r|3)!z@$QopvytPs4)>Z zX-ou3m%6G!LI!q)LmY+*vuiAm5n2SaK)z210Vh&3Tb@UcFh{KF-Y>=1;b>0@NJ zNX{GdydlOV(XSk~-?)TCoN0IrPA9<_qy8b0Lk&pc^V)tovb&kptPM^iWMlcjQd6Y7 z9CnHcjE(^>bytg!=Rn%b#F<7XVHN|!E|?Noj7N#RYR_|keDs>rDDRM^c7VFyxwWfP z=4w_hmr`Fl-g=4AV+grVDbe0&=pY2x5%$}(GaBs @@ -61,10 +61,10 @@ +-->
                      • -Project +Project
                      • -Wiki +Wiki
                      • ZooKeeper 3.4 Documentation @@ -174,13 +174,13 @@
                        diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index 86fed361504a564c21f85533a8161f51c9f9d186..804818597b4eb8978b7c977aad1db23cc9614d99 100644 GIT binary patch delta 161 zcmZ4Sj&a31#tHMdjSP$o%nS?-jZKU;u6)8HV5DmRL}nodhE|5gRwhQ9=kiWq#1Nfq z%dd+exVehoKi%2b!pzdh)xyBQ(A3Pp#nRN&)xg-y(ZtBb(#gWu*}~LL!G@5M$vovU E0DD;{%>V!Z delta 161 zcmZ4Sj&a31#tHMd4GoQqfY{jF&|u@rCp-d%x`sx&Mus7VMpnk=R)z+f=kiWq#1Nfq z%dd+exVehoKi%2V*vQh{#nI5x$k5!)$j!{c*wxI)*vZ-4&Ct@s$ - + ZooKeeper diff --git a/src/c/README b/src/c/README index 397d04c0d04..7c4e3b8e68c 100644 --- a/src/c/README +++ b/src/c/README @@ -4,9 +4,9 @@ This package provides a C client interface to Zookeeper server. For the latest information about ZooKeeper, please visit our website at: - http://hadoop.apache.org/zookeeper/ + http://zookeeper.apache.org/ and our wiki, at: - http://wiki.apache.org/hadoop/ZooKeeper + https://cwiki.apache.org/confluence/display/ZOOKEEPER Full documentation for this release can also be found in ../../docs/index.html diff --git a/src/c/configure.ac b/src/c/configure.ac index 94ff7c6e89c..31b689aca65 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.2,[zookeeper-user@hadoop.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.2,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index b89843c8829..a68dcc6287e 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -111,7 +111,7 @@ #define PACKAGE "c-client-src" /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "zookeeper-user@hadoop.apache.org" +#define PACKAGE_BUGREPORT "user@zookeeper.apache.org" /* Define to the full name of this package. */ #define PACKAGE_NAME "zookeeper C client" diff --git a/src/contrib/huebrowser/README b/src/contrib/huebrowser/README index ef921f5aed0..c03ea90fdd7 100644 --- a/src/contrib/huebrowser/README +++ b/src/contrib/huebrowser/README @@ -56,7 +56,7 @@ Hue is both a web UI for Hadoop and a framework to create interactive web applic What is ZooKeeper? ------------------ -http://hadoop.apache.org/zookeeper/ +http://zookeeper.apache.org/ ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed. diff --git a/src/contrib/huebrowser/zkui/setup.py b/src/contrib/huebrowser/zkui/setup.py index d2ae009123d..68d1352ca2c 100644 --- a/src/contrib/huebrowser/zkui/setup.py +++ b/src/contrib/huebrowser/zkui/setup.py @@ -28,7 +28,7 @@ def expand_package_data(src_dirs, strip=""): setup( name = "zkui", version = "0.1", - url = 'http://hadoop.apache.org/zookeeper/', + url = 'http://zookeeper.apache.org/', description = 'ZooKeeper Browser', packages = find_packages('src'), package_dir = {'': 'src'}, diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako index aa721695aca..c74c2020901 100644 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako +++ b/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako @@ -69,7 +69,7 @@ ${shared.header("ZooKeeper Browser > Tree > %s > %s" % (cluster['nice_name'], pa

    -Details on stat information. +Details on stat information. ${shared.footer()} diff --git a/src/contrib/loggraph/ivy.xml b/src/contrib/loggraph/ivy.xml index ee69011bfb7..d6fa9d6d759 100644 --- a/src/contrib/loggraph/ivy.xml +++ b/src/contrib/loggraph/ivy.xml @@ -21,7 +21,7 @@ - + ZooKeeper Graphing diff --git a/src/contrib/monitoring/README b/src/contrib/monitoring/README index c88a9d86dbc..d48e2ce26cf 100644 --- a/src/contrib/monitoring/README +++ b/src/contrib/monitoring/README @@ -80,5 +80,5 @@ Apache License 2.0 or later. ZooKeeper 4letterwords Commands ------------------------------- -http://hadoop.apache.org/zookeeper/docs/current/zookeeperAdmin.html#sc_zkCommands +http://zookeeper.apache.org/docs/current/zookeeperAdmin.html#sc_zkCommands diff --git a/src/contrib/rest/ivy.xml b/src/contrib/rest/ivy.xml index f4ee8ba0a8f..903d3903e87 100644 --- a/src/contrib/rest/ivy.xml +++ b/src/contrib/rest/ivy.xml @@ -21,7 +21,7 @@ - + ZooKeeper REST diff --git a/src/contrib/zkperl/README b/src/contrib/zkperl/README index 520e62468fc..bbe2a0d8f3b 100644 --- a/src/contrib/zkperl/README +++ b/src/contrib/zkperl/README @@ -3,10 +3,10 @@ Net::ZooKeeper - Perl extension for Apache ZooKeeper Net::ZooKeeper provides a Perl interface to the synchronous C API of Apache ZooKeeper. ZooKeeper is coordination service for -distributed applications and is a sub-project of the Apache Hadoop -project. For details see the ZooKeeper home page at: +distributed applications. +For details see the ZooKeeper home page at: -http://hadoop.apache.org/zookeeper/ +http://zookeeper.apache.org/ INSTALLATION diff --git a/src/contrib/zkperl/ZooKeeper.pm b/src/contrib/zkperl/ZooKeeper.pm index 2440603b558..507f0298d16 100644 --- a/src/contrib/zkperl/ZooKeeper.pm +++ b/src/contrib/zkperl/ZooKeeper.pm @@ -169,8 +169,7 @@ Net::ZooKeeper - Perl extension for Apache ZooKeeper Net::ZooKeeper provides a Perl interface to the synchronous C API of Apache ZooKeeper. ZooKeeper is coordination service for -distributed applications and is a sub-project of the Apache Hadoop -project. +distributed applications. Each connection to ZooKeeper is represented as a handle object of the class Net::ZooKeeper, similar to the manner in which database @@ -1229,7 +1228,7 @@ Everything from all of the above tagsets. =head1 SEE ALSO The Apache ZooKeeper project's home page at -L provides a wealth of detail +L provides a wealth of detail on how to develop applications using ZooKeeper. =head1 AUTHOR diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html index eed820a9421..2bd9d389fc8 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html @@ -13,9 +13,9 @@ href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/org/documents/epl-v10.php]

    ZooKeeper is available from http://hadoop.apache.org/zookeeper/ + href="http://zookeeper.apache.org/">http://zookeeper.apache.org/ and is licensed under an Apache Software Licence v2.0

    The ApacheSoftware Licence v2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0

    - \ No newline at end of file + diff --git a/src/docs/src/documentation/content/xdocs/bookkeeperStarted.xml b/src/docs/src/documentation/content/xdocs/bookkeeperStarted.xml index e65e00a8b18..74f6f7edce6 100644 --- a/src/docs/src/documentation/content/xdocs/bookkeeperStarted.xml +++ b/src/docs/src/documentation/content/xdocs/bookkeeperStarted.xml @@ -61,7 +61,7 @@ Download BookKeeper is distributed along with ZooKeeper. To get a ZooKeeper distribution, download a recent - + stable release from one of the Apache Download Mirrors.
    diff --git a/src/docs/src/documentation/content/xdocs/site.xml b/src/docs/src/documentation/content/xdocs/site.xml index 34ac4b3b8b6..e49d92cf63e 100644 --- a/src/docs/src/documentation/content/xdocs/site.xml +++ b/src/docs/src/documentation/content/xdocs/site.xml @@ -29,7 +29,7 @@ This file contains an outline of the site's information content. It is used to: See http://forrest.apache.org/docs/linking.html for more info. --> - + @@ -74,14 +74,14 @@ See http://forrest.apache.org/docs/linking.html for more info. - - - + + + - - - + + + @@ -93,10 +93,8 @@ See http://forrest.apache.org/docs/linking.html for more info. - - - - + + diff --git a/src/docs/src/documentation/content/xdocs/tabs.xml b/src/docs/src/documentation/content/xdocs/tabs.xml index 739ebf2b6c8..aef7e59b083 100644 --- a/src/docs/src/documentation/content/xdocs/tabs.xml +++ b/src/docs/src/documentation/content/xdocs/tabs.xml @@ -29,8 +29,8 @@ directory (ends in '/'), in which case /index.html will be added --> - - + + diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 85d2b8bda6a..48c1f08908e 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -152,8 +152,8 @@ from: - - http://hadoop.apache.org/zookeeper/releases.html + + http://zookeeper.apache.org/releases.html diff --git a/src/docs/src/documentation/content/xdocs/zookeeperOver.xml b/src/docs/src/documentation/content/xdocs/zookeeperOver.xml index 5d2eda6c7cd..7a0444c5908 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperOver.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperOver.xml @@ -375,7 +375,7 @@ In version 3.2 r/w performance improved by ~2x compared to the previous + url="http://zookeeper.apache.org/docs/r3.1.1/zookeeperOver.html#Performance">previous 3.1 release. Benchmarks also indicate that it is reliable, too. The ZooKeeper Project ZooKeeper has been - + successfully used in many industrial applications. It is used at Yahoo! as the @@ -454,7 +454,7 @@ All users and developers are encouraged to join the community and contribute their expertise. See the - + Zookeeper Project on Apache for more information. diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml index bfc23dc4a3f..27b25110b4a 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml @@ -1587,7 +1587,7 @@ authProvider.2=com.f.MyAuth2 Barrier and + url="https://cwiki.apache.org/confluence/display/ZOOKEEPER/Tutorial">Barrier and Queue Tutorial @@ -1598,7 +1598,7 @@ authProvider.2=com.f.MyAuth2 ZooKeeper + url="https://cwiki.apache.org/confluence/display/ZOOKEEPER/ZooKeeperArticles">ZooKeeper - A Reliable, Scalable Distributed Coordination System diff --git a/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml b/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml index 482f36cb002..cd20b63fb75 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperStarted.xml @@ -69,7 +69,7 @@ Download To get a ZooKeeper distribution, download a recent - + stable release from one of the Apache Download Mirrors. diff --git a/src/docs/src/documentation/content/xdocs/zookeeperTutorial.xml b/src/docs/src/documentation/content/xdocs/zookeeperTutorial.xml index a155a9b3e64..10b3606c842 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperTutorial.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperTutorial.xml @@ -672,4 +672,4 @@ public class SyncPrimitive implements Watcher { - \ No newline at end of file + diff --git a/src/docs/src/documentation/skinconf.xml b/src/docs/src/documentation/skinconf.xml index 6aee6265acb..91417d81251 100644 --- a/src/docs/src/documentation/skinconf.xml +++ b/src/docs/src/documentation/skinconf.xml @@ -30,7 +30,7 @@ which will be used to configure the chosen Forrest skin. In other words google will search the @domain for the query string. --> - + true @@ -66,7 +66,7 @@ which will be used to configure the chosen Forrest skin. ZooKeeper ZooKeeper: distributed coordination - http://hadoop.apache.org/zookeeper/ + http://zookeeper.apache.org/ images/zookeeper_small.gif @@ -95,10 +95,10 @@ which will be used to configure the chosen Forrest skin. Use location="none" to not display the trail (if the skin supports it). For some skins just set the attributes to blank. --> - + - - + + " + + toClose2.toString()); + byte[] buf = new byte[1024]; + try { + while (true) { + try { + int read = this.in.read(buf); + if (read > 0) { + try { + this.out.write(buf, 0, read); + } catch (IOException e) { + LOG.warn("exception during write", e); + try { + toClose.close(); + } catch (IOException ex) { + // ignore + } + try { + toClose2.close(); + } catch (IOException ex) { + // ignore + } + break; + } + } + } catch (SocketTimeoutException e) { + LOG.error("socket timeout", e); + } + Thread.sleep(1); + } + } catch (InterruptedException e) { + LOG.warn("Interrupted", e); + try { + toClose.close(); + } catch (IOException ex) { + // ignore + } + try { + toClose2.close(); + } catch (IOException ex) { + // ignore silently + } + } catch (SocketException e) { + if (!"Socket closed".equals(e.getMessage())) { + LOG.error("Unexpected exception", e); + } + } catch (IOException e) { + LOG.error("Unexpected exception", e); + } + LOG.info("Shutting down forward for " + toClose); + } + + } + + private volatile boolean stopped = false; + private ExecutorService workers = Executors.newCachedThreadPool(); + private ServerSocket serverSocket; + private final int to; + + public PortForwarder(int from, int to) throws IOException { + this.to = to; + serverSocket = new ServerSocket(from); + serverSocket.setSoTimeout(30000); + this.start(); + } + + @Override + public void run() { + try { + while (!stopped) { + Socket sock = null; + try { + LOG.info("accepting socket local:" + + serverSocket.getLocalPort() + " to:" + to); + sock = serverSocket.accept(); + LOG.info("accepted: local:" + sock.getLocalPort() + + " from:" + sock.getPort() + + " to:" + to); + Socket target = null; + int retry = 10; + while(sock.isConnected()) { + try { + target = new Socket("localhost", to); + break; + } catch (IOException e) { + if (retry == 0) { + throw e; + } + LOG.warn("connection failed, retrying(" + retry + + "): local:" + sock.getLocalPort() + + " from:" + sock.getPort() + + " to:" + to, e); + } + Thread.sleep(TimeUnit.SECONDS.toMillis(1)); + retry--; + } + LOG.info("connected: local:" + sock.getLocalPort() + + " from:" + sock.getPort() + + " to:" + to); + sock.setSoTimeout(30000); + target.setSoTimeout(30000); + this.workers.execute(new PortForwardWorker(sock, target, + sock.getInputStream(), target.getOutputStream())); + this.workers.execute(new PortForwardWorker(target, sock, + target.getInputStream(), sock.getOutputStream())); + } catch (SocketTimeoutException e) { + LOG.warn("socket timed out local:" + sock.getLocalPort() + + " from:" + sock.getPort() + + " to:" + to, e); + } catch (ConnectException e) { + LOG.warn("connection exception local:" + sock.getLocalPort() + + " from:" + sock.getPort() + + " to:" + to, e); + sock.close(); + } catch (IOException e) { + if (!"Socket closed".equals(e.getMessage())) { + LOG.warn("unexpected exception local:" + sock.getLocalPort() + + " from:" + sock.getPort() + + " to:" + to, e); + throw e; + } + } + + } + } catch (IOException e) { + LOG.error("Unexpected exception to:" + to, e); + } catch (InterruptedException e) { + LOG.error("Interrupted to:" + to, e); + } + } + + public void shutdown() throws Exception { + this.stopped = true; + this.serverSocket.close(); + this.workers.shutdownNow(); + try { + if (!this.workers.awaitTermination(5, TimeUnit.SECONDS)) { + throw new Exception( + "Failed to stop forwarding within 5 seconds"); + } + } catch (InterruptedException e) { + throw new Exception("Failed to stop forwarding"); + } + this.join(); + } +} diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index 4800ce54c4d..bb1fdf8a33b 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -23,8 +23,9 @@ import java.net.InetSocketAddress; import java.util.HashMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import junit.framework.Assert; + +import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; @@ -33,15 +34,22 @@ import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.server.persistence.FileTxnLog; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.txn.SetDataTxn; +import org.apache.zookeeper.txn.TxnHeader; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class TruncateTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(TruncateTest.class); @@ -69,12 +77,67 @@ public void process(WatchedEvent event) { connected = event.getState() == Watcher.Event.KeeperState.SyncConnected; } }; - + + @Test + public void testTruncationStreamReset() throws Exception { + File tmpdir = ClientBase.createTmpDir(); + FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpdir, tmpdir); + ZKDatabase zkdb = new ZKDatabase(snaplog); + + for (int i = 1; i <= 100; i++) { + append(zkdb, i); + } + + zkdb.truncateLog(1); + + append(zkdb, 200); + + zkdb.close(); + + // verify that the truncation and subsequent append were processed + // correctly + FileTxnLog txnlog = new FileTxnLog(new File(tmpdir, "version-2")); + TxnIterator iter = txnlog.read(1); + + TxnHeader hdr = iter.getHeader(); + Record txn = iter.getTxn(); + Assert.assertEquals(1, hdr.getZxid()); + Assert.assertTrue(txn instanceof SetDataTxn); + + iter.next(); + + hdr = iter.getHeader(); + txn = iter.getTxn(); + Assert.assertEquals(200, hdr.getZxid()); + Assert.assertTrue(txn instanceof SetDataTxn); + } + + private void append(ZKDatabase zkdb, int i) throws IOException { + TxnHeader hdr = new TxnHeader(1, 1, i, 1, ZooDefs.OpCode.setData); + Record txn = new SetDataTxn("/foo" + i, new byte[0], 1); + Request req = new Request(null, 0, 0, 0, null, null); + req.hdr = hdr; + req.txn = txn; + + zkdb.append(req); + zkdb.commit(); + } + @Test public void testTruncate() throws IOException, InterruptedException, KeeperException { // Prime the server that is going to come in late with 50 txns - ServerCnxnFactory factory = ClientBase.createNewServerInstance(dataDir1, null, "127.0.0.1:" + baseHostPort, 100); - ZooKeeper zk = new ZooKeeper("127.0.0.1:" + baseHostPort, 15000, nullWatcher); + String hostPort = "127.0.0.1:" + baseHostPort; + ServerCnxnFactory factory = ClientBase.createNewServerInstance(dataDir1, null, hostPort, 100); + ClientBase.shutdownServerInstance(factory, hostPort); + + // standalone starts with 0 epoch while quorum starts with 1 + File origfile = new File(new File(dataDir1, "version-2"), "snapshot.0"); + File newfile = new File(new File(dataDir1, "version-2"), "snapshot.100000000"); + origfile.renameTo(newfile); + + factory = ClientBase.createNewServerInstance(dataDir1, null, hostPort, 100); + + ZooKeeper zk = new ZooKeeper(hostPort, 15000, nullWatcher); for(int i = 0; i < 50; i++) { zk.create("/" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } @@ -137,11 +200,13 @@ public void testTruncate() throws IOException, InterruptedException, KeeperExcep } zk1.getData("/9", false, new Stat()); try { - // 10 wont work because the session expiration - // will match the zxid for 10 and so we wont - // actually truncate the zxid for 10 creation - // but for 11 we will for sure - zk1.getData("/11", false, new Stat()); + // /10 wont work because the session expiration + // will match the zxid for /10 and so we wont + // actually truncate the zxid for /10 creation + // due to an artifact of switching the xid of the standalone + // /11 is the last entry in the log for the xid + // as a result /12 is the first of the truncated znodes to check for + zk1.getData("/12", false, new Stat()); Assert.fail("Should have gotten an error"); } catch(KeeperException.NoNodeException e) { // this is what we want From 10d8f6e3e26de0549defdb22d67ca4231811e543 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Sun, 29 Jul 2012 05:04:27 +0000 Subject: [PATCH 095/444] ZOOKEEPER-1521. LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1366782 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/server/quorum/Leader.java | 6 ++++-- .../org/apache/zookeeper/server/quorum/LearnerHandler.java | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index eac7df3f529..6b87e66861f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -82,6 +82,9 @@ BUGFIXES: ZOOKEEPER-1489. Data loss after truncate on transaction log (phunt) + ZOOKEEPER-1521. LearnerHandler initLimit/syncLimit problems + specifying follower socket timeout limits (phunt) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index ac283a2c9ce..92b896a1e35 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -250,8 +250,10 @@ public void run() { try { while (!stop) { try{ - Socket s = ss.accept(); - s.setSoTimeout(self.tickTime * self.syncLimit); + Socket s = ss.accept(); + // start with the initLimit, once the ack is processed + // in LearnerHandler switch to the syncLimit + s.setSoTimeout(self.tickTime * self.initLimit); s.setTcpNoDelay(nodelay); LearnerHandler fh = new LearnerHandler(s, Leader.this); fh.start(); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index cdc8fb31ecd..2c55b371ae7 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -233,7 +233,6 @@ static public String packetToString(QuorumPacket p) { @Override public void run() { try { - sock.setSoTimeout(leader.self.getTickTime()*leader.self.getInitLimit()); ia = BinaryInputArchive.getArchive(new BufferedInputStream(sock .getInputStream())); bufferedOutput = new BufferedOutputStream(sock.getOutputStream()); @@ -455,6 +454,9 @@ public void run() { } leader.processAck(this.sid, qp.getZxid(), sock.getLocalSocketAddress()); + // now that the ack has been processed expect the syncLimit + sock.setSoTimeout(leader.self.tickTime * leader.self.syncLimit); + /* * Wait until leader starts up */ @@ -469,7 +471,6 @@ public void run() { // queuedPackets.add(new QuorumPacket(Leader.UPTODATE, -1, null, null)); - while (true) { qp = new QuorumPacket(); ia.readRecord(qp, "packet"); From d3923877494182b42d6ba677713a800b8ec84a93 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Sun, 29 Jul 2012 05:32:17 +0000 Subject: [PATCH 096/444] ZOOKEEPER-1493. C Client: zookeeper_process doesn't invoke completion callback if zookeeper_close has been called (Michi Mutsuzaki via phunt and mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1366785 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++++ src/c/src/zookeeper.c | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6b87e66861f..87e93d08460 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,10 @@ BUGFIXES: ZOOKEEPER-1521. LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits (phunt) + ZOOKEEPER-1493. C Client: zookeeper_process doesn't invoke + completion callback if zookeeper_close has been called + (Michi Mutsuzaki via phunt and mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index f117b18b83e..be8165e9966 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -2224,11 +2224,8 @@ int zookeeper_process(zhandle_t *zh, int events) completion_list_t *cptr = dequeue_completion(&zh->sent_requests); /* [ZOOKEEPER-804] Don't assert if zookeeper_close has been called. */ - if (zh->close_requested == 1) { - if (cptr) { - destroy_completion_entry(cptr); - cptr = NULL; - } + if (zh->close_requested == 1 && cptr == NULL) { + LOG_DEBUG(("Completion queue has been cleared by zookeeper_close()")); close_buffer_iarchive(&ia); return api_epilog(zh,ZINVALIDSTATE); } From 63542a1390b8e13039187492fea301cc8c2e8bd4 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 1 Aug 2012 15:55:58 +0000 Subject: [PATCH 097/444] ZOOKEEPER-1522. intermittent failures in Zab test due to NPE in recursiveDelete test function (phunt via flavio) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1368077 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../zookeeper/server/quorum/Zab1_0Test.java | 40 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 87e93d08460..ea7862ee10c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -89,6 +89,9 @@ BUGFIXES: completion callback if zookeeper_close has been called (Michi Mutsuzaki via phunt and mahadev) + ZOOKEEPER-1522. intermittent failures in Zab test due to NPE in + recursiveDelete test function (phunt via flavio) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 1ccde2770eb..60b3484bcb9 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -48,8 +48,6 @@ import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; -import org.apache.zookeeper.server.quorum.Leader; -import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.util.ZxidUtils; @@ -59,8 +57,12 @@ import org.apache.zookeeper.txn.TxnHeader; import org.junit.Assert; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Zab1_0Test { + private static final Logger LOG = LoggerFactory.getLogger(Zab1_0Test.class); + private static final class LeadThread extends Thread { private final Leader leader; @@ -71,8 +73,10 @@ private LeadThread(Leader leader) { public void run() { try { leader.lead(); + } catch (InterruptedException e) { + LOG.info("Leader thread interrupted", e); } catch (Exception e) { - e.printStackTrace(); + LOG.warn("Unexpected exception in leader thread", e); } finally { leader.shutdown("lead ended"); } @@ -159,10 +163,10 @@ public void testLeaderInConnectingFollowers() throws Exception { Assert.fail("leader timed out in getEpochToPropose"); } } finally { - recursiveDelete(tmpDir); if (leader != null) { leader.shutdown("end of test"); } + recursiveDelete(tmpDir); } } @@ -207,10 +211,14 @@ public void testLastAcceptedEpoch() throws Exception { } } finally { - recursiveDelete(tmpDir); if (leader != null) { leader.shutdown("end of test"); } + if (leadThread != null) { + leadThread.interrupt(); + leadThread.join(); + } + recursiveDelete(tmpDir); } } @@ -244,10 +252,10 @@ public void testLeaderInElectingFollowers() throws Exception { Assert.assertTrue(f1.msg + " without waiting for leader", f1.msg == null); Assert.assertTrue(f2.msg + " without waiting for leader", f2.msg == null); } finally { - recursiveDelete(tmpDir); if (leader != null) { leader.shutdown("end of test"); } + recursiveDelete(tmpDir); } } @@ -346,7 +354,6 @@ public void testLeaderConversation(LeaderConversation conversation) throws Excep conversation.converseWithLeader(ia, oa, leader); } finally { - recursiveDelete(tmpDir); if (leader != null) { leader.shutdown("end of test"); } @@ -354,6 +361,7 @@ public void testLeaderConversation(LeaderConversation conversation) throws Excep leadThread.interrupt(); leadThread.join(); } + recursiveDelete(tmpDir); } } @@ -415,7 +423,6 @@ public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversa conversation.converseWithLeader(ia, oa, leader, zxid); } finally { - recursiveDelete(tmpDir); if (leader != null) { leader.shutdown("end of test"); } @@ -423,6 +430,7 @@ public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversa leadThread.interrupt(); leadThread.join(); } + recursiveDelete(tmpDir); } } @@ -448,8 +456,10 @@ public void testFollowerConversation(FollowerConversation conversation) throws E public void run() { try { followerForThread.followLeader(); - } catch(Exception e) { - e.printStackTrace(); + } catch (InterruptedException e) { + LOG.info("Follower thread interrupted", e); + } catch (Exception e) { + LOG.warn("Unexpected exception in follower thread", e); } } }; @@ -749,7 +759,7 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, // Make sure the data was recorded in the filesystem ok ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); zkDb2.loadDataBase(); - System.out.println(zkDb2.getSessions()); + LOG.info("zkdb2 sessions:" + zkDb2.getSessions()); Assert.assertNotNull(zkDb2.getSessionWithTimeOuts().get(4L)); } finally { recursiveDelete(tmpDir); @@ -913,8 +923,12 @@ private void recursiveDelete(File file) { if (file.isFile()) { file.delete(); } else { - for(File c: file.listFiles()) { - recursiveDelete(c); + // might return null if deleted out from under us... + File[] files = file.listFiles(); + if (files != null) { + for(File c: files) { + recursiveDelete(c); + } } file.delete(); } From 5ed71e34307d4cd763058a3c9a7302c8650ef437 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 1 Aug 2012 19:22:55 +0000 Subject: [PATCH 098/444] ZOOKEEPER-1503. remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest (Eugene Koontz via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1368205 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../apache/zookeeper/test/SaslAuthFailTest.java | 16 ---------------- .../org/apache/zookeeper/test/SaslAuthTest.java | 16 ---------------- 3 files changed, 3 insertions(+), 32 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ea7862ee10c..a6b59b8439d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -103,6 +103,9 @@ IMPROVEMENTS: ZOOKEEPER-1454. Document how to run autoreconf if cppunit is installed in a non-standard directory (Michi Mutsuzaki via phunt) + ZOOKEEPER-1503. remove redundant JAAS configuration code in SaslAuthTest and + SaslAuthFailTest (Eugene Koontz via phunt) + Release 3.4.3 - 2012-02-06 Backward compatible changes: diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java index 79f0b5035c3..7ceeef0d281 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java @@ -66,22 +66,6 @@ public class SaslAuthFailTest extends ClientBase { protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { - File tmpDir = ClientBase.createTmpDir(); - File saslConfFile = new File(tmpDir, "jaas_bad_password.conf"); - FileWriter fwriter = new FileWriter(saslConfFile); - - fwriter.write("" + - "Server {\n" + - " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + - " user_super=\"test\";\n" + - "};\n" + - "Client {\n" + - " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + - " username=\"super\"\n" + - " password=\"test1\";\n" + // NOTE: wrong password to cause authentication failure : 'test' != 'test1'. - "};" + "\n"); - fwriter.close(); - System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); MyWatcher watcher = new MyWatcher(); return createClient(watcher, hp); } diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java index 39955862d31..d82e3836836 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java @@ -70,22 +70,6 @@ public class SaslAuthTest extends ClientBase { protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { - File tmpDir = ClientBase.createTmpDir(); - File saslConfFile = new File(tmpDir, "jaas.conf"); - FileWriter fwriter = new FileWriter(saslConfFile); - - fwriter.write("" + - "Server {\n" + - " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + - " user_super=\"test\";\n" + - "};\n" + - "Client {\n" + - " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + - " username=\"super\"\n" + - " password=\"test\";\n" + - "};" + "\n"); - fwriter.close(); - System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); MyWatcher watcher = new MyWatcher(); return createClient(watcher, hp); } From a9d0ec71682811ececc47c776b8cb1144cb21e15 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 1 Aug 2012 21:39:22 +0000 Subject: [PATCH 099/444] ZOOKEEPER-1510. Should not log SASL errors for non-secure usage (Todd Lipcon via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1368270 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/ClientCnxn.java | 10 +++++++--- .../zookeeper/client/ZooKeeperSaslClient.java | 17 ++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a6b59b8439d..a22dfe6f2a4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -106,6 +106,9 @@ IMPROVEMENTS: ZOOKEEPER-1503. remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest (Eugene Koontz via phunt) + ZOOKEEPER-1510. Should not log SASL errors for non-secure usage + (Todd Lipcon via phunt) + Release 3.4.3 - 2012-02-06 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 425ce833896..2fb591878e6 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -934,19 +934,23 @@ private void startConnect() throws IOException { addr = hostProvider.next(1000); } - LOG.info("Opening socket connection to server " + addr); - setName(getName().replaceAll("\\(.*\\)", "(" + addr.getHostName() + ":" + addr.getPort() + ")")); try { zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); } catch (LoginException e) { - LOG.warn("SASL authentication failed: " + e + " Will continue connection to Zookeeper server without " + LOG.warn("SASL configuration failed: " + e + " Will continue connection to Zookeeper server without " + "SASL authentication, if Zookeeper server allows it."); eventThread.queueEvent(new WatchedEvent( Watcher.Event.EventType.None, Watcher.Event.KeeperState.AuthFailed, null)); } + String log = "Opening socket connection to server " + addr; + if (zooKeeperSaslClient != null) { + log += ". " + zooKeeperSaslClient.getConfigStatus(); + } + LOG.info(log); + clientCnxnSocket.connect(addr); } diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 18f18e3b2f6..340db4887db 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -67,6 +67,8 @@ public enum SaslState { private SaslState saslState = SaslState.INITIAL; + private final String configStatus; + public SaslState getSaslState() { return saslState; } @@ -95,7 +97,7 @@ public ZooKeeperSaslClient(final String serverPrincipal) securityException = e; } if (entries != null) { - LOG.info("Found Login Context section '" + clientSection + "': will use it to attempt to SASL-authenticate."); + this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'"; this.saslClient = createSaslClient(serverPrincipal, clientSection); } else { // Handle situation of clientSection's being null: it might simply because the client does not intend to @@ -119,12 +121,13 @@ public ZooKeeperSaslClient(final String serverPrincipal) } else { // The user did not override the default context. It might be that they just don't intend to use SASL, // so log at INFO, not WARN, since they don't expect any SASL-related information. + String msg = "Will not attempt to authenticate using SASL "; if (securityException != null) { - LOG.warn("SecurityException: " + securityException + " occurred when trying to find JAAS configuration."); + msg += "(" + securityException.getLocalizedMessage() + ")"; + } else { + msg += "(unknown error)"; } - LOG.info("Client will not SASL-authenticate because the default JAAS configuration section 'Client' " + - "could not be found. If you are not using SASL, you may ignore this. On the other hand, " + - "if you expected SASL to work, please fix your JAAS configuration."); + this.configStatus = msg; } if (System.getProperty("java.security.auth.login.config") != null) { // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed. @@ -144,6 +147,10 @@ public ZooKeeperSaslClient(final String serverPrincipal) } } + public String getConfigStatus() { + return configStatus; + } + public boolean isComplete() { return (saslState == SaslState.COMPLETE); } From 0a080a18d4d827dbc72b324b68c34374c370110d Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 1 Aug 2012 22:25:03 +0000 Subject: [PATCH 100/444] ZOOKEEPER-1510. Should not log SASL errors for non-secure usage (Todd Lipcon via phunt) Missed a bit in the prior commit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1368301 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/main/org/apache/zookeeper/ClientCnxn.java | 14 +++++++++----- .../zookeeper/client/ZooKeeperSaslClient.java | 4 ++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 2fb591878e6..d1c5e55f873 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -945,15 +945,19 @@ private void startConnect() throws IOException { Watcher.Event.EventType.None, Watcher.Event.KeeperState.AuthFailed, null)); } - String log = "Opening socket connection to server " + addr; - if (zooKeeperSaslClient != null) { - log += ". " + zooKeeperSaslClient.getConfigStatus(); - } - LOG.info(log); + logStartConnect(addr); clientCnxnSocket.connect(addr); } + private void logStartConnect(InetSocketAddress addr) { + String msg = "Opening socket connection to server " + addr; + if (zooKeeperSaslClient != null) { + msg += ". " + zooKeeperSaslClient.getConfigStatus(); + } + LOG.info(msg); + } + private static final String RETRY_CONN_MSG = ", closing socket connection and attempting reconnect"; diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 340db4887db..0d2e962761e 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -67,6 +67,7 @@ public enum SaslState { private SaslState saslState = SaslState.INITIAL; + /** informational message indicating the current configuration status */ private final String configStatus; public SaslState getSaslState() { @@ -147,6 +148,9 @@ public ZooKeeperSaslClient(final String serverPrincipal) } } + /** + * @return informational message indicating the current configuration status. + */ public String getConfigStatus() { return configStatus; } From e57238c7328f6121c6908098cca227c15f4f8a6c Mon Sep 17 00:00:00 2001 From: Henry Robinson Date: Thu, 2 Aug 2012 22:18:57 +0000 Subject: [PATCH 101/444] ZOOKEEPER-1514. FastLeaderElection - leader ignores the round information when joining a quorum (flavio via henryr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1368739 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../server/quorum/FastLeaderElection.java | 8 +- .../test/FLEBackwardElectionRoundTest.java | 154 ++++++++++++++++++ .../zookeeper/test/FLELostMessageTest.java | 89 ++-------- .../apache/zookeeper/test/FLETestUtils.java | 98 +++++++++++ 5 files changed, 274 insertions(+), 78 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java create mode 100644 src/java/test/org/apache/zookeeper/test/FLETestUtils.java diff --git a/CHANGES.txt b/CHANGES.txt index a22dfe6f2a4..1bf2f625181 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -92,6 +92,9 @@ BUGFIXES: ZOOKEEPER-1522. intermittent failures in Zab test due to NPE in recursiveDelete test function (phunt via flavio) + ZOOKEEPER-1514. FastLeaderElection - leader ignores the round + information when joining a quorum (flavio via henryr) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index 01adcc2315d..ad60bf9d77b 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -450,7 +450,7 @@ void halt(){ * Returns the current vlue of the logical clock counter */ public long getLogicalClock(){ - return logicalclock; + return logicalclock; } /** @@ -629,7 +629,9 @@ private boolean checkLeader( if(leader != self.getId()){ if(votes.get(leader) == null) predicate = false; else if(votes.get(leader).getState() != ServerState.LEADING) predicate = false; - } + } else if(logicalclock != electionEpoch) { + predicate = false; + } return predicate; } @@ -732,7 +734,7 @@ public Vote lookForLeader() throws InterruptedException { synchronized(this){ logicalclock++; - updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch()); + updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch()); } LOG.info("New election. My id = " + self.getId() + diff --git a/src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java b/src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java new file mode 100644 index 00000000000..7443c24d15d --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java @@ -0,0 +1,154 @@ +/* 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.test; + +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.HashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.server.quorum.QuorumCnxManager; +import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.quorum.Vote; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.apache.zookeeper.test.FLETestUtils.LEThread; + +public class FLEBackwardElectionRoundTest extends ZKTestCase { + protected static final Logger LOG = LoggerFactory.getLogger(FLELostMessageTest.class); + + int count; + HashMap peers; + File tmpdir[]; + int port[]; + + QuorumCnxManager cnxManagers[]; + + @Before + public void setUp() throws Exception { + count = 3; + + peers = new HashMap(count); + tmpdir = new File[count]; + port = new int[count]; + cnxManagers = new QuorumCnxManager[count - 1]; + } + + @After + public void tearDown() throws Exception { + for(int i = 0; i < (count - 1); i++){ + if(cnxManagers[i] != null){ + cnxManagers[i].halt(); + } + } + } + + /** + * This test is checking the following case. A server S is + * currently LOOKING and it receives notifications from + * a quorum indicating they are following S. The election + * round E of S is higher than the election round E' in the + * notification messages, so S becomes the leader and sets + * its epoch back to E'. In the meanwhile, one or more + * followers turn to LOOKING and elect S in election round E. + * Having leader and followers with different election rounds + * might prevent other servers from electing a leader because + * they can't get a consistent set of notifications from a + * quorum. + * + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1514} + * + * + * @throws Exception + */ + + @Test + public void testBackwardElectionRound() throws Exception { + LOG.info("TestLE: " + getTestName()+ ", " + count); + for(int i = 0; i < count; i++) { + int clientport = PortAssignment.unique(); + peers.put(Long.valueOf(i), + new QuorumServer(i, + new InetSocketAddress(clientport), + new InetSocketAddress(PortAssignment.unique()))); + tmpdir[i] = ClientBase.createTmpDir(); + port[i] = clientport; + } + + /* + * Start server 0 + */ + + QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); + peer.startLeaderElection(); + FLETestUtils.LEThread thread = new FLETestUtils.LEThread(peer, 0); + thread.start(); + + + /* + * Start mock server 1 + */ + QuorumPeer mockPeer = new QuorumPeer(peers, tmpdir[1], tmpdir[1], port[1], 3, 1, 1000, 2, 2); + cnxManagers[0] = new QuorumCnxManager(mockPeer); + QuorumCnxManager.Listener listener = cnxManagers[0].listener; + listener.start(); + + cnxManagers[0].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + + /* + * Start mock server 2 + */ + mockPeer = new QuorumPeer(peers, tmpdir[2], tmpdir[2], port[2], 3, 2, 1000, 2, 2); + cnxManagers[1] = new QuorumCnxManager(mockPeer); + listener = cnxManagers[1].listener; + listener.start(); + + cnxManagers[1].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + + /* + * Run another instance of leader election. + */ + thread.join(5000); + thread = new FLETestUtils.LEThread(peer, 0); + thread.start(); + + /* + * Send the same messages, this time should not make 0 the leader. + */ + cnxManagers[0].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + cnxManagers[1].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + + + thread.join(5000); + + if (!thread.isAlive()) { + Assert.fail("Should not have joined"); + } + + } +} \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java b/src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java index 98f8e10a872..eb64b4bcdad 100644 --- a/src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java @@ -21,7 +21,6 @@ import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; -import java.nio.ByteBuffer; import java.util.HashMap; import org.slf4j.Logger; @@ -31,7 +30,6 @@ import org.apache.zookeeper.server.quorum.FastLeaderElection; import org.apache.zookeeper.server.quorum.QuorumCnxManager; import org.apache.zookeeper.server.quorum.QuorumPeer; -import org.apache.zookeeper.server.quorum.Vote; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.junit.After; @@ -42,14 +40,14 @@ public class FLELostMessageTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLELostMessageTest.class); - + int count; HashMap peers; File tmpdir[]; int port[]; - + QuorumCnxManager cnxManager; - + @Before public void setUp() throws Exception { count = 3; @@ -64,49 +62,10 @@ public void tearDown() throws Exception { cnxManager.halt(); } - - class LEThread extends Thread { - int i; - QuorumPeer peer; - - LEThread(QuorumPeer peer, int i) { - this.i = i; - this.peer = peer; - LOG.info("Constructor: " + getName()); - - } - - public void run(){ - boolean flag = true; - try{ - Vote v = null; - peer.setPeerState(ServerState.LOOKING); - LOG.info("Going to call leader election: " + i); - v = peer.getElectionAlg().lookForLeader(); - - if (v == null){ - Assert.fail("Thread " + i + " got a null vote"); - } - - /* - * A real zookeeper would take care of setting the current vote. Here - * we do it manually. - */ - peer.setCurrentVote(v); - - LOG.info("Finished election: " + i + ", " + v.getId()); - - Assert.assertTrue("State is not leading.", peer.getPeerState() == ServerState.LEADING); - } catch (Exception e) { - e.printStackTrace(); - } - LOG.info("Joining"); - } - } @Test public void testLostMessage() throws Exception { FastLeaderElection le[] = new FastLeaderElection[count]; - + LOG.info("TestLE: " + getTestName()+ ", " + count); for(int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); @@ -117,16 +76,16 @@ public void testLostMessage() throws Exception { tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } - + /* * Start server 0 */ - + QuorumPeer peer = new QuorumPeer(peers, tmpdir[1], tmpdir[1], port[1], 3, 1, 1000, 2, 2); peer.startLeaderElection(); - LEThread thread = new LEThread(peer, 1); + FLETestUtils.LEThread thread = new FLETestUtils.LEThread(peer, 1); thread.start(); - + /* * Start mock server 1 */ @@ -136,24 +95,7 @@ public void testLostMessage() throws Exception { Assert.fail("Threads didn't join"); } } - - ByteBuffer createMsg(int state, long leader, long zxid, long epoch){ - byte requestBytes[] = new byte[28]; - ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); - - /* - * Building notification packet to send - */ - - requestBuffer.clear(); - requestBuffer.putInt(state); - requestBuffer.putLong(leader); - requestBuffer.putLong(zxid); - requestBuffer.putLong(epoch); - - return requestBuffer; - } - + void mockServer() throws InterruptedException, IOException { /* * Create an instance of the connection manager @@ -161,14 +103,11 @@ void mockServer() throws InterruptedException, IOException { QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); cnxManager = new QuorumCnxManager(peer); QuorumCnxManager.Listener listener = cnxManager.listener; - if(listener != null){ - listener.start(); - } else { - LOG.error("Null listener when initializing cnx manager"); - } - - cnxManager.toSend(new Long(1), createMsg(ServerState.LOOKING.ordinal(), 0, 0, 1)); + listener.start(); + + + cnxManager.toSend(1l, FLETestUtils.createMsg(ServerState.LOOKING.ordinal(), 0, 0, 1)); cnxManager.recvQueue.take(); - cnxManager.toSend(new Long(1), createMsg(ServerState.FOLLOWING.ordinal(), 1, 0, 1)); + cnxManager.toSend(1L, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 1, 0, 1)); } } diff --git a/src/java/test/org/apache/zookeeper/test/FLETestUtils.java b/src/java/test/org/apache/zookeeper/test/FLETestUtils.java new file mode 100644 index 00000000000..81f5cc5ba74 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/FLETestUtils.java @@ -0,0 +1,98 @@ +/* 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.test; + +import java.nio.ByteBuffer; + +import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.quorum.Vote; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.junit.Assert; + +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; + +public class FLETestUtils { + protected static final Logger LOG = LoggerFactory.getLogger(FLETestUtils.class); + + + /* + * Thread to run an instance of leader election for + * a given quorum peer. + */ + static class LEThread extends Thread { + private int i; + private QuorumPeer peer; + + LEThread(QuorumPeer peer, int i) { + this.i = i; + this.peer = peer; + LOG.info("Constructor: " + getName()); + + } + + public void run(){ + try{ + Vote v = null; + peer.setPeerState(ServerState.LOOKING); + LOG.info("Going to call leader election: " + i); + v = peer.getElectionAlg().lookForLeader(); + + if (v == null){ + Assert.fail("Thread " + i + " got a null vote"); + } + + /* + * A real zookeeper would take care of setting the current vote. Here + * we do it manually. + */ + peer.setCurrentVote(v); + + LOG.info("Finished election: " + i + ", " + v.getId()); + + Assert.assertTrue("State is not leading.", peer.getPeerState() == ServerState.LEADING); + } catch (Exception e) { + e.printStackTrace(); + } + LOG.info("Joining"); + } + } + + /* + * Creates a leader election notification message. + */ + + static ByteBuffer createMsg(int state, long leader, long zxid, long epoch){ + byte requestBytes[] = new byte[28]; + ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); + + /* + * Building notification packet to send + */ + + requestBuffer.clear(); + requestBuffer.putInt(state); + requestBuffer.putLong(leader); + requestBuffer.putLong(zxid); + requestBuffer.putLong(epoch); + + return requestBuffer; + } + +} \ No newline at end of file From c89295cf05eef61ef7972cc35414cd6b505ead10 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 28 Aug 2012 19:57:18 +0000 Subject: [PATCH 102/444] ZOOKEEPER-1497. Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) (Matteo Bertozzi via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1378287 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 + .../org/apache/zookeeper/Environment.java | 2 + .../zookeeper/client/ZooKeeperSaslClient.java | 11 +- .../server/NIOServerCnxnFactory.java | 18 +-- .../server/NettyServerCnxnFactory.java | 17 +-- .../zookeeper/server/ServerCnxnFactory.java | 60 ++++++++++ .../zookeeper/server/ZooKeeperSaslServer.java | 3 + .../auth/SaslServerCallbackHandler.java | 6 +- .../apache/zookeeper/JaasConfiguration.java | 75 +++++++++++++ .../org/apache/zookeeper/test/ClientBase.java | 6 + .../test/SaslAuthDesignatedServerTest.java | 104 ++++++++++++++++++ 11 files changed, 267 insertions(+), 39 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/JaasConfiguration.java create mode 100644 src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedServerTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 1bf2f625181..4af7f9884fa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -112,6 +112,10 @@ IMPROVEMENTS: ZOOKEEPER-1510. Should not log SASL errors for non-secure usage (Todd Lipcon via phunt) + ZOOKEEPER-1497. Allow server-side SASL login with JAAS configuration + to be programmatically set (rather than only by reading JAAS + configuration file) (Matteo Bertozzi via phunt) + Release 3.4.3 - 2012-02-06 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/Environment.java b/src/java/main/org/apache/zookeeper/Environment.java index 9a667438237..34cac4ca922 100644 --- a/src/java/main/org/apache/zookeeper/Environment.java +++ b/src/java/main/org/apache/zookeeper/Environment.java @@ -31,6 +31,8 @@ * */ public class Environment { + public static String JAAS_CONF_KEY = "java.security.auth.login.config"; + public static class Entry { private String k; private String v; diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 0d2e962761e..78ff27723c0 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -22,6 +22,7 @@ import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.Login; import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.Environment; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.ReplyHeader; @@ -111,10 +112,8 @@ public ZooKeeperSaslClient(final String serverPrincipal) if (securityException != null) { throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + " section of the supplied JAAS configuration: '" + - System.getProperty("java.security.auth.login.config") + "' because of a " + + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + "SecurityException: " + securityException); - - } else { throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " + "section '" + explicitClientSection + "' could not be found."); @@ -130,19 +129,19 @@ public ZooKeeperSaslClient(final String serverPrincipal) } this.configStatus = msg; } - if (System.getProperty("java.security.auth.login.config") != null) { + if (System.getProperty(Environment.JAAS_CONF_KEY) != null) { // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed. if (securityException != null) { throw new LoginException("Zookeeper client cannot authenticate using the '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + "' section of the supplied JAAS configuration: '" + - System.getProperty("java.security.auth.login.config") + "' because of a " + + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + "SecurityException: " + securityException); } else { throw new LoginException("No JAAS configuration section named '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + "' was found in specified JAAS configuration file: '" + - System.getProperty("java.security.auth.login.config") + "'."); + System.getProperty(Environment.JAAS_CONF_KEY) + "'."); } } } diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java index c1d97e514ce..8b4c46bc9a1 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java @@ -32,11 +32,6 @@ import java.util.HashSet; import java.util.Set; -import javax.security.auth.login.Configuration; -import javax.security.auth.login.LoginException; - -import org.apache.zookeeper.Login; -import org.apache.zookeeper.server.auth.SaslServerCallbackHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,17 +84,8 @@ public NIOServerCnxnFactory() throws IOException { Thread thread; @Override public void configure(InetSocketAddress addr, int maxcc) throws IOException { - if (System.getProperty("java.security.auth.login.config") != null) { - try { - saslServerCallbackHandler = new SaslServerCallbackHandler(Configuration.getConfiguration()); - login = new Login("Server",saslServerCallbackHandler); - login.startThreadIfNeeded(); - } - catch (LoginException e) { - throw new IOException("Could not configure server because SASL configuration did not allow the " - + " Zookeeper server to authenticate itself properly: " + e); - } - } + configureSaslLogin(); + thread = new Thread(this, "NIOServerCxn.Factory:" + addr); thread.setDaemon(true); maxClientCnxns = maxcc; diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index 83f18204078..32692e218d1 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -28,8 +28,6 @@ import java.util.Set; import java.util.concurrent.Executors; -import org.apache.zookeeper.Login; -import org.apache.zookeeper.server.auth.SaslServerCallbackHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.jboss.netty.bootstrap.ServerBootstrap; @@ -47,9 +45,6 @@ import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; -import javax.security.auth.login.Configuration; -import javax.security.auth.login.LoginException; - public class NettyServerCnxnFactory extends ServerCnxnFactory { Logger LOG = LoggerFactory.getLogger(NettyServerCnxnFactory.class); @@ -306,17 +301,7 @@ public void closeSession(long sessionId) { public void configure(InetSocketAddress addr, int maxClientCnxns) throws IOException { - if (System.getProperty("java.security.auth.login.config") != null) { - try { - saslServerCallbackHandler = new SaslServerCallbackHandler(Configuration.getConfiguration()); - login = new Login("Server",saslServerCallbackHandler); - login.startThreadIfNeeded(); - } - catch (LoginException e) { - throw new IOException("Could not configure server because SASL configuration did not allow the " - + " Zookeeper server to authenticate itself properly: " + e); - } - } + configureSaslLogin(); localAddress = addr; this.maxClientCnxns = maxClientCnxns; } diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java index 406a6dd389a..e5c65652513 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java @@ -24,9 +24,14 @@ import java.util.HashMap; import java.util.HashSet; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.AppConfigurationEntry; + import javax.management.JMException; import org.apache.zookeeper.Login; +import org.apache.zookeeper.Environment; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.auth.SaslServerCallbackHandler; import org.slf4j.Logger; @@ -146,4 +151,59 @@ public void registerConnection(ServerCnxn serverCnxn) { } + /** + * Initialize the server SASL if specified. + * + * If the user has specified a "ZooKeeperServer.LOGIN_CONTEXT_NAME_KEY" + * or a jaas.conf using "java.security.auth.login.config" + * the authentication is required and an exception is raised. + * Otherwise no authentication is configured and no exception is raised. + * + * @throws IOException if jaas.conf is missing or there's an error in it. + */ + protected void configureSaslLogin() throws IOException { + String serverSection = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, + ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME); + + // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. + AppConfigurationEntry entries[] = null; + SecurityException securityException = null; + try { + entries = Configuration.getConfiguration().getAppConfigurationEntry(serverSection); + } catch (SecurityException e) { + // handle below: might be harmless if the user doesn't intend to use JAAS authentication. + securityException = e; + } + + // No entries in jaas.conf + // If there's a configuration exception fetching the jaas section and + // the user has required sasl by specifying a LOGIN_CONTEXT_NAME_KEY or a jaas file + // we throw an exception otherwise we continue without authentication. + if (entries == null) { + String jaasFile = System.getProperty(Environment.JAAS_CONF_KEY); + String loginContextName = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY); + if (securityException != null && (loginContextName != null || jaasFile != null)) { + String errorMessage = "No JAAS configuration section named '" + serverSection + "' was found"; + if (jaasFile != null) { + errorMessage += "in '" + jaasFile + "'."; + } + if (loginContextName != null) { + errorMessage += " But " + ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY + " was set."; + } + LOG.error(errorMessage); + throw new IOException(errorMessage); + } + return; + } + + // jaas.conf entry available + try { + saslServerCallbackHandler = new SaslServerCallbackHandler(Configuration.getConfiguration()); + login = new Login(serverSection, saslServerCallbackHandler); + login.startThreadIfNeeded(); + } catch (LoginException e) { + throw new IOException("Could not configure server because SASL configuration did not allow the " + + " ZooKeeper server to authenticate itself properly: " + e); + } + } } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java index 0ca18dc3ed3..e3221a3aacd 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java @@ -30,6 +30,9 @@ import org.apache.zookeeper.Login; public class ZooKeeperSaslServer { + public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.serverconfig"; + public static final String DEFAULT_LOGIN_CONTEXT_NAME = "Server"; + Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslServer.class); private SaslServer saslServer; diff --git a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java index fda0ed2f9f3..2fbd6eda4e7 100644 --- a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java +++ b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java @@ -34,6 +34,8 @@ import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.RealmCallback; +import org.apache.zookeeper.server.ZooKeeperSaslServer; + public class SaslServerCallbackHandler implements CallbackHandler { private static final String USER_PREFIX = "user_"; private static final Logger LOG = LoggerFactory.getLogger(SaslServerCallbackHandler.class); @@ -45,7 +47,9 @@ public class SaslServerCallbackHandler implements CallbackHandler { private final Map credentials = new HashMap(); public SaslServerCallbackHandler(Configuration configuration) throws IOException { - AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry("Server"); + String serverSection = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, + ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME); + AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry(serverSection); if (configurationEntries == null) { String errorMessage = "Could not find a 'Server' entry in this configuration: Server cannot start."; diff --git a/src/java/test/org/apache/zookeeper/JaasConfiguration.java b/src/java/test/org/apache/zookeeper/JaasConfiguration.java new file mode 100644 index 00000000000..bf145d0249c --- /dev/null +++ b/src/java/test/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 sections = + new HashMap(); + + 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 conf = new HashMap(); + // 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 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 diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 1f711ba0e4a..4c667f889a1 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -159,6 +159,12 @@ protected TestableZooKeeper createClient(String hp) return createClient(watcher, hp); } + protected TestableZooKeeper createClient(CountdownWatcher watcher) + throws IOException, InterruptedException + { + return createClient(watcher, hostPort); + } + private LinkedList allClients; private boolean allClientsSetup = false; diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedServerTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedServerTest.java new file mode 100644 index 00000000000..aa30870da28 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedServerTest.java @@ -0,0 +1,104 @@ +/** + * 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.test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.server.ZooKeeperSaslServer; +import org.apache.zookeeper.JaasConfiguration; +import org.junit.Assert; +import org.junit.Test; + +public class SaslAuthDesignatedServerTest extends ClientBase { + public static int AUTHENTICATION_TIMEOUT = 30000; + + static { + System.setProperty("zookeeper.authProvider.1","org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + System.setProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperServer"); + + JaasConfiguration conf = new JaasConfiguration(); + + /* this 'Server' section has an incorrect password, but we're not configured + * to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) + * to use the 'MyZookeeperServer' section below, which has the correct password). + */ + conf.addSection("Server", "org.apache.zookeeper.server.auth.DigestLoginModule", + "user_myuser", "wrongpassword"); + + conf.addSection("MyZookeeperServer", "org.apache.zookeeper.server.auth.DigestLoginModule", + "user_myuser", "mypassword"); + + conf.addSection("Client", "org.apache.zookeeper.server.auth.DigestLoginModule", + "username", "myuser", "password", "mypassword"); + + javax.security.auth.login.Configuration.setConfiguration(conf); + } + + private AtomicInteger authFailed = new AtomicInteger(0); + + private class MyWatcher extends CountdownWatcher { + volatile CountDownLatch authCompleted; + + @Override + synchronized public void reset() { + authCompleted = new CountDownLatch(1); + super.reset(); + } + + @Override + public synchronized void process(WatchedEvent event) { + if (event.getState() == KeeperState.AuthFailed) { + authFailed.incrementAndGet(); + authCompleted.countDown(); + } else if (event.getState() == KeeperState.SaslAuthenticated) { + authCompleted.countDown(); + } else { + super.process(event); + } + } + } + + @Test + public void testAuth() throws Exception { + MyWatcher watcher = new MyWatcher(); + ZooKeeper zk = createClient(watcher); + watcher.authCompleted.await(AUTHENTICATION_TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertEquals(authFailed.get(), 0); + + try { + zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); + } catch (KeeperException e) { + Assert.fail("test failed :" + e); + } + finally { + zk.close(); + } + } +} From f1660f4af5f88405efe2a10c9f00fd4bf9bc451e Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 30 Aug 2012 19:26:58 +0000 Subject: [PATCH 103/444] ZOOKEEPER-1536 c client : memory leak in winport.c (brooklin via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1379080 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/src/winport.c | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 4af7f9884fa..122dcc827a2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -95,6 +95,8 @@ BUGFIXES: ZOOKEEPER-1514. FastLeaderElection - leader ignores the round information when joining a quorum (flavio via henryr) + ZOOKEEPER-1536 c client : memory leak in winport.c (brooklin via michim) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/c/src/winport.c b/src/c/src/winport.c index 35eec7ed8f6..aeef3a84e61 100644 --- a/src/c/src/winport.c +++ b/src/c/src/winport.c @@ -109,6 +109,7 @@ pthread_cond_init (pthread_cond_t *cv, int pthread_cond_destroy(pthread_cond_t *cond) { CloseHandle( cond->sema_); + DeleteCriticalSection(&cond->waiters_count_lock_); return (CloseHandle( cond->waiters_done_ ) == 0)? GetLastError(): 0 ; } From 76159a4e0e349de530f66af1734acaf4ba76521f Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 30 Aug 2012 20:41:08 +0000 Subject: [PATCH 104/444] ZOOKEEPER-1481 allow the C cli to run exists with a watcher (phunt via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1379132 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/src/cli.c | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 122dcc827a2..0f971f3c8cd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -97,6 +97,8 @@ BUGFIXES: ZOOKEEPER-1536 c client : memory leak in winport.c (brooklin via michim) + ZOOKEEPER-1481 allow the C cli to run exists with a watcher (phunt via michim) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/c/src/cli.c b/src/c/src/cli.c index da764f38150..44ff9d45cb0 100644 --- a/src/c/src/cli.c +++ b/src/c/src/cli.c @@ -84,13 +84,30 @@ static const char* state2String(int state){ return "INVALID_STATE"; } +static const char* type2String(int state){ + if (state == ZOO_CREATED_EVENT) + return "CREATED_EVENT"; + if (state == ZOO_DELETED_EVENT) + return "DELETED_EVENT"; + if (state == ZOO_CHANGED_EVENT) + return "CHANGED_EVENT"; + if (state == ZOO_CHILD_EVENT) + return "CHILD_EVENT"; + if (state == ZOO_SESSION_EVENT) + return "SESSION_EVENT"; + if (state == ZOO_NOTWATCHING_EVENT) + return "NOTWATCHING_EVENT"; + + return "UNKNOWN_EVENT_TYPE"; +} + void watcher(zhandle_t *zzh, int type, int state, const char *path, void* context) { /* Be careful using zh here rather than zzh - as this may be mt code * the client lib may call the watcher before zookeeper_init returns */ - fprintf(stderr, "Watcher %d state = %s", type, state2String(state)); + fprintf(stderr, "Watcher %s state = %s", type2String(type), state2String(state)); if (path && strlen(path) > 0) { fprintf(stderr, " for path %s", path); } @@ -298,6 +315,7 @@ void processline(char *line) { fprintf(stderr, " ls2 \n"); fprintf(stderr, " sync \n"); fprintf(stderr, " exists \n"); + fprintf(stderr, " wexists \n"); fprintf(stderr, " myid\n"); fprintf(stderr, " verbose\n"); fprintf(stderr, " addauth \n"); @@ -428,6 +446,23 @@ void processline(char *line) { if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } + } else if (startsWith(line, "wexists ")) { +#ifdef THREADED + struct Stat stat; +#endif + line += 8; + if (line[0] != '/') { + fprintf(stderr, "Path must start with /, found: %s\n", line); + return; + } +#ifndef THREADED + rc = zoo_awexists(zh, line, watcher, (void*) 0, my_stat_completion, strdup(line)); +#else + rc = zoo_wexists(zh, line, watcher, (void*) 0, &stat); +#endif + if (rc) { + fprintf(stderr, "Error %d for %s\n", rc, line); + } } else if (startsWith(line, "exists ")) { #ifdef THREADED struct Stat stat; From 06e2871214d08ad36c6dbf69ae31d8ebad649e38 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Tue, 4 Sep 2012 23:26:07 +0000 Subject: [PATCH 105/444] ZOOKEEPER-1105 c client zookeeper_close not send CLOSE_OP request to server (lincoln.lee via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1380932 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 0f971f3c8cd..0a0682ee917 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -99,6 +99,9 @@ BUGFIXES: ZOOKEEPER-1481 allow the C cli to run exists with a watcher (phunt via michim) + ZOOKEEPER-1105 c client zookeeper_close not send CLOSE_OP request to server + (lincoln.lee via michim) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index be8165e9966..af56a6f29d0 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -2515,6 +2515,22 @@ int zookeeper_close(zhandle_t *zh) /* make sure the close request is sent; we set timeout to an arbitrary * (but reasonable) number of milliseconds since we want the call to block*/ rc=adaptor_send_queue(zh, 3000); + + /* make sure server has read the close request and sent back a response*/ + struct pollfd fd_s[1]; + fd_s[0].fd = zh->fd; + fd_s[0].events = POLLIN; + int ret = poll(fd_s, 1, 1000); + if (ret == 0) { + LOG_WARN(("Timeout when waitting for server's reply after sending a close request, sessionId=%#llx\n", + zh->client_id.client_id)); + } else if (ret < 0) { + LOG_WARN(("System error happens when waitting for server's reply, sessionId=%#llx\n", + zh->client_id.client_id)); + } else { + // do nothing + } + }else{ LOG_INFO(("Freeing zookeeper resources for sessionId=%#llx\n", zh->client_id.client_id)); From 8096030ed967b20dfd6d76e0ae275cca366be15d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 7 Sep 2012 06:13:38 +0000 Subject: [PATCH 106/444] ZOOKEEPER-1380. zkperl: _zk_release_watch doesn't remove items properly from the watch list. (Botond Hejj via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1381895 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/zkperl/ZooKeeper.xs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0a0682ee917..6a249f640dd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -102,6 +102,9 @@ BUGFIXES: ZOOKEEPER-1105 c client zookeeper_close not send CLOSE_OP request to server (lincoln.lee via michim) + ZOOKEEPER-1380. zkperl: _zk_release_watch doesn't remove items properly from + the watch list. (Botond Hejj via mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/contrib/zkperl/ZooKeeper.xs b/src/contrib/zkperl/ZooKeeper.xs index 271789b9579..dbc13827e48 100644 --- a/src/contrib/zkperl/ZooKeeper.xs +++ b/src/contrib/zkperl/ZooKeeper.xs @@ -251,12 +251,12 @@ static void _zk_release_watch(pTHX_ zk_watch_t *watch, int list) if (list) { if (watch->prev) { watch->prev->next = watch->next; - watch->prev = NULL; } if (watch->next) { watch->next->prev = watch->prev; - watch->next = NULL; } + watch->prev = NULL; + watch->next = NULL; } if (--watch->ref_count == 0) { From fae9cf72bd1d6be3eef15a1b8dd1a6556750e4a1 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 7 Sep 2012 06:28:55 +0000 Subject: [PATCH 107/444] ZOOKEEPER-1501. Nagios plugin always returns OK when it cannot connect to zookeeper. (Brian Sutherland via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1381901 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/monitoring/check_zookeeper.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 6a249f640dd..9a5676dc19a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -105,6 +105,9 @@ BUGFIXES: ZOOKEEPER-1380. zkperl: _zk_release_watch doesn't remove items properly from the watch list. (Botond Hejj via mahadev) + ZOOKEEPER-1501. Nagios plugin always returns OK when it cannot connect to + zookeeper. (Brian Sutherland via mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/contrib/monitoring/check_zookeeper.py b/src/contrib/monitoring/check_zookeeper.py index 348ac393b93..c00db8bc0b9 100755 --- a/src/contrib/monitoring/check_zookeeper.py +++ b/src/contrib/monitoring/check_zookeeper.py @@ -74,6 +74,11 @@ def analyze(self, opts, cluster_stats): elif (warning < critical and critical <= value) or (warning > critical and critical >= value): critical_state.append(host) + if not values: + # Zookeeper may be down, not serving requests or we may have a bad configuration + print 'Critical, %s not found' % opts.key + return 2 + values = ' '.join(values) if critical_state: print 'Critical "%s" %s!|%s' % (opts.key, ', '.join(critical_state), values) From cae51455dede772b8cca5f73052e955028fe3b96 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sun, 9 Sep 2012 18:22:06 +0000 Subject: [PATCH 108/444] ZOOKEEPER-1437. Client uses session before SASL authentication complete. (Eugene Koontz via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1382554 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../main/org/apache/zookeeper/ClientCnxn.java | 150 +++++++++++++----- .../apache/zookeeper/ClientCnxnSocket.java | 8 +- .../apache/zookeeper/ClientCnxnSocketNIO.java | 101 ++++++++++-- .../zookeeper/client/ZooKeeperSaslClient.java | 149 ++++++++++++----- .../test/SaslAuthDesignatedClientTest.java | 1 - .../SaslAuthFailDesignatedClientTest.java | 1 - .../zookeeper/test/SaslAuthFailTest.java | 11 +- .../test/SaslAuthMissingClientConfigTest.java | 1 - .../apache/zookeeper/test/SaslAuthTest.java | 3 - 10 files changed, 326 insertions(+), 102 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9a5676dc19a..cbd88851d76 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -128,6 +128,9 @@ IMPROVEMENTS: ZOOKEEPER-1497. Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) (Matteo Bertozzi via phunt) + + ZOOKEEPER-1437. Client uses session before SASL authentication complete. + (Eugene Koontz via mahadev) Release 3.4.3 - 2012-02-06 diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index d1c5e55f873..27547b4b52b 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -64,11 +64,11 @@ import org.apache.zookeeper.proto.GetChildren2Response; import org.apache.zookeeper.proto.GetChildrenResponse; import org.apache.zookeeper.proto.GetDataResponse; +import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetACLResponse; import org.apache.zookeeper.proto.SetDataResponse; -import org.apache.zookeeper.proto.SetSASLResponse; import org.apache.zookeeper.proto.SetWatches; import org.apache.zookeeper.proto.WatcherEvent; import org.apache.zookeeper.server.ByteBufferInputStream; @@ -251,6 +251,8 @@ static class Packet { WatchRegistration watchRegistration; + public boolean readOnly; + /** Convenience ctor */ Packet(RequestHeader requestHeader, ReplyHeader replyHeader, Record request, Record response, @@ -267,7 +269,11 @@ static class Packet { this.replyHeader = replyHeader; this.request = request; this.response = response; + this.readOnly = readOnly; + this.watchRegistration = watchRegistration; + } + public void createBB() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); @@ -289,8 +295,6 @@ static class Packet { } catch (IOException e) { LOG.warn("Ignoring unexpected exception", e); } - - this.watchRegistration = watchRegistration; } @Override @@ -380,12 +384,6 @@ public ClientCnxn(String chrootPath, HostProvider hostProvider, int sessionTimeo } - // used by ZooKeeperSaslClient.queueSaslPacket(). - public void queuePacket(RequestHeader h, ReplyHeader r, Record request, - Record response, AsyncCallback cb) { - queuePacket(h,r,request,response, cb, null, null, this, null); - } - /** * tests use this to check on reset of watches * @return if the auto reset of watches are disabled @@ -553,17 +551,6 @@ private void processEvent(Object event) { } else { cb.processResult(rc, clientPath, p.ctx, null); } - } else if (p.cb instanceof ZooKeeperSaslClient.ServerSaslResponseCallback) { - ZooKeeperSaslClient.ServerSaslResponseCallback cb = (ZooKeeperSaslClient.ServerSaslResponseCallback) p.cb; - SetSASLResponse rsp = (SetSASLResponse) p.response; - // TODO : check rc (== 0, etc) as with other packet types. - cb.processResult(rc,null,p.ctx,rsp.getToken(),null); - ClientCnxn clientCnxn = (ClientCnxn)p.ctx; - if ((clientCnxn == null) || (clientCnxn.zooKeeperSaslClient == null) || - (clientCnxn.zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.FAILED)) { - queueEvent(new WatchedEvent(EventType.None, - KeeperState.AuthFailed, null)); - } } else if (p.response instanceof GetDataResponse) { DataCallback cb = (DataCallback) p.cb; GetDataResponse rsp = (GetDataResponse) p.response; @@ -777,6 +764,18 @@ else if (serverPath.length() > chrootPath.length()) eventThread.queueEvent( we ); return; } + + // If SASL authentication is currently in progress, construct and + // send a response packet immediately, rather than queuing a + // response as with other packets. + if (clientTunneledAuthenticationInProgress()) { + GetSASLRequest request = new GetSASLRequest(); + request.deserialize(bbia,"token"); + zooKeeperSaslClient.respondToServer(request.getToken(), + ClientCnxn.this); + return; + } + Packet packet; synchronized (pendingQueue) { if (pendingQueue.size() == 0) { @@ -923,6 +922,10 @@ private void sendPing() { private int pingRwTimeout = minPingRwTimeout; + // Set to true if and only if constructor of ZooKeeperSaslClient + // throws a LoginException: see startConnect() below. + private boolean saslLoginFailed = false; + private void startConnect() throws IOException { state = States.CONNECTING; @@ -939,11 +942,16 @@ private void startConnect() throws IOException { try { zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); } catch (LoginException e) { + // An authentication error occurred when the SASL client tried to initialize: + // for Kerberos this means that the client failed to authenticate with the KDC. + // This is different from an authentication error that occurs during communication + // with the Zookeeper server, which is handled below. LOG.warn("SASL configuration failed: " + e + " Will continue connection to Zookeeper server without " - + "SASL authentication, if Zookeeper server allows it."); + + "SASL authentication, if Zookeeper server allows it."); eventThread.queueEvent(new WatchedEvent( - Watcher.Event.EventType.None, - Watcher.Event.KeeperState.AuthFailed, null)); + Watcher.Event.EventType.None, + Watcher.Event.KeeperState.AuthFailed, null)); + saslLoginFailed = true; } logStartConnect(addr); @@ -987,21 +995,35 @@ public void run() { } if (state.isConnected()) { - if ((zooKeeperSaslClient != null) && (zooKeeperSaslClient.isFailed() != true) && (zooKeeperSaslClient.isComplete() != true)) { - try { - zooKeeperSaslClient.initialize(ClientCnxn.this); + // determine whether we need to send an AuthFailed event. + if (zooKeeperSaslClient != null) { + boolean sendAuthEvent = false; + if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) { + try { + zooKeeperSaslClient.initialize(ClientCnxn.this); + } catch (SaslException e) { + LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e); + state = States.AUTH_FAILED; + sendAuthEvent = true; + } } - catch (SaslException e) { - LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e); - state = States.AUTH_FAILED; - eventThread.queueEvent(new WatchedEvent( - Watcher.Event.EventType.None, - KeeperState.AuthFailed,null)); + KeeperState authState = zooKeeperSaslClient.getKeeperState(); + if (authState != null) { + if (authState == KeeperState.AuthFailed) { + // An authentication error occurred during authentication with the Zookeeper Server. + state = States.AUTH_FAILED; + sendAuthEvent = true; + } else { + if (authState == KeeperState.SaslAuthenticated) { + sendAuthEvent = true; + } + } } - if (zooKeeperSaslClient.readyToSendSaslAuthEvent()) { + + if (sendAuthEvent == true) { eventThread.queueEvent(new WatchedEvent( - Watcher.Event.EventType.None, - Watcher.Event.KeeperState.SaslAuthenticated, null)); + Watcher.Event.EventType.None, + authState,null)); } } to = readTimeout - clientCnxnSocket.getIdleRecv(); @@ -1022,7 +1044,6 @@ public void run() { if (timeToNextPing <= 0) { sendPing(); clientCnxnSocket.updateLastSend(); - clientCnxnSocket.enableWrite(); } else { if (timeToNextPing < to) { to = timeToNextPing; @@ -1044,8 +1065,7 @@ public void run() { to = Math.min(to, pingRwTimeout - idlePingRwServer); } - clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue); - + clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this); } catch (Throwable e) { if (closing) { if (LOG.isDebugEnabled()) { @@ -1204,6 +1224,26 @@ void close() { void testableCloseSocket() throws IOException { clientCnxnSocket.testableCloseSocket(); } + + public boolean clientTunneledAuthenticationInProgress() { + // 1. SASL login failed. + if (saslLoginFailed == true) { + return false; + } + + // 2. SendThread has not created the authenticating object yet, + // therefore authentication is (at the earliest stage of being) in progress. + if (zooKeeperSaslClient == null) { + return true; + } + + // 3. authenticating object exists, so ask it for its progress. + return zooKeeperSaslClient.clientTunneledAuthenticationInProgress(); + } + + public void sendPacket(Packet p) throws IOException { + clientCnxnSocket.sendPacket(p); + } } /** @@ -1250,7 +1290,11 @@ public void close() throws IOException { private volatile States state = States.NOT_CONNECTED; - synchronized private int getXid() { + /* + * getXid() is called externally by ClientCnxnNIO::doIO() when packets are sent from the outgoingQueue to + * the server. Thus, getXid() must be public. + */ + synchronized public int getXid() { return xid++; } @@ -1268,15 +1312,37 @@ public ReplyHeader submitRequest(RequestHeader h, Record request, return r; } + public void enableWrite() { + sendThread.getClientCnxnSocket().enableWrite(); + } + + public void sendPacket(Record request, Record response, AsyncCallback cb, int opCode) + throws IOException { + // Generate Xid now because it will be sent immediately, + // by call to sendThread.sendPacket() below. + int xid = getXid(); + RequestHeader h = new RequestHeader(); + h.setXid(xid); + h.setType(opCode); + + ReplyHeader r = new ReplyHeader(); + r.setXid(xid); + + Packet p = new Packet(h, r, request, response, null, false); + p.cb = cb; + sendThread.sendPacket(p); + } + Packet queuePacket(RequestHeader h, ReplyHeader r, Record request, Record response, AsyncCallback cb, String clientPath, String serverPath, Object ctx, WatchRegistration watchRegistration) { Packet packet = null; + + // Note that we do not generate the Xid for the packet yet. It is + // generated later at send-time, by an implementation of ClientCnxnSocket::doIO(), + // where the packet is actually sent. synchronized (outgoingQueue) { - if (h.getType() != OpCode.ping && h.getType() != OpCode.auth) { - h.setXid(getXid()); - } packet = new Packet(h, r, request, response, watchRegistration); packet.cb = cb; packet.ctx = ctx; diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java index 269f8e84d5c..5ca0ba77bcc 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java @@ -160,11 +160,15 @@ void readConnectResult() throws IOException { abstract void enableWrite(); + abstract void disableWrite(); + abstract void enableReadWriteOnly(); abstract void doTransport(int waitTimeOut, List pendingQueue, - LinkedList outgoingQueue) throws IOException, - InterruptedException; + LinkedList outgoingQueue, ClientCnxn cnxn) + throws IOException, InterruptedException; abstract void testableCloseSocket() throws IOException; + + abstract void sendPacket(Packet p) throws IOException; } diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java index ad445040683..a4b389c8188 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java @@ -25,6 +25,7 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -57,7 +58,8 @@ boolean isConnected() { * @throws InterruptedException * @throws IOException */ - void doIO(List pendingQueue, LinkedList outgoingQueue) throws InterruptedException, IOException { + void doIO(List pendingQueue, LinkedList outgoingQueue, ClientCnxn cnxn) + throws InterruptedException, IOException { SocketChannel sock = (SocketChannel) sockKey.channel(); if (sock == null) { throw new IOException("Socket is null!"); @@ -78,7 +80,10 @@ void doIO(List pendingQueue, LinkedList outgoingQueue) throws In } else if (!initialized) { readConnectResult(); enableRead(); - if (!outgoingQueue.isEmpty()) { + if (findSendablePacket(outgoingQueue, + cnxn.sendThread.clientTunneledAuthenticationInProgress()) != null) { + // Since SASL authentication has completed (if client is configured to do so), + // outgoing packets waiting in the outgoingQueue can now be sent. enableWrite(); } lenBuffer.clear(); @@ -95,26 +100,82 @@ void doIO(List pendingQueue, LinkedList outgoingQueue) throws In } if (sockKey.isWritable()) { LinkedList pending = new LinkedList(); - synchronized (outgoingQueue) { - if (!outgoingQueue.isEmpty()) { + Packet p = null; + synchronized(outgoingQueue) { + p = findSendablePacket(outgoingQueue, + cnxn.sendThread.clientTunneledAuthenticationInProgress()); + + if (p != null) { + outgoingQueue.removeFirstOccurrence(p); updateLastSend(); - ByteBuffer pbb = outgoingQueue.getFirst().bb; + if ((p.requestHeader != null) && + (p.requestHeader.getType() != OpCode.ping) && + (p.requestHeader.getType() != OpCode.auth)) { + p.requestHeader.setXid(cnxn.getXid()); + } + p.createBB(); + ByteBuffer pbb = p.bb; sock.write(pbb); if (!pbb.hasRemaining()) { sentCount++; - Packet p = outgoingQueue.removeFirst(); if (p.requestHeader != null && p.requestHeader.getType() != OpCode.ping && p.requestHeader.getType() != OpCode.auth) { pending.add(p); } } + } else { + // No suitable packet to send: turn off write interest flag. + // Will be turned on later by a later call to enableWrite(), + // from within ZooKeeperSaslClient (if client is configured + // to attempt SASL authentication), or in either doIO() or + // in doTransport() if not. + disableWrite(); } } synchronized(pendingQueue) { pendingQueue.addAll(pending); } + + } + } + + private Packet findSendablePacket(LinkedList outgoingQueue, + boolean clientTunneledAuthenticationInProgress) { + synchronized (outgoingQueue) { + if (!outgoingQueue.isEmpty()) { + if (clientTunneledAuthenticationInProgress) { + Packet p = null; + // Since client's authentication with server is in progress, + // send only the null-header packet queued by primeConnection(). + // This packet must be sent so that the SASL authentication process + // can proceed, but all other packets should wait until + // SASL authentication completes. + Iterator iter = outgoingQueue.listIterator(); + while(iter.hasNext()) { + p = iter.next(); + if (p.requestHeader == null) { + // We've found the priming-packet. + return p; + } else { + // Non-priming packet: defer it until later, leaving it in the queue + // until authentication completes. + if (LOG.isDebugEnabled()) { + LOG.debug("deferring non-priming packet: " + p + + "until SASL authentication completes."); + } + } + } + // no sendable packet found. + return null; + } else { + // Tunnelled authentication is not in progress: just + // send the first packet in the queue. + return outgoingQueue.getFirst(); + } + } } + return null; } @Override @@ -200,7 +261,7 @@ SocketChannel createSock() throws IOException { void registerAndConnect(SocketChannel sock, InetSocketAddress addr) throws IOException { sockKey = sock.register(selector, SelectionKey.OP_CONNECT); - boolean immediateConnect = sock.connect(addr); + boolean immediateConnect = sock.connect(addr); if (immediateConnect) { sendThread.primeConnection(); } @@ -269,7 +330,8 @@ synchronized void wakeupCnxn() { } @Override - void doTransport(int waitTimeOut, List pendingQueue, LinkedList outgoingQueue ) + void doTransport(int waitTimeOut, List pendingQueue, LinkedList outgoingQueue, + ClientCnxn cnxn) throws IOException, InterruptedException { selector.select(waitTimeOut); Set selected; @@ -288,15 +350,14 @@ void doTransport(int waitTimeOut, List pendingQueue, LinkedList sendThread.primeConnection(); } } else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) { - doIO(pendingQueue, outgoingQueue); + doIO(pendingQueue, outgoingQueue, cnxn); } } if (sendThread.getZkState().isConnected()) { synchronized(outgoingQueue) { - if (!outgoingQueue.isEmpty()) { + if (findSendablePacket(outgoingQueue, + cnxn.sendThread.clientTunneledAuthenticationInProgress()) != null) { enableWrite(); - } else { - disableWrite(); } } } @@ -318,7 +379,8 @@ synchronized void enableWrite() { } } - private synchronized void disableWrite() { + @Override + public synchronized void disableWrite() { int i = sockKey.interestOps(); if ((i & SelectionKey.OP_WRITE) != 0) { sockKey.interestOps(i & (~SelectionKey.OP_WRITE)); @@ -340,4 +402,17 @@ synchronized void enableReadWriteOnly() { Selector getSelector() { return selector; } + + @Override + void sendPacket(Packet p) throws IOException { + SocketChannel sock = (SocketChannel) sockKey.channel(); + if (sock == null) { + throw new IOException("Socket is null!"); + } + p.createBB(); + ByteBuffer pbb = p.bb; + sock.write(pbb); + } + + } diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 78ff27723c0..4ce54770ded 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -21,17 +21,17 @@ import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.Login; +import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.Environment; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetSASLRequest; -import org.apache.zookeeper.proto.ReplyHeader; -import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetSASLResponse; import org.apache.zookeeper.server.auth.KerberosName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -68,6 +68,7 @@ public enum SaslState { private SaslState saslState = SaslState.INITIAL; + private boolean gotLastPacket = false; /** informational message indicating the current configuration status */ private final String configStatus; @@ -166,7 +167,7 @@ public static class ServerSaslResponseCallback implements AsyncCallback.DataCall public void processResult(int rc, String path, Object ctx, byte data[], Stat stat) { // processResult() is used by ClientCnxn's sendThread to respond to // data[] contains the Zookeeper Server's SASL token. - // ctx is the ZooKeeperSaslClient object. We use this object's prepareSaslResponseToServer() method + // ctx is the ZooKeeperSaslClient object. We use this object's respondToServer() method // to reply to the Zookeeper Server's SASL token ZooKeeperSaslClient client = ((ClientCnxn)ctx).zooKeeperSaslClient; if (client == null) { @@ -181,7 +182,7 @@ public void processResult(int rc, String path, Object ctx, byte data[], Stat sta usedata = new byte[0]; LOG.debug("ServerSaslResponseCallback(): using empty data[] as server response (length="+usedata.length+")"); } - client.prepareSaslResponseToServer(usedata, (ClientCnxn)ctx); + client.respondToServer(usedata, (ClientCnxn)ctx); } } @@ -242,37 +243,49 @@ public SaslClient run() throws SaslException { return null; } } - } - catch (LoginException e) { + } catch (LoginException e) { + // We throw LoginExceptions... throw e; - } - catch (Exception e) { + } catch (Exception e) { + // ..but consume (with a log message) all other types of exceptions. LOG.error("Exception while trying to create SASL client: " + e); return null; } } - private void prepareSaslResponseToServer(byte[] serverToken, ClientCnxn cnxn) { - saslToken = serverToken; - + public void respondToServer(byte[] serverToken, ClientCnxn cnxn) { if (saslClient == null) { LOG.error("saslClient is unexpectedly null. Cannot respond to server's SASL message; ignoring."); return; } - LOG.debug("saslToken (server) length: " + saslToken.length); if (!(saslClient.isComplete())) { try { - saslToken = createSaslToken(saslToken); + saslToken = createSaslToken(serverToken); if (saslToken != null) { - LOG.debug("saslToken (client) length: " + saslToken.length); - queueSaslPacket(saslToken, cnxn); + sendSaslPacket(saslToken, cnxn); } } catch (SaslException e) { LOG.error("SASL authentication failed using login context '" + - this.getLoginContext() + "'."); + this.getLoginContext() + "'."); saslState = SaslState.FAILED; + gotLastPacket = true; + } + } + + if (saslClient.isComplete()) { + // GSSAPI: server sends a final packet after authentication succeeds + // or fails. + if ((serverToken == null) && (saslClient.getMechanismName() == "GSSAPI")) + gotLastPacket = true; + // non-GSSAPI: no final packet from server. + if (saslClient.getMechanismName() != "GSSAPI") { + gotLastPacket = true; } + // SASL authentication is completed, successfully or not: + // enable the socket's writable flag so that any packets waiting for authentication to complete in + // the outgoing queue will be sent to the Zookeeper server. + cnxn.enableWrite(); } } @@ -284,6 +297,7 @@ private byte[] createSaslToken() throws SaslException { private byte[] createSaslToken(final byte[] saslToken) throws SaslException { if (saslToken == null) { // TODO: introspect about runtime environment (such as jaas.conf) + saslState = SaslState.FAILED; throw new SaslException("Error in authenticating with a Zookeeper Quorum member: the quorum member's saslToken is null."); } @@ -314,6 +328,7 @@ public byte[] run() throws SaslException { } error += " Zookeeper Client will go to AUTH_FAILED state."; LOG.error(error); + saslState = SaslState.FAILED; throw new SaslException(error); } } @@ -324,51 +339,73 @@ public byte[] run() throws SaslException { } } - private void queueSaslPacket(byte[] saslToken, ClientCnxn cnxn) { - LOG.debug("ClientCnxn:sendSaslPacket:length="+saslToken.length); - RequestHeader h = new RequestHeader(); - h.setType(ZooDefs.OpCode.sasl); + private void sendSaslPacket(byte[] saslToken, ClientCnxn cnxn) + throws SaslException{ + if (LOG.isDebugEnabled()) { + LOG.debug("ClientCnxn:sendSaslPacket:length="+saslToken.length); + } + GetSASLRequest request = new GetSASLRequest(); request.setToken(saslToken); SetSASLResponse response = new SetSASLResponse(); ServerSaslResponseCallback cb = new ServerSaslResponseCallback(); - ReplyHeader r = new ReplyHeader(); - cnxn.queuePacket(h,r,request,response,cb); + + try { + cnxn.sendPacket(request,response,cb, ZooDefs.OpCode.sasl); + } catch (IOException e) { + throw new SaslException("Failed to send SASL packet to server.", + e); + } } - - private void queueSaslPacket(ClientCnxn cnxn) throws SaslException { - queueSaslPacket(createSaslToken(), cnxn); + + private void sendSaslPacket(ClientCnxn cnxn) throws SaslException { + if (LOG.isDebugEnabled()) { + LOG.debug("ClientCnxn:sendSaslPacket:length="+saslToken.length); + } + GetSASLRequest request = new GetSASLRequest(); + request.setToken(createSaslToken()); + SetSASLResponse response = new SetSASLResponse(); + ServerSaslResponseCallback cb = new ServerSaslResponseCallback(); + try { + cnxn.sendPacket(request,response,cb, ZooDefs.OpCode.sasl); + } catch (IOException e) { + throw new SaslException("Failed to send SASL packet to server due " + + "to IOException:", e); + } } - // used by ClientCnxn to know when to emit SaslAuthenticated event. - // transitions internally from INTERMEDIATE to COMPLETE as a side effect if - // it's ready to emit this event. - public boolean readyToSendSaslAuthEvent() { + // used by ClientCnxn to know whether to emit a SASL-related event: either AuthFailed or SaslAuthenticated, + // or none, if not ready yet. Sets saslState to COMPLETE as a side-effect. + public KeeperState getKeeperState() { if (saslClient != null) { + if (saslState == SaslState.FAILED) { + return KeeperState.AuthFailed; + } if (saslClient.isComplete()) { if (saslState == SaslState.INTERMEDIATE) { saslState = SaslState.COMPLETE; - return true; + return KeeperState.SaslAuthenticated; } } } - else { - LOG.warn("saslClient is null: client could not authenticate properly."); - } - return false; + // No event ready to emit yet. + return null; } + // Initialize the client's communications with the Zookeeper server by sending the server the first + // authentication packet. public void initialize(ClientCnxn cnxn) throws SaslException { if (saslClient == null) { + saslState = SaslState.FAILED; throw new SaslException("saslClient failed to initialize properly: it's null."); } if (saslState == SaslState.INITIAL) { if (saslClient.hasInitialResponse()) { - queueSaslPacket(cnxn); + sendSaslPacket(cnxn); } else { byte[] emptyToken = new byte[0]; - queueSaslPacket(emptyToken, cnxn); + sendSaslPacket(emptyToken, cnxn); } saslState = SaslState.INTERMEDIATE; } @@ -442,4 +479,44 @@ public void handle(Callback[] callbacks) throws } } } + + public boolean clientTunneledAuthenticationInProgress() { + // TODO: Rather than checking a disjunction here, should be a single member + // variable or method in this class to determine whether the client is + // configured to use SASL. (see also ZOOKEEPER-1455). + try { + if ((System.getProperty(Environment.JAAS_CONF_KEY) != null) || + (javax.security.auth.login.Configuration.getConfiguration() != null)) { + // Client is configured to use SASL. + + // 1. Authentication hasn't finished yet: we must wait for it to do so. + if ((isComplete() == false) && + (isFailed() == false)) { + return true; + } + + // 2. SASL authentication has succeeded or failed.. + if (isComplete() || isFailed()) { + if (gotLastPacket == false) { + // ..but still in progress, because there is a final SASL + // message from server which must be received. + return true; + } + } + } + // Either client is not configured to use a tunnelled authentication + // scheme, or tunnelled authentication has completed (successfully or + // not), and all server SASL messages have been received. + return false; + } catch (SecurityException e) { + // Thrown if the caller does not have permission to retrieve the Configuration. + // In this case, simply returning false is correct. + if (LOG.isDebugEnabled() == true) { + LOG.debug("Could not retrieve login configuration: " + e); + } + return false; + } + } + + } diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java index 3cad9874e81..b27c9873586 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java @@ -85,7 +85,6 @@ public synchronized void process(WatchedEvent event) { @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Thread.sleep(1000); diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java index f9f0e72b399..b6e166aae8e 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java @@ -85,7 +85,6 @@ public synchronized void process(WatchedEvent event) { @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Assert.fail("Should have gotten exception."); diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java index 7ceeef0d281..1589b1f513d 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java @@ -74,7 +74,10 @@ private class MyWatcher extends CountdownWatcher { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { - authFailed.incrementAndGet(); + synchronized(authFailed) { + authFailed.incrementAndGet(); + authFailed.notify(); + } } else { super.process(event); @@ -85,7 +88,10 @@ public synchronized void process(WatchedEvent event) { @Test public void testBadSaslAuthNotifiesWatch() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); + // wait for authFailed event from client's EventThread. + synchronized(authFailed) { + authFailed.wait(); + } Assert.assertEquals(authFailed.get(),1); zk.close(); } @@ -94,7 +100,6 @@ public void testBadSaslAuthNotifiesWatch() throws Exception { @Test public void testAuthFail() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Assert.fail("Should have gotten exception."); diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java index 8642155d80e..98be0be3386 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java @@ -83,7 +83,6 @@ public synchronized void process(WatchedEvent event) { @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Assert.fail("Should have gotten exception."); diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java index d82e3836836..6e75998b1c8 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java @@ -89,7 +89,6 @@ public synchronized void process(WatchedEvent event) { @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Thread.sleep(1000); @@ -101,7 +100,6 @@ public void testAuth() throws Exception { @Test public void testValidSaslIds() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); List validIds = new ArrayList(); validIds.add("user"); @@ -122,7 +120,6 @@ public void testValidSaslIds() throws Exception { @Test public void testInvalidSaslIds() throws Exception { ZooKeeper zk = createClient(); - Thread.sleep(1000); List invalidIds = new ArrayList(); invalidIds.add("user@KERB.REALM/server.com"); From 116e593ece0dc2b924130e31fd9a366891763d94 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 10 Sep 2012 07:03:23 +0000 Subject: [PATCH 109/444] ZOOKEEPER-1494. C client: socket leak after receive timeout in zookeeper_interest() (Michi Mutsuzaki via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1382664 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index cbd88851d76..0c221323cfc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -108,6 +108,9 @@ BUGFIXES: ZOOKEEPER-1501. Nagios plugin always returns OK when it cannot connect to zookeeper. (Brian Sutherland via mahadev) + ZOOKEEPER-1494. C client: socket leak after receive timeout in + zookeeper_interest() (Michi Mutsuzaki via mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index af56a6f29d0..76d8df02fd7 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1637,12 +1637,14 @@ int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, #else errno = ETIMEDOUT; #endif - *fd=-1; *interest=0; *tv = get_timeval(0); return api_epilog(zh,handle_socket_error_msg(zh, __LINE__,ZOPERATIONTIMEOUT, - "connection timed out (exceeded timeout by %dms)",-recv_to)); + "connection to %s timed out (exceeded timeout by %dms)", + format_endpoint_info(&zh->addrs[zh->connect_index]), + -recv_to)); + } // We only allow 1/3 of our timeout time to expire before sending // a PING From c7d5d7625200caebe8614fc2f8f6bc1a174829d3 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 14 Sep 2012 07:33:29 +0000 Subject: [PATCH 110/444] ZOOKEEPER-1483. Fix leader election recipe documentation. (Michi Mutsuzaki via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1384670 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/docs/src/documentation/content/xdocs/recipes.xml | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0c221323cfc..516a73ffe44 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -111,6 +111,9 @@ BUGFIXES: ZOOKEEPER-1494. C client: socket leak after receive timeout in zookeeper_interest() (Michi Mutsuzaki via mahadev) + ZOOKEEPER-1483. Fix leader election recipe documentation. (Michi Mutsuzaki + via mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/docs/src/documentation/content/xdocs/recipes.xml b/src/docs/src/documentation/content/xdocs/recipes.xml index c911e2d4b01..c982d216340 100644 --- a/src/docs/src/documentation/content/xdocs/recipes.xml +++ b/src/docs/src/documentation/content/xdocs/recipes.xml @@ -604,7 +604,7 @@ - Watch for changes on "ELECTION/n_j", where j is the smallest + Watch for changes on "ELECTION/n_j", where j is the largest sequence number such that j < i and n_j is a znode in C; @@ -623,14 +623,14 @@ Otherwise, watch for changes on "ELECTION/n_j", where j is the - smallest sequence number such that j < i and n_j is a znode in C; + largest sequence number such that j < i and n_j is a znode in C; Note that the znode having no preceding znode on the list of children does not imply that the creator of this znode is aware that it is - the current leader. Applications may consider creating a separate to znode + the current leader. Applications may consider creating a separate znode to acknowledge that the leader has executed the leader procedure. From cbdb503206029dcecd227b24cbaac94602da06c2 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sun, 16 Sep 2012 21:35:36 +0000 Subject: [PATCH 111/444] Revert r1380932: ZOOKEEPER-1105 c client zookeeper_close not send CLOSE_OP request to server (lincoln.lee via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1385380 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 --- src/c/src/zookeeper.c | 16 ---------------- 2 files changed, 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 516a73ffe44..047c7055825 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -99,9 +99,6 @@ BUGFIXES: ZOOKEEPER-1481 allow the C cli to run exists with a watcher (phunt via michim) - ZOOKEEPER-1105 c client zookeeper_close not send CLOSE_OP request to server - (lincoln.lee via michim) - ZOOKEEPER-1380. zkperl: _zk_release_watch doesn't remove items properly from the watch list. (Botond Hejj via mahadev) diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 76d8df02fd7..de58c62b55e 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -2517,22 +2517,6 @@ int zookeeper_close(zhandle_t *zh) /* make sure the close request is sent; we set timeout to an arbitrary * (but reasonable) number of milliseconds since we want the call to block*/ rc=adaptor_send_queue(zh, 3000); - - /* make sure server has read the close request and sent back a response*/ - struct pollfd fd_s[1]; - fd_s[0].fd = zh->fd; - fd_s[0].events = POLLIN; - int ret = poll(fd_s, 1, 1000); - if (ret == 0) { - LOG_WARN(("Timeout when waitting for server's reply after sending a close request, sessionId=%#llx\n", - zh->client_id.client_id)); - } else if (ret < 0) { - LOG_WARN(("System error happens when waitting for server's reply, sessionId=%#llx\n", - zh->client_id.client_id)); - } else { - // do nothing - } - }else{ LOG_INFO(("Freeing zookeeper resources for sessionId=%#llx\n", zh->client_id.client_id)); From 538e5a82edf64c9560d9ad5d771b4ac01ccd595d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 17 Sep 2012 05:03:30 +0000 Subject: [PATCH 112/444] ZOOKEEPER-1361. Leader.lead iterates over 'learners' set without proper synchronisation. (Henry Robinson via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1386472 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/NIOServerCnxn.java | 6 +- .../zookeeper/server/NettyServerCnxn.java | 6 +- .../zookeeper/server/quorum/Leader.java | 97 +++++++++++++------ .../zookeeper/server/quorum/LeaderBean.java | 2 +- .../zookeeper/server/quorum/QuorumPeer.java | 8 +- .../org/apache/zookeeper/test/QuorumTest.java | 3 +- 7 files changed, 83 insertions(+), 42 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 047c7055825..dccd13a180b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -135,6 +135,9 @@ IMPROVEMENTS: ZOOKEEPER-1437. Client uses session before SASL authentication complete. (Eugene Koontz via mahadev) + ZOOKEEPER-1361. Leader.lead iterates over 'learners' set without proper + synchronisation. (Henry Robinson via mahadev) + Release 3.4.3 - 2012-02-06 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index eb61a297de6..0fd56b6dfe7 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -771,9 +771,9 @@ public void commandRun() { if(stats.getServerState().equals("leader")) { Leader leader = ((LeaderZooKeeperServer)zkServer).getLeader(); - print("followers", leader.learners.size()); - print("synced_followers", leader.forwardingFollowers.size()); - print("pending_syncs", leader.pendingSyncs.size()); + print("followers", leader.getLearners().size()); + print("synced_followers", leader.getForwardingFollowers().size()); + print("pending_syncs", leader.getNumPendingSyncs()); } } diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index 1d2034aac78..33b61049708 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -582,9 +582,9 @@ public void commandRun() { if(stats.getServerState().equals("leader")) { Leader leader = ((LeaderZooKeeperServer)zkServer).getLeader(); - print("followers", leader.learners.size()); - print("synced_followers", leader.forwardingFollowers.size()); - print("pending_syncs", leader.pendingSyncs.size()); + print("followers", leader.getLearners().size()); + print("synced_followers", leader.getForwardingFollowers().size()); + print("pending_syncs", leader.getNumPendingSyncs()); } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 92b896a1e35..af3096ebf54 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -32,10 +32,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.server.FinalRequestProcessor; @@ -79,22 +79,66 @@ public String toString() { LearnerCnxAcceptor cnxAcceptor; // list of all the followers - public final HashSet learners = + private final HashSet learners = new HashSet(); - // list of followers that are ready to follow (i.e synced with the leader) - public final HashSet forwardingFollowers = + /** + * Returns a copy of the current learner snapshot + */ + public List getLearners() { + synchronized (learners) { + return new ArrayList(learners); + } + } + + // list of followers that are ready to follow (i.e synced with the leader) + private final HashSet forwardingFollowers = new HashSet(); - protected final HashSet observingLearners = + /** + * Returns a copy of the current forwarding follower snapshot + */ + public List getForwardingFollowers() { + synchronized (forwardingFollowers) { + return new ArrayList(forwardingFollowers); + } + } + + private void addForwardingFollower(LearnerHandler lh) { + synchronized (forwardingFollowers) { + forwardingFollowers.add(lh); + } + } + + private final HashSet observingLearners = new HashSet(); - //Pending sync requests - public final HashMap> pendingSyncs = + /** + * Returns a copy of the current observer snapshot + */ + public List getObservingLearners() { + synchronized (observingLearners) { + return new ArrayList(observingLearners); + } + } + + private void addObserverLearnerHandler(LearnerHandler lh) { + synchronized (observingLearners) { + observingLearners.add(lh); + } + } + + // Pending sync requests. Must access under 'this' lock. + private final HashMap> pendingSyncs = new HashMap>(); + synchronized public int getNumPendingSyncs() { + return pendingSyncs.size(); + } + //Follower counter final AtomicLong followerCounter = new AtomicLong(-1); + /** * Adds peer to the leader. * @@ -119,6 +163,9 @@ void removeLearnerHandler(LearnerHandler peer) { synchronized (learners) { learners.remove(peer); } + synchronized (observingLearners) { + observingLearners.remove(peer); + } } boolean isLearnerSynced(LearnerHandler peer){ @@ -350,9 +397,11 @@ void lead() throws IOException, InterruptedException { shutdown("Waiting for a quorum of followers, only synced with: " + ackToString); HashSet followerSet = new HashSet(); - for(LearnerHandler f : learners) + + for(LearnerHandler f : getLearners()) { followerSet.add(f.getSid()); - + } + if (self.getQuorumVerifier().containsQuorum(followerSet)) { //if (followers.size() >= self.quorumPeers.size() / 2) { LOG.warn("Enough followers present. "+ @@ -405,16 +454,16 @@ void lead() throws IOException, InterruptedException { // lock on the followers when we use it. syncedSet.add(self.getId()); - synchronized (learners) { - for (LearnerHandler f : learners) { - // Synced set is used to check we have a supporting quorum, so only - // PARTICIPANT, not OBSERVER, learners should be used - if (f.synced() && f.getLearnerType() == LearnerType.PARTICIPANT) { - syncedSet.add(f.getSid()); - } - f.ping(); + + for (LearnerHandler f : getLearners()) { + // Synced set is used to check we have a supporting quorum, so only + // PARTICIPANT, not OBSERVER, learners should be used + if (f.synced() && f.getLearnerType() == LearnerType.PARTICIPANT) { + syncedSet.add(f.getSid()); } + f.ping(); } + if (!tickSkip && !self.getQuorumVerifier().containsQuorum(syncedSet)) { //if (!tickSkip && syncedCount < self.quorumPeers.size() / 2) { // Lost quorum, shutdown @@ -626,10 +675,8 @@ void sendPacket(QuorumPacket qp) { * send a packet to all observers */ void sendObserverPacket(QuorumPacket qp) { - synchronized(observingLearners) { - for (LearnerHandler f : observingLearners) { - f.queuePacket(qp); - } + for (LearnerHandler f : getObservingLearners()) { + f.queuePacket(qp); } } @@ -789,13 +836,9 @@ synchronized public long startForwarding(LearnerHandler handler, } } if (handler.getLearnerType() == LearnerType.PARTICIPANT) { - synchronized (forwardingFollowers) { - forwardingFollowers.add(handler); - } + addForwardingFollower(handler); } else { - synchronized (observingLearners) { - observingLearners.add(handler); - } + addObserverLearnerHandler(handler); } return lastProposed; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java index 921b0027ae1..b5a3a10ecd0 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java @@ -44,7 +44,7 @@ public String getCurrentZxid() { public String followerInfo() { StringBuilder sb = new StringBuilder(); - for (LearnerHandler handler : leader.learners) { + for (LearnerHandler handler : leader.getLearners()) { sb.append(handler.toString()).append("\n"); } return sb.toString(); 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 b4761b73831..72d09d41c59 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -855,12 +855,8 @@ public String[] getQuorumPeers() { List l = new ArrayList(); synchronized (this) { if (leader != null) { - synchronized (leader.learners) { - for (LearnerHandler fh : - leader.learners) - { - if (fh.getSocket() == null) - continue; + for (LearnerHandler fh : leader.getLearners()) { + if (fh.getSocket() != null) { String s = fh.getSocket().getRemoteSocketAddress().toString(); if (leader.isLearnerSynced(fh)) s += "*"; diff --git a/src/java/test/org/apache/zookeeper/test/QuorumTest.java b/src/java/test/org/apache/zookeeper/test/QuorumTest.java index 0926811f67d..3ffe82c7b97 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumTest.java @@ -151,8 +151,7 @@ public void processResult(int rc, String path, Object ctx, } }, null); } - ArrayList fhs = new ArrayList(leader.forwardingFollowers); - for(LearnerHandler f: fhs) { + for(LearnerHandler f : leader.getForwardingFollowers()) { f.getSocket().shutdownInput(); } for(int i = 0; i < 5000; i++) { From 595a031bdc4d919f4ca03df81c98b59cff5c0f28 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 17 Sep 2012 05:38:28 +0000 Subject: [PATCH 113/444] Release Notes for 3.4.4 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1386478 13f79535-47bb-0310-9956-ffa450edef68 --- docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17115 -> 17115 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13517 -> 13517 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12462 -> 12462 bytes docs/recipes.html | 6 +- docs/recipes.pdf | Bin 31043 -> 31044 bytes docs/releasenotes.html | 629 +++++++++++++++++- docs/releasenotes.pdf | Bin 96926 -> 116281 bytes docs/zookeeperAdmin.pdf | Bin 72882 -> 72882 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133787 -> 133787 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27556 -> 27556 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes .../content/xdocs/releasenotes.xml | 470 ++++++++++++- 23 files changed, 1099 insertions(+), 6 deletions(-) diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 4ea0e5e7062fff6913617f31b31936b8539c5867..a52a7f5fbffdec04e93cf4250ce2e3abdbeb8fb2 100644 GIT binary patch delta 184 zcmaE#{XTobWFAXHGb1Bob5l!Q1M`jZuk#34>KdAb7#Ud^n_GbeH@ERlV8jretj4d4 zA-Fl2Uso;Oz{Jwj#nR2i$jsHk(ag-q+10?<&DF%sz|_*s(Zbc#PQiwtl2|S~JFeoA Y#G;alqSQ1lLnAXwBQ8}{SARDy0C~+WKL7v# delta 184 zcmaE#{XTobWF8{}GXoXGegFUf delta 171 zcmey?!1=9#b3z-Bk%5_kk%^&!v95u|#!07m1dMbI%t8!|tV|58z=E4|cqcGoh)(9= z*ToQQ_T+E(Sr#m!E^ MhLDo!kq%6<0Ml72TmS$7 diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 4c7daf6cf253965a8a2d39950ef7c6b1cada00fa..230791c62b5e03562a854bca9536ed4aa573e9b3 100644 GIT binary patch delta 162 zcmZoY%-DLEal&LCOG7gwBV!8#LtO*&jq|Vb2w3VGnuQn{Ss7bcfdx0W@lIgG5S^^X zuZtnLIhp@(ptFIilaryFp_74|lewd@g`tI;sf&@JxsjWTlYyzRi=&-_4Iw3yGZSS1 DZILLY delta 162 zcmZoY%-DLEal&LCBLg!7BNIad16>1)jq|Vb2pH)an1vV^S(zAGfdx0W@lIgG5S^^X zuZtnLIhp@(ptGBcqoujCxs$Prk*m3-rIDq%xtpV_xr@1@p^=fHk)fS}4Iw3yGZSS1 DQi3Q= diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index 3921cbc591800ffb053d0e8491300777b1790535..5bbe03872cbde5a7065fe483863832e5ce10107a 100644 GIT binary patch delta 162 zcmccJ%6Pk#al&LCOG7gwBV!8#V_gIDjq|Vb2w3VGnuQn{Ss7bcfdx0W@lIgG5S^^X zuZtnLIhkM5*xAIu*v!$$($dA$%*e^u+`!xnNSZqunpzk-TRJ(L*eTc$QZjkJlMDbj Cq9>jJ delta 162 zcmccJ%6Pk#al&LCBLg!7BNIadBV7ZFjq|Vb2ml#oAqGZPCWcmE!Od;F6Bsc>C#&)6 zVhCtb#*c|F?KR^wNtPmq-64bCm8@M CiYL?n diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index f035d4acb160b8d30a01d54b1abd2b961aedb4c6..6457063dc791246ed2d43b76e6dbeeb7b62191df 100644 GIT binary patch delta 184 zcmbP`J|TTV509mxnURsPxv9CXf%(Q67kC6Lbq&ozjEt;|&8@(Kn=5!HFk*;Kmf+XL z5ZoNfe@8jq#L3OT#ni;uz|z#%*ww_qz{JJH&D6rs#mvmr(8$r*PQiwtl2|S~JFeoA Y#G;alqSQ1lLn9*tQ!Z6iSARDy0O@BfRsaA1 delta 184 zcmbP`J|TTV508<7nSqgsfvKskfyKrd7kC7WbPdcx42-Ny46ML{n=5!HFk*;Kmf+XL z5ZoNfe@8jq!pz*#+0D|;)!4|`$;I5jz}(c$$;iyqz|h6g%+SKoPQiwtl2|S~JFeoA Y#G;alqSQ1lLn9*tQ!Z6iSARDy0L^kOCIA2c diff --git a/docs/index.pdf b/docs/index.pdf index aceedfef1f8ee9e7d2331e3d8fcfdaa84f26189f..a357a906c03ed2c88afc4859d7290bf7ebee6ac1 100644 GIT binary patch delta 160 zcmX?`c{X#xdLBzdGb1Bob5nC&1M`i$Kl2D!>KdAb7#Ud^n_GbeH}B(}z=$C_IgVc! zLvZtK{>v)P#+C-ghDI(v)PmKK&q#zrPCmL^V?mKH{a2Bwyl#>U3Zj;01C7B0?ab_zCxluX`eA_D-8 CQ7D-J diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index 3f45114e7f1b8b37c65ca2fea637382fbf3fefaf..df854e146fb9630dbe9785a7a4ab71e1df020cd2 100644 GIT binary patch delta 162 zcmeC`VCw2%n$XE(X=r9-WNcwztZQJtaq3wf0ZUy&vk)UAD`N{Qu;At*-U*BtqLYRA zbuk1t`}4QLXD2g9XG<3YI|Um;N+!Q+mH`0v CaVT^E delta 162 zcmeC`VCw2%n$XE(WMF1sWMXJwq-$WYaq3wf0U*OH#K6eP#Lx;XxVeaT0wad#WFdZC z48hI*{4piYhR()DZkEobZia3~E~e&g&Tgj8&Sp+-mQE&a21Z7vb_zCxluUltECT@F C7AQaf diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index 5945d8402f2e187e3b0dc7b479d77902e17a83d4..114a0e90199f28d2350eb861a464b6518d73b94c 100644 GIT binary patch delta 184 zcmZ3NxGr(RL>@~+Gb1Bo3j;%41M`h@ukr|3>KdAb7#Ud^TUdbwH#hN4V8jreti-R2 zA-Fl7|ARujvyr2zu_F+=xR@9io0vMgn!34}xw@E`7#q5{8amo3*br0_%VlTBRa}x- XR8motn#N^lXkuW_rK;-c@5TiHF&Qoo delta 184 zcmZ3NxGr(RL>?mpGXoLeader Election
  • -

    Watch for changes on "ELECTION/n_j", where j is the smallest +

    Watch for changes on "ELECTION/n_j", where j is the largest sequence number such that j < i and n_j is a znode in C;

  • @@ -932,7 +932,7 @@

    Leader Election

  • Otherwise, watch for changes on "ELECTION/n_j", where j is the - smallest sequence number such that j < i and n_j is a znode in C; + largest sequence number such that j < i and n_j is a znode in C;

  • @@ -940,7 +940,7 @@

    Leader Election

    Note that the znode having no preceding znode on the list of children does not imply that the creator of this znode is aware that it is - the current leader. Applications may consider creating a separate to znode + the current leader. Applications may consider creating a separate znode to acknowledge that the leader has executed the leader procedure.

    diff --git a/docs/recipes.pdf b/docs/recipes.pdf index 03f962e352e3785db8229e8e1f532d21b1eb1216..ea162d306cd98bee1a9a690daa0b75f79bb070d7 100644 GIT binary patch delta 1777 zcmaiyX;4#F7>13ugDgpqO`yYC5fp+qH+N-|EpdUG3OG;|BbG&>h=c$^ksw=7!VOd!0ap4^OV|S6hyg_FL&bUCgL?V) zE2&LyA80KlOPxC76yZ!;Bc@tx{zO&R_^755bI>KPB9832zrDGEtvjNL^~r@)7@3M)#ewHtqi61ucslJ~HSH)A}#kaRBStR2Vx z%+hHCBli&hKNQ!Js-Jp?{eB94^Kl!j=_z{uy4$*k_IkjtSk=AY#UYfdtEwJ(OCJ3F zmbqQ;W1_{B(SozSsTnOh_|QRpbyAUG#-_62+-Dui_JzeyL>8*y0$rP=YKx{Uxx(#6 zfI{3I{loS9v$CnHhzZm;wz2=n0f!s>LrPWL5yP0!6!y!@?&8f3NH6np#ZhX1#a^g* zX@iH|WN%j;`<$yvZc9<@wLS`o1?AcW^1|^glMY*afx(zeprKwu;xOOEDWc(R7rux%6vl zg8Jfbfp_%6f@+s+EN6RGbwn5FH73f%PBn25JHE)mYo|%ut69OGp_&2Hb@OF)kGpcL z1MF*}GuQ=!i>J`qhHz=JL(@rSMN!dfw z%rXoP*SRRuD==-8XYM>H{l-u7zEx2z=rK^}uBCEi(ps16$6vj*+O97; zZF)1nGDobI+O8TDTk^%+W4n``4_}yE^fp zDwKYc^O5lp;osFe-I;KzZ-^{!A>Y<&%LuK+8ZYhL9g+E}*Y;#4sEnFdRWwG8jS$S{O!% z3}rwBEr$J*MDQgZ4$uir5CCMX6eDziz;FO3Tnz#;aDtXW|1T2&0Ep93f*}IIgsE{5 zCZt1Ph}al`2ycg=FhUpxgD?RB;xNGw1rWk8C>aDJ~8>r??OO8 zSkAZOfXjEp!VyOBfcsQ4k{5mKQ52Y4G2^L PaR!L1n3*xzES0|iM;D!< delta 1703 zcmai!do+}J7{^T|ov92_sAOg~#F~2No%gCCV~`bTyGY6~gmTR=GlnuY%8?f9jm#EG zHcQURH8LXA(vqT!atWn1sZ=gG>$2ANr+3fU-(S!9KHul_JkNQ~`Q06*+!>{$rLO=1 z1OOobMp*z>n)7Uh7RUmSy#Qzl0hV9I@`ROX6p~78q_Ie1xsaCcD(@C`BU&dq_j}I_ zw&gEV6SR*TKiw3vwlm4s4}1(g<~ceZcG6z)oxRJgTgz9eg;l+}Rq^HdAq%~RoZbkv z-FE$xG1NI`+b)s&36J)+?b;e%LsLgbotdoBJQppdY!U9blPw!DhVqI4F2IjD^k8??o zRQG;tL}b`Fs}`eWxSjYE)d0~K*;uVVxU-IZo1Hupmp6NTQ|d9fF58x?`|A1Rv~g$A z)b63_V))H;y!w+Yt+neSE-0Waxo*tx_=A-}u6b2=0*2kM+Ugl~tMUN18;Kv>SnCeR zGtD+ks{CJ?I{B=0_I+gSdDJ0Jdwnc(?~aPv&b|z$jq=-j)k|%x-t+e+W_QhsE&Pbn z?fT)x-B0%yX697_^7>43ag{7*6yf+AUzC;BbQRxt=q1>;c~0r>5WA~Q+1sOhai|1c zZW$(+E&D5EZ0haPi_A3oSZM&KXSGis*I-~k2U{Xz_;Z;FUp~L*O1Av2P4gVjsF-o@ z?g>%%Xga;ICAlK=7lWc+B~>qV&8N}n^Ff0JS1fe4TBIh@p7HI^iDm$;47 zm1Aj(X`6JbRX6fnf~AroiN1Y}v>?TNNb7RDkzwl|lWfG=qv63S6;Jn6aF<4ZXo_Wl zx_09-jVqy*iX#Ete`D7sA5k2ma(=FkSJj-l84J3`UBWtcGZH4I>kAJF0~O6P5rUC` z9p{T4NM#*{@-@jhDOE0h3~?(hpvyTa(bpy9Bd_f0#wTTryK{Oyo8R28EXX!JZuFUD z))s!QLqF0$6k~FNfBu}dmdSN!i8Y6q#rTTBZj3K>Q}ZB#kMCiUTv6$K6}X zo4HFz~M$5D?; zk)lOSraWfiaFUhr4tK;21!n`Q%V^8j$i`UN`F~8TOj(>fylb`B$o)tP_)O2cKlH6# zUq@j0_6o(d*ZHx(RbO$QN|c0vF?p zAsi>fVp9rAWQ(`X8(z%E9D*V-ozI&|Tqz(SlbC)FEJR`$hLbcLK^AH-gpz7u7y*dS z5*;fJ$&F#8;c -ZooKeeper 3.4.3 Release Notes +ZooKeeper 3.4.4 Release Notes @@ -202,11 +202,14 @@ PDF -icon
    PDF
    -

    ZooKeeper 3.4.3 Release Notes

    +

    ZooKeeper 3.4.4 Release Notes

    + +

    Changes Since 3.4.3

    +
    + + + +Changes Since ZooKeeper 3.4.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.3
    IssueNotes
    + + ZOOKEEPER-1048 + +addauth command does not work in cli_mt/cli_st +
    + + ZOOKEEPER-1163 + +Memory leak in zk_hashtable.c:do_insert_watcher_object() +
    + + ZOOKEEPER-1210 + +Can't build ZooKeeper RPM with RPM > +
    + + ZOOKEEPER-1236 + +Security uses proprietary Sun APIs +
    + + ZOOKEEPER-1256 + +ClientPortBindTest is failing on Mac OS X +
    + + ZOOKEEPER-1277 + +servers stop serving when lower 32bits of zxid roll over +
    + + ZOOKEEPER-1307 + +zkCli.sh is exiting when an Invalid ACL exception is thrown from setACL command through client +
    + + ZOOKEEPER-1318 + +In Python binding, get_children (and get and exists, and probably others) with expired session doesn't raise exception properly +
    + + ZOOKEEPER-1339 + +C clien doesn't build with --enable-debug +
    + + ZOOKEEPER-1344 + +ZooKeeper client multi-update command is not considering the Chroot request +
    + + ZOOKEEPER-1354 + +AuthTest.testBadAuthThenSendOtherCommands fails intermittently +
    + + ZOOKEEPER-1361 + +Leader.lead iterates over 'learners' set without proper synchronisation +
    + + ZOOKEEPER-1380 + +zkperl: _zk_release_watch doesn't remove items properly from the watch list +
    + + ZOOKEEPER-1384 + +test-cppunit overrides LD_LIBRARY_PATH and fails if gcc is in non-standard location +
    + + ZOOKEEPER-1386 + +avoid flaky URL redirection in "ant javadoc" : replace "http://java.sun.com/javase/6/docs/api/" with "http://download.oracle.com/javase/6/docs/api/" +
    + + ZOOKEEPER-1395 + +node-watcher double-free redux +
    + + ZOOKEEPER-1403 + +zkCli.sh script quoting issue +
    + + ZOOKEEPER-1406 + +dpkg init scripts don't restart - missing check_priv_sep_dir +
    + + ZOOKEEPER-1412 + +java client watches inconsistently triggered on reconnect +
    + + ZOOKEEPER-1419 + +Leader election never settles for a 5-node cluster +
    + + ZOOKEEPER-1427 + +Writing to local files is done non-atomically +
    + + ZOOKEEPER-1431 + +zkpython: async calls leak memory +
    + + ZOOKEEPER-1437 + +Client uses session before SASL authentication complete +
    + + ZOOKEEPER-1463 + +external inline function is not compatible with C99 +
    + + ZOOKEEPER-1465 + +Cluster availability following new leader election takes a long time with large datasets - is correlated to dataset size +
    + + ZOOKEEPER-1466 + +QuorumCnxManager.shutdown missing synchronization +
    + + ZOOKEEPER-1471 + +Jute generates invalid C++ code +
    + + ZOOKEEPER-1480 + +ClientCnxn(1161) can't get the current zk server add, so that - Session 0x for server null, unexpected error +
    + + ZOOKEEPER-1483 + +Fix leader election recipe documentation +
    + + ZOOKEEPER-1489 + +Data loss after truncate on transaction log +
    + + ZOOKEEPER-1490 + +If the configured log directory does not exist zookeeper will not start. Better to create the directory and start +
    + + ZOOKEEPER-1493 + +C Client: zookeeper_process doesn't invoke completion callback if zookeeper_close has been called +
    + + ZOOKEEPER-1494 + +C client: socket leak after receive timeout in zookeeper_interest() +
    + + ZOOKEEPER-1496 + +Ephemeral node not getting cleared even after client has exited +
    + + ZOOKEEPER-1501 + +Nagios plugin always returns OK when it cannot connect to zookeeper +
    + + ZOOKEEPER-1514 + +FastLeaderElection - leader ignores the round information when joining a quorum +
    + + ZOOKEEPER-1521 + +LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits +
    + + ZOOKEEPER-1522 + +intermittent failures in Zab test due to NPE in recursiveDelete test function +
    + + ZOOKEEPER-1536 + +c client : memory leak in winport.c +
    + + ZOOKEEPER-1321 + +Add number of client connections metric in JMX and srvr +
    + + ZOOKEEPER-1377 + +add support for dumping a snapshot file content (similar to LogFormatter) +
    + + ZOOKEEPER-1389 + +it would be nice if start-foreground used exec $JAVA in order to get rid of the intermediate shell process +
    + + ZOOKEEPER-1390 + +some expensive debug code not protected by a check for debug +
    + + ZOOKEEPER-1433 + +improve ZxidRolloverTest (test seems flakey) +
    + + ZOOKEEPER-1454 + +Document how to run autoreconf if cppunit is installed in a non-standard directory +
    + + ZOOKEEPER-1481 + +allow the C cli to run exists with a watcher +
    + + ZOOKEEPER-1497 + +Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) +
    + + ZOOKEEPER-1503 + +remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest +
    + + ZOOKEEPER-1510 + +Should not log SASL errors for non-secure usage +
    + + ZOOKEEPER-1450 + +Backport ZOOKEEPER-1294 fix to 3.4 and 3.3 +
    +
    + +

    Changes Since ZooKeeper 3.4.2

    diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 82ab9024708f8407d7e77695d102e5845d9d5678..ab3d65dcf6afec665171d534a67d66a3a86499d7 100644 GIT binary patch delta 47345 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 f|6h?&Skun0nNeOnBUsU7V~!dJQa* delta 37555 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 IG1X=NAI0IvWMyJ#1s2@g!aIQxLv*qV zzb=Mgb0YutM1ICkJn;sOrcTaIX2vcCZmwKdAb7#Ud^TUdbwH}~;QV8jretjDj5 zA-Fl4-;68H)zZSr(%jL)!p+&h)!50=(ZIse$lSu*(Z$iw!pzChPQiwtl2|S~JFeoA X#G;alqSQ1lGeZ+YE>%@me>W}w4i_!0 delta 183 zcmexw{NH%ObRHuEGXo%@me>W}w+N~`; diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index 730e73ff8d8bf72de3002f64eb188af6f1364637..6651aec8d9a50daf673ac2c8f7f3ba00fb2dfbc2 100644 GIT binary patch delta 186 zcmX@~m+8=7rU`94mWF0VM#dHfCb|aZ8z-IO5wO%XGz&2@vNE=?0t;@=;hn&UAv&3d zUl&7gvnRjQ`gjvdXA3g}LuXSrQ!@icLjwat3m0c2Q#V%&XLA=zBUd{G8-hw=x$Nw? aic1oUN-By{)3{8`%uOx1R8?L5-M9eK1TK*P delta 186 zcmX@~m+8=7rU`94Mh0dEMka;^#<~U;8z-IO5irstO5V8P7^yb~BPL?`j< zVu)?Fn7XUouEz|%2 delta 185 zcmaFV!1$1OHT=;rEdVCrgY>f&PVYNudBP)RJ8ogG(k ZNn%k+MNw)Rm!YY#nK_rLs;j>n7XV^QF0=pu diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index 9ed7a373c5ee1c5d105c4d2e93c941ca9c676c3c..5ee921bb707517bb9c3ded415a3287e7e6874dd0 100644 GIT binary patch delta 160 zcmX?^ax!H?8;_--nURsPg@KW-f%(Qsr+5S`bq&ozjEt;|Ev&$Tn{#+4Fk*;K=Hb`H z5Zvs^zfj59$=Jxrz{%3p#MQvf+|k6q(!kNw*vZ7v(bCA$)Xmk*PQiwdlF7P8G642P BCmH|% delta 160 zcmX?^ax!H?8;_BJnSqgsp@E^UfyKs2r+5U6bPdcx42-Ny46VR|n{#+4Fk*;K=Hb`H z5Zvs^zfj59*~!t;+||s~+04nw#Ldyk(!kQ#)y>q*!qL>s*wV$=PQiwdlF7P8G63n) BCoTX0 diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index c138e2345862c65767f5f3466aef9bd65989bf5e..8d01a0e087de2d52dca68243f6638a2f3dabea28 100644 GIT binary patch delta 209 zcmbQYTWH>Hp$RoSmWF0VM#dHfM!E*(8#@m02w3VGnuQn{Ss7bcfdw~5@J?XF5S{#k zPZvY5*_6NCl%ElZnShviyD2}5P{Z`leij)HBLyQv1p|ek>5ly@A}odqhQ`~Q`&oAL wIvW`oIh&grxtcl|89O<IxI=UH~7?`-anA<7X5K=O|aW0E20On>hw*UYD delta 218 zcmbQYTWH>Hp$RoSMh0dEMka;^hPnn88#@m02pH)an1vV^S(zAGfdw~5@J?XF5S{#k zPZvY5*_6NCl%ElZnShviyD2}5Py>gtg0YE$fkM!9$9@(iAwva2V-Sx^K|$X$*GR$0 zaC>t<%Wht0S2IULR~Kgk3j;T27gtMjO9Mj-S0^V^GiOIvS0_VfI|Um;N~Sl?WswB{ DPnb1@ diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index 39d3f2cecb99510efa811efccf5771692385a54b..d41892e75f34767822fc503a7613d34ca44c5b12 100644 GIT binary patch delta 166 zcmbO|m1Fi)jtTudmWF0VM#dHf#<~XP8)skU5wO%XGz&2@vNE=?0t;@g z#Smfq|u^xq-Q$ z5ZoNd?Wa?ySZf<5|;c97NWa8>-?q=#{r(i=+Ni3J09anKl YVo^y&QED2Op@Ff1IhU%ctG^o;05}mXoB#j- delta 184 zcmewt{x5t&7mtyFnSqgsfvLH!fyKsY=XeB+bPdcx42-Ny46ML{n@e~nFk*;K7U9>$ z5ZoNd?Rdw}u;{pIgYc2i& diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 0f54f691ed64d730518a086d3dc50607e1f9d59d..a3c7e4cb4377bd1d65e80f70f9edf982e7569c56 100644 GIT binary patch delta 162 zcmZ2-opH%^#tD;nEDg~&mac}TZU!bs=EhDI&PK-0PL5{IE~a)0HiVQ+?#Pw_ E0H{4F4gdfE delta 162 zcmZ2-opH%^#tD;nj10^Sj7$s-40R1GHqN`oBVeR!U>0IvWMyJ#1s2@g!aIQxLv*qV zzb=O0=0yG{(atU=mX;=#jwX%<7B0?CF3#qrhR)88=FUKC%*|X4&FvIy2q~G|ku3uN Dn9?Z< diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index d12e4e3430e1405278440137a5fe255110ba1751..5c030f63d8842da58ac1b7f481395450cd61ddd6 100644 GIT binary patch delta 162 zcmZ4Sj&a31#t92}EDgCP5TCZ@(NPL7sluEvHY7M3n0IvWMyJ#1s2@AfOi5ThUjDm zeq9W~&9(eK>CUc3MsAKq<|b}#Muvv2rWS7IrY^2dP8QB)&W4W8hR${hHiVQ+<|&r} E0L{B84gdfE diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index 51355d36fcd..ccf94ac8913 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -18,7 +18,7 @@

    - ZooKeeper 3.4.3 Release Notes + ZooKeeper 3.4.4 Release Notes @@ -42,6 +42,474 @@ These release notes include new developer and user facing incompatibilities, fea Changes +
    +Changes Since 3.4.3 + +Changes Since ZooKeeper 3.4.3 + + + + + Issue + Notes + + + + + + + ZOOKEEPER-1048 + + +addauth command does not work in cli_mt/cli_st + + + + + + ZOOKEEPER-1163 + + +Memory leak in zk_hashtable.c:do_insert_watcher_object() + + + + + + ZOOKEEPER-1210 + + +Can't build ZooKeeper RPM with RPM > + + + + + + ZOOKEEPER-1236 + + +Security uses proprietary Sun APIs + + + + + + ZOOKEEPER-1256 + + +ClientPortBindTest is failing on Mac OS X + + + + + + ZOOKEEPER-1277 + + +servers stop serving when lower 32bits of zxid roll over + + + + + + ZOOKEEPER-1307 + + +zkCli.sh is exiting when an Invalid ACL exception is thrown from setACL command through client + + + + + + ZOOKEEPER-1318 + + +In Python binding, get_children (and get and exists, and probably others) with expired session doesn't raise exception properly + + + + + + ZOOKEEPER-1339 + + +C clien doesn't build with --enable-debug + + + + + + ZOOKEEPER-1344 + + +ZooKeeper client multi-update command is not considering the Chroot request + + + + + + ZOOKEEPER-1354 + + +AuthTest.testBadAuthThenSendOtherCommands fails intermittently + + + + + + ZOOKEEPER-1361 + + +Leader.lead iterates over 'learners' set without proper synchronisation + + + + + + ZOOKEEPER-1380 + + +zkperl: _zk_release_watch doesn't remove items properly from the watch list + + + + + + ZOOKEEPER-1384 + + +test-cppunit overrides LD_LIBRARY_PATH and fails if gcc is in non-standard location + + + + + + ZOOKEEPER-1386 + + +avoid flaky URL redirection in "ant javadoc" : replace "http://java.sun.com/javase/6/docs/api/" with "http://download.oracle.com/javase/6/docs/api/" + + + + + + ZOOKEEPER-1395 + + +node-watcher double-free redux + + + + + + ZOOKEEPER-1403 + + +zkCli.sh script quoting issue + + + + + + ZOOKEEPER-1406 + + +dpkg init scripts don't restart - missing check_priv_sep_dir + + + + + + ZOOKEEPER-1412 + + +java client watches inconsistently triggered on reconnect + + + + + + ZOOKEEPER-1419 + + +Leader election never settles for a 5-node cluster + + + + + + ZOOKEEPER-1427 + + +Writing to local files is done non-atomically + + + + + + ZOOKEEPER-1431 + + +zkpython: async calls leak memory + + + + + + ZOOKEEPER-1437 + + +Client uses session before SASL authentication complete + + + + + + ZOOKEEPER-1463 + + +external inline function is not compatible with C99 + + + + + + ZOOKEEPER-1465 + + +Cluster availability following new leader election takes a long time with large datasets - is correlated to dataset size + + + + + + ZOOKEEPER-1466 + + +QuorumCnxManager.shutdown missing synchronization + + + + + + ZOOKEEPER-1471 + + +Jute generates invalid C++ code + + + + + + ZOOKEEPER-1480 + + +ClientCnxn(1161) can't get the current zk server add, so that - Session 0x for server null, unexpected error + + + + + + ZOOKEEPER-1483 + + +Fix leader election recipe documentation + + + + + + ZOOKEEPER-1489 + + +Data loss after truncate on transaction log + + + + + + ZOOKEEPER-1490 + + +If the configured log directory does not exist zookeeper will not start. Better to create the directory and start + + + + + + ZOOKEEPER-1493 + + +C Client: zookeeper_process doesn't invoke completion callback if zookeeper_close has been called + + + + + + ZOOKEEPER-1494 + + +C client: socket leak after receive timeout in zookeeper_interest() + + + + + + ZOOKEEPER-1496 + + +Ephemeral node not getting cleared even after client has exited + + + + + + ZOOKEEPER-1501 + + +Nagios plugin always returns OK when it cannot connect to zookeeper + + + + + + ZOOKEEPER-1514 + + +FastLeaderElection - leader ignores the round information when joining a quorum + + + + + + ZOOKEEPER-1521 + + +LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits + + + + + + ZOOKEEPER-1522 + + +intermittent failures in Zab test due to NPE in recursiveDelete test function + + + + + + ZOOKEEPER-1536 + + +c client : memory leak in winport.c + + + + + + ZOOKEEPER-1321 + + +Add number of client connections metric in JMX and srvr + + + + + + ZOOKEEPER-1377 + + +add support for dumping a snapshot file content (similar to LogFormatter) + + + + + + ZOOKEEPER-1389 + + +it would be nice if start-foreground used exec $JAVA in order to get rid of the intermediate shell process + + + + + + ZOOKEEPER-1390 + + +some expensive debug code not protected by a check for debug + + + + + + ZOOKEEPER-1433 + + +improve ZxidRolloverTest (test seems flakey) + + + + + + ZOOKEEPER-1454 + + +Document how to run autoreconf if cppunit is installed in a non-standard directory + + + + + + ZOOKEEPER-1481 + + +allow the C cli to run exists with a watcher + + + + + + ZOOKEEPER-1497 + + +Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) + + + + + + ZOOKEEPER-1503 + + +remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest + + + + + + ZOOKEEPER-1510 + + +Should not log SASL errors for non-secure usage + + + + + + ZOOKEEPER-1450 + + +Backport ZOOKEEPER-1294 fix to 3.4 and 3.3 + + + + +
    +
    +
    Changes Since ZooKeeper 3.4.2 From 10705ba6e763eade42e8a2e54acabc181f5b2400 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 17 Sep 2012 07:56:19 +0000 Subject: [PATCH 114/444] ZOOKEEPER-1496. Ephemeral node not getting cleared even after client has exited (Rakesh R via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1386497 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/SessionTrackerImpl.java | 13 +- .../zookeeper/server/SessionTrackerTest.java | 156 ++++++++++++++++++ 3 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java diff --git a/CHANGES.txt b/CHANGES.txt index dccd13a180b..2dc035ab0ba 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -111,6 +111,9 @@ BUGFIXES: ZOOKEEPER-1483. Fix leader election recipe documentation. (Michi Mutsuzaki via mahadev) + ZOOKEEPER-1496. Ephemeral node not getting cleared even after client has + exited (Rakesh R via mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java index cbae57af499..31f278528d9 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java @@ -150,7 +150,7 @@ synchronized public void run() { set = sessionSets.remove(nextExpirationTime); if (set != null) { for (SessionImpl s : set.sessions) { - sessionsById.remove(s.sessionId); + setSessionClosing(s.sessionId); expirer.expire(s); } } @@ -170,7 +170,8 @@ synchronized public boolean touchSession(long sessionId, int timeout) { + Long.toHexString(sessionId) + " with timeout " + timeout); } SessionImpl s = sessionsById.get(sessionId); - if (s == null) { + // Return false, if the session doesn't exists or marked as closing + if (s == null || s.isClosing()) { return false; } long expireTime = roundToInterval(System.currentTimeMillis() + timeout); @@ -212,7 +213,11 @@ synchronized public void removeSession(long sessionId) { + Long.toHexString(sessionId)); } if (s != null) { - sessionSets.get(s.tickTime).sessions.remove(s); + SessionSet set = sessionSets.get(s.tickTime); + // Session expiration has been removing the sessions + if(set != null){ + set.sessions.remove(s); + } } } @@ -266,7 +271,7 @@ synchronized public void checkSession(long sessionId, Object owner) throws Keepe synchronized public void setOwner(long id, Object owner) throws SessionExpiredException { SessionImpl session = sessionsById.get(id); - if (session == null) { + if (session == null || session.isClosing()) { throw new KeeperException.SessionExpiredException(); } session.owner = owner; diff --git a/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java b/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java new file mode 100644 index 00000000000..61072e677d9 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java @@ -0,0 +1,156 @@ +/** + * 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.server; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.Assert; + +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.server.SessionTrackerImpl.SessionImpl; +import org.apache.zookeeper.test.ClientBase; +import org.junit.Test; + +/** + * Testing zk client session logic in sessiontracker + */ +public class SessionTrackerTest extends ZKTestCase { + + private final long sessionId = 339900; + private final int sessionTimeout = 3000; + private FirstProcessor firstProcessor; + private CountDownLatch latch; + + /** + * Verify the create session call in the Leader.FinalRequestProcessor after + * the session expiration. + */ + @Test(timeout = 20000) + public void testAddSessionAfterSessionExpiry() throws Exception { + ZooKeeperServer zks = setupSessionTracker(); + + latch = new CountDownLatch(1); + zks.sessionTracker.addSession(sessionId, sessionTimeout); + SessionTrackerImpl sessionTrackerImpl = (SessionTrackerImpl) zks.sessionTracker; + SessionImpl sessionImpl = sessionTrackerImpl.sessionsById + .get(sessionId); + Assert.assertNotNull("Sessionid:" + sessionId + + " doesn't exists in sessiontracker", sessionImpl); + + // verify the session existence + Object sessionOwner = new Object(); + sessionTrackerImpl.checkSession(sessionId, sessionOwner); + + // waiting for the session expiry + latch.await(sessionTimeout * 2, TimeUnit.MILLISECONDS); + + // Simulating FinalRequestProcessor logic: create session request has + // delayed and now reaches FinalRequestProcessor. Here the leader zk + // will do sessionTracker.addSession(id, timeout) + sessionTrackerImpl.addSession(sessionId, sessionTimeout); + try { + sessionTrackerImpl.checkSession(sessionId, sessionOwner); + Assert.fail("Should throw session expiry exception " + + "as the session has expired and closed"); + } catch (KeeperException.SessionExpiredException e) { + // expected behaviour + } + Assert.assertTrue("Session didn't expired", sessionImpl.isClosing()); + Assert.assertFalse("Session didn't expired", sessionTrackerImpl + .touchSession(sessionId, sessionTimeout)); + Assert.assertEquals( + "Duplicate session expiry request has been generated", 1, + firstProcessor.getCountOfCloseSessionReq()); + } + + /** + * Verify the session closure request has reached PrepRequestProcessor soon + * after session expiration by the session tracker + */ + @Test(timeout = 20000) + public void testCloseSessionRequestAfterSessionExpiry() throws Exception { + ZooKeeperServer zks = setupSessionTracker(); + + latch = new CountDownLatch(1); + zks.sessionTracker.addSession(sessionId, sessionTimeout); + SessionTrackerImpl sessionTrackerImpl = (SessionTrackerImpl) zks.sessionTracker; + SessionImpl sessionImpl = sessionTrackerImpl.sessionsById + .get(sessionId); + Assert.assertNotNull("Sessionid:" + sessionId + + " doesn't exists in sessiontracker", sessionImpl); + + // verify the session existence + Object sessionOwner = new Object(); + sessionTrackerImpl.checkSession(sessionId, sessionOwner); + + // waiting for the session expiry + latch.await(sessionTimeout * 2, TimeUnit.MILLISECONDS); + + // Simulating close session request: removeSession() will be executed + // while OpCode.closeSession + sessionTrackerImpl.removeSession(sessionId); + SessionImpl actualSession = sessionTrackerImpl.sessionsById + .get(sessionId); + Assert.assertNull("Session:" + sessionId + + " still exists after removal", actualSession); + } + + private ZooKeeperServer setupSessionTracker() throws IOException { + File tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + zks.setupRequestProcessors(); + firstProcessor = new FirstProcessor(zks, null); + zks.firstProcessor = firstProcessor; + + // setup session tracker + zks.createSessionTracker(); + zks.startSessionTracker(); + return zks; + } + + // Mock processor used in zookeeper server + private class FirstProcessor extends PrepRequestProcessor { + private volatile int countOfCloseSessionReq = 0; + + public FirstProcessor(ZooKeeperServer zks, + RequestProcessor nextProcessor) { + super(zks, nextProcessor); + } + + @Override + public void processRequest(Request request) { + // check session close request + if (request.type == OpCode.closeSession) { + latch.countDown(); + countOfCloseSessionReq++; + } + } + + // return number of session expiry calls + int getCountOfCloseSessionReq() { + return countOfCloseSessionReq; + } + } +} From d612bb4cca0f916b055ed49bc4845dc71b699740 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 17 Sep 2012 08:10:08 +0000 Subject: [PATCH 115/444] Add Release Note for ZOOKEEPER-1496. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1386502 13f79535-47bb-0310-9956-ffa450edef68 --- docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17115 -> 17115 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13517 -> 13517 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12462 -> 12462 bytes docs/recipes.pdf | Bin 31044 -> 31044 bytes docs/releasenotes.html | 12 ++++++++++++ docs/releasenotes.pdf | Bin 116281 -> 116611 bytes docs/zookeeperAdmin.pdf | Bin 72882 -> 72882 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133787 -> 133787 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27556 -> 27556 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes .../content/xdocs/releasenotes.xml | 9 +++++++++ 22 files changed, 21 insertions(+) diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index a52a7f5fbffdec04e93cf4250ce2e3abdbeb8fb2..f4aa35dffab3298b37a30e0b7b4890afe47e14e0 100644 GIT binary patch delta 160 zcmaE#{XTobRCaR%Ljwy#lZ|uk@^G1l7#La^SXddFY;NYA#)!n9tjuqW#NC|0Z>SdM zW^88e?B;6e=<4d?Sb$ zVrlAP>E>c&=4#<+W@hB-T2$x z_!+mm@iSd8aCS9uHa0XecQbG_HL-9ua5J-XGc|CvbTKz^b1`r>bhT5kA*5t_qyv*I E0IY5$i2wiq delta 148 zcmey?!1=9#b3zBZnURsPg@MV&{tG-@W+6sKR>l@q1}2*`d8aWV@h5Zg8zXU>-T2$x z_!+mm@iSd8a5l3rurzTrw={5abTczHH*s`zbTl$Gu&^{YH!(IaGqO{#A*5t_qyv*I E0GiGu?EnA( diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 230791c62b5e03562a854bca9536ed4aa573e9b3..12335f65d1181b222af4516f819490e0be15c32d 100644 GIT binary patch delta 139 zcmZoY%-DLEal%w~a|1&I3q#Y5bMEqRnTHq{S{Yba8Jccx=AFif#GkCpZ;Zs1t|bV(#eXW@_wgVr*<~;%MRQYT#_*}+CSZ02ZWY3X8WX5?gSZeVT(B+VTSO)U(aEuEZA>=bMWDVeSVrXb=YG~@}W@h2+>}q0QVB+H9W@=&RVrJ%QXyoW@r(i=+Ni3J09anKlVo^y&QED2O Qp^=e+DVM6MtG^o;0EyKo%K!iX diff --git a/docs/index.pdf b/docs/index.pdf index a357a906c03ed2c88afc4859d7290bf7ebee6ac1..4a2f5c7173154d39f8c15076919f27c36dac87ba 100644 GIT binary patch delta 137 zcmX?`c{X#xMs{-pLjwy#qm4U$@^G1l7#La^SXdbvZQjj0jS-1IIhx-XiMx3Q|1}k7 tXEP%UGoWU3b0bGr7jsuvLvu4n7bjCA6E|aLOCxhT1sg(2CT}#60RW&_B%%NS delta 137 zcmX?`c{X#xMs_nJBV%(@^Nl-x@^G1j7#Ud^n_HQhZ{E#2jS-1IIhx-XiMx3Q|1}k7 uV@m^LLn9X_OEVJ}S7SE|V-pJtR}*JbCld=pQ$r&oI|Um;N+xeKkpTdxf+TDJ diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index df854e146fb9630dbe9785a7a4ab71e1df020cd2..9efd27cb3aba94a2a6fe3d344009e3e15d7b535a 100644 GIT binary patch delta 139 zcmeC`VCw2%n$XQ|ZeVC&VQ9W_(q$el^AH0=D+3EFL-Wn~ywezw_>=khjgh#UefZ-` voQ*6k&0Snf9F5(~T}%udU7Z|VEnF>KE!>=khjgh#UefZ-` uoDD22jon-=4J?49sfDwFv7w8Lv!$zrvy+*lv!#oHoq`P^C6nJZ%K!iYT_foL diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index 114a0e90199f28d2350eb861a464b6518d73b94c..758b7f4c07eeef53325fe71367401d7b2ef1a3c9 100644 GIT binary patch delta 161 zcmZ3NxGr(RWOj1{Ljwy#(~Yxk^KhAm7#La^SXddFZm#E@#)!n9EYEL@#N8aj|5+j4 z+|AL#)WzJ`$lS@y%-Gn}(Za>V&Be&f%+l4=+0@*?PQiwtl2|S~JFeoA#G;alqSQ1l QLqih-b1qd?SARDy0KBFtHUIzs delta 161 zcmZ3NxGr(RWOg$nBV!8#!;Q0U^KhAk7#Ud^TUZ$wZm#E@#)!n9EYEL@#N8aj|5+j4 z*~rn<*bxX_Tucm%O-!9#P2JqgTwP2|j165}4IS+iYzQie<+8KmDlSPZDyb++P2(~& PG%+yeQdM>JcjE#8%`GW} diff --git a/docs/recipes.pdf b/docs/recipes.pdf index ea162d306cd98bee1a9a690daa0b75f79bb070d7..d069bfaa4765088f85f8d26bf3377cc69ca47972 100644 GIT binary patch delta 138 zcmX@|iSfuM#tBo{%?%6htDB>_qpORtiJP&7vxS?Zp{1Lfxr?2G4FM&S)hlHIwvZ&m delta 138 zcmX@|iSfuM#tBo{&5VqU%}p&f&c4ILWfo#&WMyn_Woogxk#`y+5`VHHzcCVba~ywr vrjwDep{1LpnTwOFiLr&bv7xc4iG`z!fq{XGv89=trHh?{4FM&S)hlHIsU;)? diff --git a/docs/releasenotes.html b/docs/releasenotes.html index f6161db1f92..9d5197bd52c 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -261,6 +261,18 @@

    Changes Since 3.4.3

    + + + + + ZOOKEEPER-1496 + + +Ephemeral node not getting cleared even after client has exited + + + + diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index ab3d65dcf6afec665171d534a67d66a3a86499d7..2756908646be024317c2d872d473c64f29e47a8d 100644 GIT binary patch delta 20552 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

    ?p^Nz}r#Z9zU%fTNRwiy; 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 K+-d4q<^KWO2PIbk diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index 486f97dc1939a6f9af8ca7a7d848402ed741731b..c51b37671716a534da14f054a1d61a633c05caa0 100644 GIT binary patch delta 167 zcmdnAlV#IRmI+hX%?%6OLKDrS64$bLo+)C8-hw=x$Nw?ic1oU WN-By{)40qmjLl5BR8?L5-M9eTQ7YvC diff --git a/docs/zookeeperHierarchicalQuorums.pdf b/docs/zookeeperHierarchicalQuorums.pdf index 83f1853f1d9a7e46386335ac02e033988d7f4580..56bad94b68774d4404363d9a01af63b7f6517468 100644 GIT binary patch delta 160 zcmexw{NH%OOm=evLjwy#(~S!r@^G1l7#La^SXddFZtmut#)!n9tj%wX#NC|1Z^;#B zZed{PWNu(->FVa}X6))_>}F|XZe;9cZt3Q1Y-ny_r(i=+Ni3J09anKlVo^y&QED2O PnW2dxm#V6(zZ(|-l>jK> delta 160 zcmexw{NH%OOm;IPBV!8#!;K3b@^G1j7#Ud^TUZ$wZtmut#)!n9tj%wX#NC|1Z^;$s zYH8tQY3^uY;pS}MYV73bXkcM!WNu;Z=;CN-Vdms$r(i=+Ni3J09anKlVo^y&QED2O PnW2dxm#V6(zZ(|-rZOoa diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index 6651aec8d9a50daf673ac2c8f7f3ba00fb2dfbc2..dec57302720b607e7f7fba17303c29711131cffb 100644 GIT binary patch delta 163 zcmX@~m+8=7rU@PF<_3la7KWA^`!DctnTHq{S{Yba8Cq`6-DhU7p delta 163 zcmaFV!1$tO5rk0yyd8aWV@hAV~Ge+WWw&Fi& z5N}{%?rLOaZen8KXzuD}WMW}pY-s4}=4R?-VBuosW?`pbLr_U9mz^C~aY28LD!7FLGln=^T*F(UCNbMhM_aW}j1FHv$b tv~+ZHH8pZ}b~ABvGBY)Ea&mStGc++VaWgkIHLx_ZQ?MbRWU`KtECA<^B76V< delta 136 zcmX?^ax!H?2fLY(k+Fq=(Z>D@JX~fWMn+b~7FGsEn=^T*F(UCNbMhM_aW}j1FHv%G tGB$EDaI$nYaW!x=cQi4uG;lOEb~15vv^26bb#pbdQ?MbRWU`KtECAb2B3J+b diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index 8d01a0e087de2d52dca68243f6638a2f3dabea28..5fe46e30e8ea8c7293211aa82d5c8fd88ea5dd3e 100644 GIT binary patch delta 172 zcmbQYTWH>Hp$T>D<_3la7KUaUTaWQ@nTHq{S{Yba8JcYl<(v|)O3Ka1*gr+yZh?H&Cr+jyPLoJ~#4T+LjK&D_k)oQ#}}EzDh= aoSiMr4J^#eT`dgl6l@47ncg^;MHT=7DJ@6< delta 172 zcmbQYTWH>Hp$T>DW=2NF76wKeTaWQ@nS~e`Ss7bc85nI2<(v|)N-Ka2Qur+yZ#?H&Cr+jyOgjEtPkO^sYlos5i~oL$Y_oDB?2 ajSU^$j7yaxykEH?%Y{Hg>Xfb~Q9}Gj?;cQ?MbRWb#98SpfE{BNYGu delta 136 zcmewt{x5t&54)L>k+Hd{#m323c(}|$jEt;|&8PVv7LeqAtjSLvSk3KUnH{t delta 139 zcmZ2-opH%^#tBo{&5VqUEewn{&c4ILWfo#&WMyn&Wni?qk#`y+5`VHHzcCVba~%Kk wXlGYLV^=dHLt`gXOIJfvHvk diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index 5c030f63d8842da58ac1b7f481395450cd61ddd6..4d800a61062b90e66b27a52f6fb98828f4bb98ee 100644 GIT binary patch delta 139 zcmZ4Sj&a31#tDnq%?%6rSueq$u=<|=;w ubY}w-M?(Wsb4vqbLkl1rSueq$u=<|=;w wbY}}E6H{XsCr3*&S7SpH3riO_BLg=xXD4GdGfO)K8$wDZ^OVZ~0LkPePXGV_ diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index ccf94ac8913..d3eca954098 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -55,6 +55,15 @@ These release notes include new developer and user facing incompatibilities, fea + + + + ZOOKEEPER-1496 + + +Ephemeral node not getting cleared even after client has exited + + From 7e8867d46f9309f55d945c5fa9a182162844955f Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 17 Sep 2012 08:16:27 +0000 Subject: [PATCH 116/444] Preparing for release 3.4.4 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1386503 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- build.xml | 2 +- src/c/configure.ac | 2 +- src/c/include/zookeeper_version.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2dc035ab0ba..70c0559064c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Release 3.4.4 - TBD +Release 3.4.4 - 2012-09-17 Backward compatible changes: diff --git a/build.xml b/build.xml index 4d5bf245a11..c72ed0a9b82 100644 --- a/build.xml +++ b/build.xml @@ -24,7 +24,7 @@ - + diff --git a/src/c/configure.ac b/src/c/configure.ac index 7e0bfb9ef14..a3e53eec045 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.3,[user@zookeeper.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.4,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 2afb34f7725..9fe83d3a904 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 3 +#define ZOO_PATCH_VERSION 4 #ifdef __cplusplus } From 1e4b84f99387d54abb89fcbc2e71469f59ff7c75 Mon Sep 17 00:00:00 2001 From: Henry Robinson Date: Fri, 21 Sep 2012 23:04:12 +0000 Subject: [PATCH 117/444] ZOOKEEPER-1376. zkServer.sh does not correctly check for $SERVER_JVMFLAGS (Skye W-M via henryr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1388718 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 9 +++++++++ bin/zkServer.sh | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 70c0559064c..d43c4ee0396 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,12 @@ +Release 3.4.5 - TBD + +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-1376. zkServer.sh does not correctly check for + $SERVER_JVMFLAGS (Skye W-M via henryr) + Release 3.4.4 - 2012-09-17 Backward compatible changes: diff --git a/bin/zkServer.sh b/bin/zkServer.sh index 3f27f6a633c..aa6cbaa45f4 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -53,7 +53,7 @@ else . "$ZOOBINDIR"/zkEnv.sh fi -if [ "x$SERVER_JVMFLAGS" ] +if [ "x$SERVER_JVMFLAGS" != "x" ] then JVMFLAGS="$SERVER_JVMFLAGS $JVMFLAGS" fi From 749e7be3368e6f23fdfca205cb2b7a98603b5c6e Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 28 Sep 2012 17:03:55 +0000 Subject: [PATCH 118/444] ZOOKEEPER-1550. ZooKeeperSaslClient does not finish anonymous login on OpenJDK. (Eugene Koontz via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1391555 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ src/java/main/org/apache/zookeeper/Login.java | 4 ++ .../main/org/apache/zookeeper/ZooKeeper.java | 5 +++ .../zookeeper/client/ZooKeeperSaslClient.java | 18 +++++---- .../test/SaslAuthDesignatedClientTest.java | 40 +++++++++++-------- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d43c4ee0396..941f52baa28 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -123,6 +123,9 @@ BUGFIXES: ZOOKEEPER-1496. Ephemeral node not getting cleared even after client has exited (Rakesh R via mahadev) + ZOOKEEPER-1550. ZooKeeperSaslClient does not finish anonymous login on + OpenJDK. (Eugene Koontz via mahadev) + IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index 54b4a2303c9..31ac39a2878 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -276,6 +276,10 @@ public Subject getSubject() { return subject; } + public String getLoginContextName() { + return loginContextName; + } + private synchronized LoginContext login(final String loginContextName) throws LoginException { if (loginContextName == null) { throw new LoginException("loginContext name (JAAS file section header) was null. " + diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index acfcd729326..86c619d8d01 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -23,6 +23,7 @@ import org.apache.zookeeper.client.ConnectStringParser; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.StaticHostProvider; +import org.apache.zookeeper.client.ZooKeeperSaslClient; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; @@ -94,6 +95,10 @@ public class ZooKeeper { Environment.logEnv("Client environment:", LOG); } + public ZooKeeperSaslClient getSaslClient() { + return cnxn.zooKeeperSaslClient; + } + private final ZKWatchManager watchManager = new ZKWatchManager(); List getDataWatches() { diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 4ce54770ded..6087e99b2d3 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -76,10 +76,10 @@ public SaslState getSaslState() { return saslState; } - private String loginContext; - public String getLoginContext() { - return loginContext; + if (login != null) + return login.getLoginContextName(); + return null; } public ZooKeeperSaslClient(final String serverPrincipal) @@ -193,7 +193,6 @@ synchronized private SaslClient createSaslClient(final String servicePrincipal, if (LOG.isDebugEnabled()) { LOG.debug("JAAS loginContext is: " + loginContext); } - this.loginContext = loginContext; // note that the login object is static: it's shared amongst all zookeeper-related connections. // createSaslClient() must be declared synchronized so that login is initialized only once. login = new Login(loginContext, new ClientCallbackHandler(null)); @@ -485,9 +484,14 @@ public boolean clientTunneledAuthenticationInProgress() { // variable or method in this class to determine whether the client is // configured to use SASL. (see also ZOOKEEPER-1455). try { - if ((System.getProperty(Environment.JAAS_CONF_KEY) != null) || - (javax.security.auth.login.Configuration.getConfiguration() != null)) { - // Client is configured to use SASL. + if ((System.getProperty(Environment.JAAS_CONF_KEY) != null) || + ((javax.security.auth.login.Configuration.getConfiguration() != null) && + (javax.security.auth.login.Configuration.getConfiguration(). + getAppConfigurationEntry(System. + getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,"Client")) + != null))) { + // Client is configured to use a valid login Configuration, so + // authentication is either in progress, successful, or failed. // 1. Authentication hasn't finished yet: we must wait for it to do so. if ((isComplete() == false) && diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java index b27c9873586..9e28278f5fc 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java @@ -21,13 +21,10 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.client.ZooKeeperSaslClient; import org.junit.Assert; @@ -68,20 +65,6 @@ to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NA } } - private AtomicInteger authFailed = new AtomicInteger(0); - - private class MyWatcher extends CountdownWatcher { - @Override - public synchronized void process(WatchedEvent event) { - if (event.getState() == KeeperState.AuthFailed) { - authFailed.incrementAndGet(); - } - else { - super.process(event); - } - } - } - @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); @@ -95,4 +78,27 @@ public void testAuth() throws Exception { zk.close(); } } + + @Test + public void testSaslConfig() throws Exception { + ZooKeeper zk = createClient(); + try { + zk.getChildren("/", false); + Assert.assertFalse(zk.getSaslClient(). + clientTunneledAuthenticationInProgress()); + Assert.assertEquals(zk.getSaslClient().getSaslState(), + ZooKeeperSaslClient.SaslState.COMPLETE); + Assert.assertNotNull( + javax.security.auth.login.Configuration.getConfiguration(). + getAppConfigurationEntry("MyZookeeperClient")); + Assert.assertSame(zk.getSaslClient().getLoginContext(), + "MyZookeeperClient"); + } catch (KeeperException e) { + Assert.fail("test failed :" + e); + } finally { + zk.close(); + } + } + + } From 3c832a08d75db058ba327097084905771ed67b44 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sun, 30 Sep 2012 17:26:47 +0000 Subject: [PATCH 119/444] Fixing the CHANGES.txt file git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1392079 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 941f52baa28..3b4ce9bf437 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,10 @@ BUGFIXES: ZOOKEEPER-1376. zkServer.sh does not correctly check for $SERVER_JVMFLAGS (Skye W-M via henryr) + ZOOKEEPER-1550. ZooKeeperSaslClient does not finish anonymous login on + OpenJDK. (Eugene Koontz via mahadev) + + Release 3.4.4 - 2012-09-17 Backward compatible changes: @@ -123,9 +127,6 @@ BUGFIXES: ZOOKEEPER-1496. Ephemeral node not getting cleared even after client has exited (Rakesh R via mahadev) - ZOOKEEPER-1550. ZooKeeperSaslClient does not finish anonymous login on - OpenJDK. (Eugene Koontz via mahadev) - IMPROVEMENTS: ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA From 3e8141d7f4a59a55f0789573bdb69b08f0d964f7 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sun, 30 Sep 2012 17:49:02 +0000 Subject: [PATCH 120/444] Preparing for release 3.4.5 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1392086 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17115 -> 17115 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13517 -> 13517 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12462 -> 12462 bytes docs/recipes.pdf | Bin 31044 -> 31044 bytes docs/releasenotes.html | 53 +++++++++++++++++- docs/releasenotes.pdf | Bin 116611 -> 118218 bytes docs/zookeeperAdmin.pdf | Bin 72882 -> 72882 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.pdf | Bin 133787 -> 133787 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27556 -> 27556 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes src/c/configure.ac | 2 +- src/c/include/zookeeper_version.h | 2 +- .../content/xdocs/releasenotes.xml | 38 ++++++++++++- 25 files changed, 91 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3b4ce9bf437..3c1e540cf90 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Release 3.4.5 - TBD +Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index f4aa35dffab3298b37a30e0b7b4890afe47e14e0..3a8f32d252bfb03e9d4214364a4c79c70400c183 100644 GIT binary patch delta 165 zcmaE#{XTob6b@qpLjx02W8;l;?(lFM8-y4dSeckw85?hI=AFujEHGJ_-w2t%If36m zE#Aq=!r0Nm(!kiw*wo0#&B(ye(a6BW*u=!d$k5W%+|5qGhMqq%c&9NU@h5Ze8zXU>UHRKx z`5Cvn@-tmDaI$nWGBY$cbTlw@v9K^THE=XDv~)5xHn21>Ff}uAagaeZt E09KhKMF0Q* delta 147 zcmey?!1=9#b3!}2p}B#ffra75z6(5DhUOs#hE@g^R)(81c&9NU@h5Ze8zXU>UHRKx z`5Cvn@-tmDaB?+qHa0XecQbG_HL-9ua5J-XGc|CvbTKz^b1`r>bhT5kA)sV>gaeZt E0C!C#ApigX diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 12335f65d1181b222af4516f819490e0be15c32d..6487743a04ebf5c095a80e4c1137dea0cf4bc13f 100644 GIT binary patch delta 139 zcmZoY%-DLEal#aKV*^726I0`jv+we785@Kc8d#Z_S{ZL{;+@8b#GkChZ;Zs<9M69| u(Amt$(agla0%(GnrGcZPv4y3%o3o3ti?N%Li;1zZqn&~cAtjSD6J-EJRU>); delta 139 zcmZoY%-DLEal#aKLvsT|0}I2Av+we78JdR}7+M)vSQ&0^;+@8b#GkChZ;Zs<9M69| v(Amw+#mv#w%+=J=%*4&y#m&^&&A`RP+{MV$)zHYu+1O6OhLDoUnTaw0a<(K) diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index f31361b2d5f4686e3532bed101b5b2569c633a9c..311630781233f822837b477a1c99122ff5dacfc9 100644 GIT binary patch delta 143 zcmccJ%6Pk#al#Z1V*^726H{aJjdSksa2p$h7#diam|7W|Z*JzD%7`p5S()DmnZG%K xU&h$k!pP9v(9qn%)Xl)a%*4>u*wEO?(7?^q)YaA8*udPvPQiwdlF9p>WB{FOBYgk> delta 143 zcmccJ%6Pk#al#Z1LvsT|0}DfojdSksa2uM37#La^SXdcaY;NYA%7`p5S()DmnZG%K xU&h$k)!5m|(a_S>)Xc=((ap`&*xAI`*xba?!r9fp*~H1&PQiwdlF9p>WB}2fB&`4d diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index 22cee24497b3265a3846a10db9cadf1865e79014..39ed2deb8384d953967f7f0491bce9b8c05cdbdd 100644 GIT binary patch delta 161 zcmbP`J|TTVFT1gUp@E61@y03Fc({xWLJSS8OiZneH<$2EV?^Rl7U4HW;%*M)f1n)i zYT#mKVqoTIY+-C_X=du`W^U}{=4fW&X5s2)YG&kQr(i=+Ni3J09anKlVo^y&QED2O Qp^=e+DVM6MtG^o;0DPw?=Kufz delta 161 zcmbP`J|TTVFT0_+fuVtg;l?S~c(@GBLktY93@oe+H<$2EV?^Rl7U4HW;%*M)f1n(1 zYG7>SVrXb=YG~@}W@h2+7jjxJ87Mka2?&Xz{zb_zDm_?1lFU?K|u&@m+I diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index 9efd27cb3aba94a2a6fe3d344009e3e15d7b535a..3717d795ada5b1d9e43128ec182a537dad03a857 100644 GIT binary patch delta 139 zcmeC`VCw2%n$X2=Y+z_$Vrsl`;$=kgjgh#Uz4;SL voDJPvEDYQ%%p8r)P2HSa44e!tEzJxp9Gy)Zo!v}LP3;tH2q~HTu2}{E?x7=< delta 139 zcmeC`VCw2%n$X2=Xl`I=U}3m%;$=kgjgh#Uz4;SL voQ*6k&0Snf9F5(~T}%udU7Z|VEnF>KE!>&gA diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index 758b7f4c07eeef53325fe71367401d7b2ef1a3c9..221c303717d4aa66c184355750465e3d517d114b 100644 GIT binary patch delta 161 zcmZ3NxGr(RBz9v1Ljx02V&Be&f%+l4=+0@*?PQiwtl2|S~JFeoA#G;alqSQ1l QLqih-b1qd?SARDy0I}sMA^-pY diff --git a/docs/recipes.pdf b/docs/recipes.pdf index d069bfaa4765088f85f8d26bf3377cc69ca47972..503806f946d628f9e1ace55026dddfe1751dc7fb 100644 GIT binary patch delta 143 zcmX@|iSfuM#tD--j13G8OiYc9HqO4y!)Wd7zj y{`O2~Q#ThE7jriwV>dS=7Z+!9OE*_X7b6!Fb4N=TGh<5&I|Um;N+xSm$^ZajjwS5? delta 143 zcmX@|iSfuM#tD--49yJ;4J-_eH_pDz!)<6DVqj=xU}0rwyt$EgDkHMMWJP`>Wd7zj y{`O2~b8}Y{S5spbV -ZooKeeper 3.4.4 Release Notes +ZooKeeper 3.4.5 Release Notes @@ -202,11 +202,14 @@ PDF -icon
    PDF

    -

    ZooKeeper 3.4.4 Release Notes

    +

    ZooKeeper 3.4.5 Release Notes

    + +

    Changes Since 3.4.4

    +
    + + + +Changes Since ZooKeeper 3.4.4 + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.4
    IssueNotes
    + + ZOOKEEPER-1550 + +ZooKeeperSaslClient does not finish anonymous login on OpenJDK +
    + + ZOOKEEPER-1376 + +zkServer.sh does not correctly check for $SERVER_JVMFLAGS +
    +
    +

    Changes Since 3.4.3

    diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 2756908646be024317c2d872d473c64f29e47a8d..029fd3e0023f67e9eaebb4e47b73d0b5585581d7 100644 GIT binary patch literal 118218 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

    wdBz;td7w?qKUvi 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 XMI{wQscBqh7RF|#T&k+B{%%|VdKD}6 delta 171 zcmdnAlV#IRmI;$N49yJ;4J-`JHqO4y!)<6DVqj=xU}0rwwz-jaDkHMMWJP`>WPWoT z|Mob3#;-i_My^ICj;<~)#wL~qCYBZ!E*3`4hK}Y=Zf35oW~P>Ib_zBGmBez{*>M$@ ZBo>ua6s4wdnOPW{nR2PBy863u0RV-uEENC% diff --git a/docs/zookeeperHierarchicalQuorums.pdf b/docs/zookeeperHierarchicalQuorums.pdf index 56bad94b68774d4404363d9a01af63b7f6517468..87a24e866ee7c265f8db25ae9bebee0baca749fb 100644 GIT binary patch delta 164 zcmexw{NH%O3=U%hLjx02W0Q>w9`JA*8-y4dSeckw8JleG=AFujEHGJ{-w2t%IfLJl zE6&2v*~HSx%-Pk@(89>V%*EB!&B@f%!qVKy!p+gu%*al`hM$=(#YJ%*v;J1&Dq${+`>-5hMFi-Dn|iL;5Lxr?cpv9Y6zsga3=xv8U_f{g(oC9zy~c3j0JiA5z9 UMX70ACT8ZQmRzc;uKsRZ0K!iyqyPW_ delta 166 zcmX@~m+8=7rU~sFhUNx_1{Q{v8~e}ma2uM37#La^SXdcaZqDSL%7`p5nUmiLnZMbM zUv7Pzsf)R@v#Yb2fvKaRv6HERv5|p`v9r0Gk+Y?xqouK(f{g(oC9zy~c3j0JiA5z9 UMX70ACT8ZQmRzc;uKsRZ0K?lVqyPW_ diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf index f93e999f538612c89a890b0eff501694375f2bb1..307675f71fb0fba414e5d13a2f91d712dbffefa5 100644 GIT binary patch delta 167 zcmaFV!1$x{C%-?Lq zf6^e{*~!V!#mUvt)Wy=k#mU9cz`)Sl5eS?t%pHxKom}k{YzQie<+8KmDlSPZDyb++ TP2(~&H8wNnQdM>JcjE#8`u;1e delta 167 zcmaFV!1$x{C%-?Lq zf6^e{+}y&%(AdSy+}Xm##L2+i$koNr!pYgt&CJLYXsV-~f(=0>v0Qd`T*W1cMI{wQ TscBq>rp9LGT&k+B{%%|V#-1w8 diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index b096cca1216fd5ad1a3e172bcb20a74d29898c0b..33732d89ce940665ccc22fb2ffe7086bcf558811 100644 GIT binary patch delta 141 zcmX?^ax!H?JBP7>p@E61vDwD{^E}+f1|fz9Rwkxa#%7x{d8aZW3ryzZH$vubcH>{7 ugHr>=wbw9JDWN=T3VVI8M`^VIJsC@*eTc$QZiZBNCp695F-@; delta 141 zcmX?^ax!H?JBOjUfuVtgq4~!C^E}*!<{<`#Rt6SUhUS|yd8aZW3ryzZH$vubcH>{7 v|$nUVq)TEZft5`X=bNjLrBSFT_YI)gNq|~ diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index 5fe46e30e8ea8c7293211aa82d5c8fd88ea5dd3e..a9aeb8e9ceb1ca91d162ef97c69188933da8edfe 100644 GIT binary patch delta 195 zcmbQYTWH>Hp$WAd#s-E4CZ@)w8(WX^a2p$h7#diam|7W|ZVu(0%7`p5`756hGQZh~ zzukzR5r~<9n0dPqKZ|ICkgHp$WAdhUNx_1{Q{98(WX^a2uM37#La^SXddFZ4Tv~%7`p5`756hGQZh~ zzukzR5r~<9n0dPqKZ|GshmnGjp@M-z&~(Rs7Ln=6{VXypCJN@p+nf7YcJn%$Ih&f8 qxth5eo4J{pIT<+{TbR2#IXhdL8(5f`yIL69DcBHFGQDvwi!1=KE-*U) diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index 46f914c263628f913cdcc070eab3bae72f53b878..b6ad0e541a5c353e8297ce3aca6d8d8ff3e954e7 100644 GIT binary patch delta 148 zcmbO|m1Fi)jtLVuj13G8OiYcc;$olG1pOq~o2OkB*3ogK}LoSY1uTpTT2Tr4czT+PfJ?G$VXDVbi!#Uu*= DxKbug delta 148 zcmbO|m1Fi)jtLVu49yJ;4J-^THqN-g!)<6DVqj=xU}0rwvALReDkHMMWNCgQWPWo5 z|Mm!e#>c;$9ZfAv%v{|J+zc&TES!u?jGau)%}fkUOe{>yUCd04>=bMWDVbi!#Uu*= DrMV@9 diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index 6861c4cf9f65080d33b89458230f94914f63dfa5..0686de6dc5aa22897777d6a5b206aa0433ab65a6 100644 GIT binary patch delta 161 zcmewt{x5t&H@mTcp@E61@y1D4c({xWLJSS8OiZneHy7|uV?^Rl7T`BV;%@fk50Z&D zcXBjzaW*wJaI$bRGIet_aI!QoFmpC>c6D<$G;uMtQ?MbZB$mt0j;pvNv8be?C^e1C Q(7@QhoJ&>J)!&T^0JefD2><{9 delta 161 zcmewt{x5t&H@l&^fuVtg;l@cyaxykEH?%Y{Hg>Xfb~Q9}Gj?;cQ?MbZB$mt0j;pvNv8be?C^e1C Q(7@QhoJ&>J)!&T^0JLQ&{{R30 diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 3181cf2ffe317aa52fec3d827c328dd81acb6166..2cc1f9aadfba9c5b6da0a752ddaa9ffe94359143 100644 GIT binary patch delta 143 zcmZ2-opH%^#tD--j13G8OiYcWd7zj y{^!xoZmtGKmS&b_jz%u7W=1A%W+sM)rbZ^_rq1S;E|%uzb_zCxluYi(mH_}0I3)=H delta 143 zcmZ2-opH%^#tD--49yJ;4J-`JH_pDz!)<6DVqj=xU}0rwzPXWiDkHMMWJP`>Wd7zj x{^!xoP8J5H=8hJ|PL^f{hDOegrp_*oX0B$=mKH!MOEY6T1sg(2CU<1Z001J*C3gS- diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index 4d800a61062b90e66b27a52f6fb98828f4bb98ee..6bfda861702bb68b4341ddfce7b6528b5e291ce9 100644 GIT binary patch delta 143 zcmZ4Sj&a31#t92Kj13G8OiYbUH?Dli!)

    - ZooKeeper 3.4.4 Release Notes + ZooKeeper 3.4.5 Release Notes @@ -42,6 +42,42 @@ These release notes include new developer and user facing incompatibilities, fea Changes +
    +Changes Since 3.4.4 + +Changes Since ZooKeeper 3.4.4 + + + + + Issue + Notes + + + + + + + ZOOKEEPER-1550 + + +ZooKeeperSaslClient does not finish anonymous login on OpenJDK + + + + + + ZOOKEEPER-1376 + + +zkServer.sh does not correctly check for $SERVER_JVMFLAGS + + + + + +
    +
    Changes Since 3.4.3 From 8841d5e343864a8c6cc6b80843bfd1c77ab2c14b Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Sun, 30 Sep 2012 17:52:02 +0000 Subject: [PATCH 121/444] Preparing for 3.4.5 - take 2 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1392089 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index c72ed0a9b82..31cd9d26110 100644 --- a/build.xml +++ b/build.xml @@ -24,7 +24,7 @@ - + From 6682c8c66d9d6e67e0d3e76ac6d6d970c2ce95db Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 31 Oct 2012 18:42:18 +0000 Subject: [PATCH 122/444] ZOOKEEPER-1560 Zookeeper client hangs on creation of large nodes (Skye Wanderman-Milne via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1404289 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/zookeeper/ClientCnxnSocketNIO.java | 104 +++++++++--------- .../org/apache/zookeeper/test/ClientTest.java | 16 +++ 2 files changed, 69 insertions(+), 51 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java index a4b389c8188..89c4b4155cb 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java @@ -25,16 +25,16 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.ClientCnxn.EndOfStreamException; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.ZooDefs.OpCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ClientCnxnSocketNIO extends ClientCnxnSocket { private static final Logger LOG = LoggerFactory @@ -99,83 +99,85 @@ void doIO(List pendingQueue, LinkedList outgoingQueue, ClientCnx } } if (sockKey.isWritable()) { - LinkedList pending = new LinkedList(); - Packet p = null; synchronized(outgoingQueue) { - p = findSendablePacket(outgoingQueue, + Packet p = findSendablePacket(outgoingQueue, cnxn.sendThread.clientTunneledAuthenticationInProgress()); if (p != null) { - outgoingQueue.removeFirstOccurrence(p); updateLastSend(); - if ((p.requestHeader != null) && - (p.requestHeader.getType() != OpCode.ping) && - (p.requestHeader.getType() != OpCode.auth)) { - p.requestHeader.setXid(cnxn.getXid()); + // If we already started writing p, p.bb will already exist + if (p.bb == null) { + if ((p.requestHeader != null) && + (p.requestHeader.getType() != OpCode.ping) && + (p.requestHeader.getType() != OpCode.auth)) { + p.requestHeader.setXid(cnxn.getXid()); + } + p.createBB(); } - p.createBB(); - ByteBuffer pbb = p.bb; - sock.write(pbb); - if (!pbb.hasRemaining()) { + sock.write(p.bb); + if (!p.bb.hasRemaining()) { sentCount++; + outgoingQueue.removeFirstOccurrence(p); if (p.requestHeader != null && p.requestHeader.getType() != OpCode.ping && p.requestHeader.getType() != OpCode.auth) { - pending.add(p); + synchronized (pendingQueue) { + pendingQueue.add(p); + } } } - } else { - // No suitable packet to send: turn off write interest flag. + } + if (outgoingQueue.isEmpty()) { + // No more packets to send: turn off write interest flag. // Will be turned on later by a later call to enableWrite(), // from within ZooKeeperSaslClient (if client is configured // to attempt SASL authentication), or in either doIO() or // in doTransport() if not. disableWrite(); + } else { + // Just in case + enableWrite(); } } - synchronized(pendingQueue) { - pendingQueue.addAll(pending); - } - } } private Packet findSendablePacket(LinkedList outgoingQueue, boolean clientTunneledAuthenticationInProgress) { synchronized (outgoingQueue) { - if (!outgoingQueue.isEmpty()) { - if (clientTunneledAuthenticationInProgress) { - Packet p = null; - // Since client's authentication with server is in progress, - // send only the null-header packet queued by primeConnection(). - // This packet must be sent so that the SASL authentication process - // can proceed, but all other packets should wait until - // SASL authentication completes. - Iterator iter = outgoingQueue.listIterator(); - while(iter.hasNext()) { - p = iter.next(); - if (p.requestHeader == null) { - // We've found the priming-packet. - return p; - } else { - // Non-priming packet: defer it until later, leaving it in the queue - // until authentication completes. - if (LOG.isDebugEnabled()) { - LOG.debug("deferring non-priming packet: " + p + - "until SASL authentication completes."); - } - } - } - // no sendable packet found. - return null; + if (outgoingQueue.isEmpty()) { + return null; + } + if (outgoingQueue.getFirst().bb != null // If we've already starting sending the first packet, we better finish + || !clientTunneledAuthenticationInProgress) { + return outgoingQueue.getFirst(); + } + + // Since client's authentication with server is in progress, + // send only the null-header packet queued by primeConnection(). + // This packet must be sent so that the SASL authentication process + // can proceed, but all other packets should wait until + // SASL authentication completes. + ListIterator iter = outgoingQueue.listIterator(); + while (iter.hasNext()) { + Packet p = iter.next(); + if (p.requestHeader == null) { + // We've found the priming-packet. Move it to the beginning of the queue. + iter.remove(); + outgoingQueue.add(0, p); + return p; } else { - // Tunnelled authentication is not in progress: just - // send the first packet in the queue. - return outgoingQueue.getFirst(); + // Non-priming packet: defer it until later, leaving it in the queue + // until authentication completes. + if (LOG.isDebugEnabled()) { + LOG.debug("deferring non-priming packet: " + p + + "until SASL authentication completes."); + } } } + // no sendable packet found. + return null; } - return null; } @Override diff --git a/src/java/test/org/apache/zookeeper/test/ClientTest.java b/src/java/test/org/apache/zookeeper/test/ClientTest.java index e99d8df54a3..84d4843371b 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientTest.java @@ -523,6 +523,22 @@ public void testSequentialNodeData() throws Exception { } + @Test + public void testLargeNodeData() throws Exception { + ZooKeeper zk= null; + String queue_handle = "/large"; + try { + zk = createClient(); + + zk.create(queue_handle, new byte[500000], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } finally { + if (zk != null) { + zk.close(); + } + } + + } private void verifyCreateFails(String path, ZooKeeper zk) throws Exception { try { From 70ce681e3a5e281b43d1f9e9322debaa51123df4 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 5 Nov 2012 07:34:29 +0000 Subject: [PATCH 123/444] Preparing for release 3.4.5 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1405703 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/documentation/content/xdocs/releasenotes.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml index c9219cdf4c9..08257e3ee77 100644 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ b/src/docs/src/documentation/content/xdocs/releasenotes.xml @@ -73,6 +73,15 @@ ZooKeeperSaslClient does not finish anonymous login on OpenJDK zkServer.sh does not correctly check for $SERVER_JVMFLAGS + + + + ZOOKEEPER-1560 + + +Zookeeper client hangs on creation of large nodes + + From 1bfa4c32be7eeda081024616b53bed5229d65c63 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 28 Nov 2012 07:44:33 +0000 Subject: [PATCH 124/444] ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun MXBean classes (Adalberto Medeiros via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1414567 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 10 + .../zookeeper/server/NIOServerCnxn.java | 14 +- .../zookeeper/server/NettyServerCnxn.java | 17 +- .../zookeeper/server/util/OSMXBean.java | 182 ++++++++++++++++++ .../apache/zookeeper/test/OSMXBeanTest.java | 70 +++++++ 5 files changed, 273 insertions(+), 20 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/server/util/OSMXBean.java create mode 100644 src/java/test/org/apache/zookeeper/test/OSMXBeanTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 3c1e540cf90..8b00c52d4cd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +Release 3.4.6 - TBD + +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun + MXBean classes (Adalberto Medeiros via phunt) + + Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index 0fd56b6dfe7..eade1d61ab5 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -23,8 +23,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -51,7 +49,7 @@ import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; -import com.sun.management.UnixOperatingSystemMXBean; +import org.apache.zookeeper.server.util.OSMXBean; /** * This class handles communication with clients using NIO. There is one per @@ -760,12 +758,10 @@ public void commandRun() { print("ephemerals_count", zkdb.getDataTree().getEphemeralsCount()); print("approximate_data_size", zkdb.getDataTree().approximateDataSize()); - OperatingSystemMXBean osMbean = ManagementFactory.getOperatingSystemMXBean(); - if(osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = (UnixOperatingSystemMXBean)osMbean; - - print("open_file_descriptor_count", unixos.getOpenFileDescriptorCount()); - print("max_file_descriptor_count", unixos.getMaxFileDescriptorCount()); + OSMXBean osMbean = new OSMXBean(); + if (osMbean != null && osMbean.getUnix() == true) { + print("open_file_descriptor_count", osMbean.getOpenFileDescriptorCount()); + print("max_file_descriptor_count", osMbean.getMaxFileDescriptorCount()); } if(stats.getServerState().equals("leader")) { diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index 33b61049708..ec63c0498d7 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -25,8 +25,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; @@ -49,14 +47,13 @@ import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; +import org.apache.zookeeper.server.util.OSMXBean; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.MessageEvent; -import com.sun.management.UnixOperatingSystemMXBean; - public class NettyServerCnxn extends ServerCnxn { Logger LOG = LoggerFactory.getLogger(NettyServerCnxn.class); Channel channel; @@ -571,14 +568,12 @@ public void commandRun() { print("ephemerals_count", zkdb.getDataTree().getEphemeralsCount()); print("approximate_data_size", zkdb.getDataTree().approximateDataSize()); - OperatingSystemMXBean osMbean = ManagementFactory.getOperatingSystemMXBean(); - if(osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = (UnixOperatingSystemMXBean)osMbean; - - print("open_file_descriptor_count", unixos.getOpenFileDescriptorCount()); - print("max_file_descriptor_count", unixos.getMaxFileDescriptorCount()); + OSMXBean osMbean = new OSMXBean(); + if (osMbean != null && osMbean.getUnix() == true) { + print("open_file_descriptor_count", osMbean.getOpenFileDescriptorCount()); + print("max_file_descriptor_count", osMbean.getMaxFileDescriptorCount()); } - + if(stats.getServerState().equals("leader")) { Leader leader = ((LeaderZooKeeperServer)zkServer).getLeader(); diff --git a/src/java/main/org/apache/zookeeper/server/util/OSMXBean.java b/src/java/main/org/apache/zookeeper/server/util/OSMXBean.java new file mode 100644 index 00000000000..b22824d09fb --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/util/OSMXBean.java @@ -0,0 +1,182 @@ +/** + * 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.server.util; + +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.lang.reflect.Method; + +/** + * This class is a wrapper for the implementation of + * com.sun.management.UnixOperatingSystemMXBean + * It will decide to use the sun api or its own implementation + * depending on the runtime (vendor) used. + */ +public class OSMXBean +{ + static final Logger LOG = LoggerFactory.getLogger(OSMXBean.class); + + private OperatingSystemMXBean osMbean; + + private static final boolean ibmvendor = + System.getProperty("java.vendor").contains("IBM"); + private static final boolean windows = + System.getProperty("os.name").startsWith("Windows"); + private static final boolean linux = + System.getProperty("os.name").startsWith("Linux"); + + /** + * Constructor. Get the running Operating System instance + */ + public OSMXBean () { + this.osMbean = ManagementFactory.getOperatingSystemMXBean(); + } + + /** + * Check if the OS is unix. If using the IBM java runtime, this + * will only work for linux. + * + * @return whether this is unix or not. + */ + public boolean getUnix() { + if (windows) { + return false; + } + return (ibmvendor ? linux : true); + } + + /** + * Load the implementation of UnixOperatingSystemMXBean for sun jvm + * and runs the desired method. + * @param mBeanMethodName : method to run from the interface UnixOperatingSystemMXBean + * @return the method result + */ + private Long getOSUnixMXBeanMethod (String mBeanMethodName) + { + Object unixos; + Class classRef; + Method mBeanMethod; + + try { + classRef = Class.forName("com.sun.management.UnixOperatingSystemMXBean"); + if (classRef.isInstance(osMbean)) { + mBeanMethod = classRef.getDeclaredMethod(mBeanMethodName, + new Class[0]); + unixos = classRef.cast(osMbean); + return (Long)mBeanMethod.invoke(unixos); + } + } catch(Exception e) { + LOG.warn("Not able to load class or method for com.sun.managment.UnixOperatingSystemMXBean.", e); + } + return null; + } + + /** + * Get the number of opened filed descriptor for the runtime jvm. + * If sun java, it will use the com.sun.management interfaces. + * Otherwise, this methods implements it (linux only). + * @return number of open file descriptors for the jvm + */ + public long getOpenFileDescriptorCount() + { + Long ofdc; + + if (!ibmvendor) { + ofdc = getOSUnixMXBeanMethod("getOpenFileDescriptorCount"); + return (ofdc != null ? ofdc.longValue () : -1); + } + + try { + //need to get the PID number of the process first + RuntimeMXBean rtmbean = ManagementFactory.getRuntimeMXBean(); + String rtname = rtmbean.getName(); + String[] pidhost = rtname.split("@"); + + //using linux bash commands to retrieve info + Process p = Runtime.getRuntime().exec( + new String[] { "bash", "-c", + "ls /proc/" + pidhost[0] + "/fdinfo | wc -l" }); + InputStream in = p.getInputStream(); + BufferedReader output = new BufferedReader( + new InputStreamReader(in)); + + try { + String openFileDesCount; + if ((openFileDesCount = output.readLine()) != null) { + return Long.parseLong(openFileDesCount); + } + } finally { + if (output != null) { + output.close(); + } + } + } catch (IOException ie) { + LOG.warn("Not able to get the number of open file descriptors", ie); + } + return -1; + } + + /** + * Get the number of the maximum file descriptors the system can use. + * If sun java, it will use the com.sun.management interfaces. + * Otherwise, this methods implements it (linux only). + * @return max number of file descriptors the operating system can use. + */ + public long getMaxFileDescriptorCount() + { + Long mfdc; + + if (!ibmvendor) { + mfdc = getOSUnixMXBeanMethod("getMaxFileDescriptorCount"); + return (mfdc != null ? mfdc.longValue () : -1); + } + + try { + //using linux bash commands to retrieve info + Process p = Runtime.getRuntime().exec( + new String[] { "bash", "-c", "ulimit -n" }); + InputStream in = p.getInputStream(); + BufferedReader output = new BufferedReader( + new InputStreamReader(in)); + + try { + String maxFileDesCount; + if ((maxFileDesCount = output.readLine()) != null) { + return Long.parseLong(maxFileDesCount); + } + } finally { + if (output != null) { + output.close(); + } + } + } catch (IOException ie) { + LOG.warn("Not able to get the max number of file descriptors", ie); + } + return -1; + } +} diff --git a/src/java/test/org/apache/zookeeper/test/OSMXBeanTest.java b/src/java/test/org/apache/zookeeper/test/OSMXBeanTest.java new file mode 100644 index 00000000000..ce21ab82539 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/OSMXBeanTest.java @@ -0,0 +1,70 @@ +/** + * 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.test; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.zookeeper.server.util.OSMXBean; + +public class OSMXBeanTest { + + private OSMXBean osMbean; + private Long ofdc = 0L; + private Long mfdc = 0L; + protected static final Logger LOG = LoggerFactory.getLogger(OSMXBeanTest.class); + + @Before + public void initialize() { + this.osMbean = new OSMXBean(); + Assert.assertNotNull("Could not initialize OSMXBean object!", osMbean); + } + + @Test + public final void testGetUnix() { + boolean isUnix = osMbean.getUnix(); + if (!isUnix) { + LOG.info("Running in a Windows system! Output won't be printed!"); + } else { + LOG.info("Running in a Unix or Linux system!"); + } + } + + @Test + public final void testGetOpenFileDescriptorCount() { + if (osMbean != null && osMbean.getUnix() == true) { + ofdc = osMbean.getOpenFileDescriptorCount(); + LOG.info("open fdcount is: " + ofdc); + } + Assert.assertFalse("The number of open file descriptor is negative",(ofdc < 0)); + } + + @Test + public final void testGetMaxFileDescriptorCount() { + if (osMbean != null && osMbean.getUnix() == true) { + mfdc = osMbean.getMaxFileDescriptorCount(); + LOG.info("max fdcount is: " + mfdc); + } + Assert.assertFalse("The max file descriptor number is negative",(mfdc < 0)); + } + +} From b87749ab29e0d37d97f7c7dd8e916a94cb2e845a Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 11 Dec 2012 07:45:09 +0000 Subject: [PATCH 125/444] ZOOKEEPER-1564. Allow JUnit test build with IBM Java (Paulo Ricardo Paz Vital via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420022 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 5 ++++ .../org/apache/zookeeper/test/ClientBase.java | 23 ++++++------------- .../org/apache/zookeeper/test/ClientTest.java | 18 ++++----------- .../org/apache/zookeeper/test/QuorumBase.java | 22 ++++++------------ .../org/apache/zookeeper/test/QuorumUtil.java | 12 ++++------ 5 files changed, 28 insertions(+), 52 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8b00c52d4cd..5dad9391e16 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,11 @@ BUGFIXES: ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun MXBean classes (Adalberto Medeiros via phunt) +IMPROVEMENTS: + + ZOOKEEPER-1564. Allow JUnit test build with IBM Java + (Paulo Ricardo Paz Vital via phunt) + Release 3.4.5 - 2012-09-30 diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 4c667f889a1..2948239215a 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -25,8 +25,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; @@ -54,14 +52,13 @@ import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.util.OSMXBean; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.management.UnixOperatingSystemMXBean; - public abstract class ClientBase extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ClientBase.class); @@ -393,12 +390,9 @@ public void setUp() throws Exception { * correctly. Unfortunately this only works on unix systems (the * only place sun has implemented as part of the mgmt bean api. */ - OperatingSystemMXBean osMbean = - ManagementFactory.getOperatingSystemMXBean(); - if (osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = - (UnixOperatingSystemMXBean)osMbean; - initialFdCount = unixos.getOpenFileDescriptorCount(); + OSMXBean osMbean = new OSMXBean(); + if (osMbean.getUnix() == true) { + initialFdCount = osMbean.getOpenFileDescriptorCount(); LOG.info("Initial fdcount is: " + initialFdCount); } @@ -474,12 +468,9 @@ public void tearDown() throws Exception { * correctly. Unfortunately this only works on unix systems (the * only place sun has implemented as part of the mgmt bean api. */ - OperatingSystemMXBean osMbean = - ManagementFactory.getOperatingSystemMXBean(); - if (osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = - (UnixOperatingSystemMXBean)osMbean; - long fdCount = unixos.getOpenFileDescriptorCount(); + OSMXBean osMbean = new OSMXBean(); + if (osMbean.getUnix() == true) { + long fdCount = osMbean.getOpenFileDescriptorCount(); String message = "fdcount after test is: " + fdCount + " at start it was " + initialFdCount; LOG.info(message); diff --git a/src/java/test/org/apache/zookeeper/test/ClientTest.java b/src/java/test/org/apache/zookeeper/test/ClientTest.java index 84d4843371b..9dc544cb4de 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientTest.java @@ -19,8 +19,6 @@ package org.apache.zookeeper.test; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -45,11 +43,10 @@ import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.PrepRequestProcessor; +import org.apache.zookeeper.server.util.OSMXBean; import org.junit.Assert; import org.junit.Test; -import com.sun.management.UnixOperatingSystemMXBean; - public class ClientTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(ClientTest.class); @@ -709,11 +706,8 @@ public void run() { */ @Test public void testClientCleanup() throws Throwable { - OperatingSystemMXBean osMbean = - ManagementFactory.getOperatingSystemMXBean(); - if (osMbean == null - || !(osMbean instanceof UnixOperatingSystemMXBean)) - { + OSMXBean osMbean = new OSMXBean(); + if (osMbean.getUnix() == false) { LOG.warn("skipping testClientCleanup, only available on Unix"); return; } @@ -726,9 +720,7 @@ public void testClientCleanup() throws Throwable { * on unix systems (the only place sun has implemented as part of the * mgmt bean api). */ - UnixOperatingSystemMXBean unixos = - (UnixOperatingSystemMXBean) osMbean; - long initialFdCount = unixos.getOpenFileDescriptorCount(); + long initialFdCount = osMbean.getOpenFileDescriptorCount(); VerifyClientCleanup threads[] = new VerifyClientCleanup[threadCount]; @@ -744,7 +736,7 @@ public void testClientCleanup() throws Throwable { // if this Assert.fails it means we are not cleaning up after the closed // sessions. - long currentCount = unixos.getOpenFileDescriptorCount(); + long currentCount = osMbean.getOpenFileDescriptorCount(); final String logmsg = "open fds after test ({}) are not significantly higher than before ({})"; if (currentCount > initialFdCount + 10) { diff --git a/src/java/test/org/apache/zookeeper/test/QuorumBase.java b/src/java/test/org/apache/zookeeper/test/QuorumBase.java index ac757ecd00d..f1c8821ce56 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumBase.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumBase.java @@ -20,8 +20,6 @@ import java.io.File; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.LinkedHashSet; @@ -35,10 +33,10 @@ import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.util.OSMXBean; import org.junit.Assert; import org.junit.Test; -import com.sun.management.UnixOperatingSystemMXBean; public class QuorumBase extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(QuorumBase.class); @@ -102,13 +100,10 @@ protected void setUp(boolean withObservers) throws Exception { startServers(withObservers); - OperatingSystemMXBean osMbean = - ManagementFactory.getOperatingSystemMXBean(); - if (osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = - (UnixOperatingSystemMXBean)osMbean; + OSMXBean osMbean = new OSMXBean(); + if (osMbean.getUnix() == true) { LOG.info("Initial fdcount is: " - + unixos.getOpenFileDescriptorCount()); + + osMbean.getOpenFileDescriptorCount()); } LOG.info("Setup finished"); @@ -291,13 +286,10 @@ public void setupServer(int i) throws IOException { public void tearDown() throws Exception { LOG.info("TearDown started"); - OperatingSystemMXBean osMbean = - ManagementFactory.getOperatingSystemMXBean(); - if (osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = - (UnixOperatingSystemMXBean)osMbean; + OSMXBean osMbean = new OSMXBean(); + if (osMbean.getUnix() == true) { LOG.info("fdcount after test is: " - + unixos.getOpenFileDescriptorCount()); + + osMbean.getOpenFileDescriptorCount()); } shutdownServers(); diff --git a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java index a68ebec27f2..c39cb13361f 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java @@ -20,8 +20,6 @@ import java.io.File; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.LinkedHashSet; @@ -35,10 +33,9 @@ import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.util.OSMXBean; import org.junit.Assert; -import com.sun.management.UnixOperatingSystemMXBean; - /** * Utility for quorum testing. Setups 2n+1 peers and allows to start/stop all * peers, particular peer, n peers etc. @@ -246,10 +243,9 @@ public String getConnString() { public void tearDown() throws Exception { LOG.info("TearDown started"); - OperatingSystemMXBean osMbean = ManagementFactory.getOperatingSystemMXBean(); - if (osMbean != null && osMbean instanceof UnixOperatingSystemMXBean) { - UnixOperatingSystemMXBean unixos = (UnixOperatingSystemMXBean) osMbean; - LOG.info("fdcount after test is: " + unixos.getOpenFileDescriptorCount()); + OSMXBean osMbean = new OSMXBean(); + if (osMbean.getUnix() == true) { + LOG.info("fdcount after test is: " + osMbean.getOpenFileDescriptorCount()); } shutdownAll(); From b3892c673120e7fd8859f6d2c5f4d67d581ef658 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 11 Dec 2012 08:19:42 +0000 Subject: [PATCH 126/444] ZOOKEEPER-1596. Zab1_0Test should ensure that the file is closed (Enis Soztutar via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420030 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/server/quorum/Zab1_0Test.java | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5dad9391e16..943ca99dd35 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,9 @@ BUGFIXES: ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun MXBean classes (Adalberto Medeiros via phunt) + ZOOKEEPER-1596. Zab1_0Test should ensure that the file is closed + (Enis Soztutar via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 60b3484bcb9..fe747a5d2a1 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -997,8 +997,13 @@ private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, peer.setCnxnFactory(new NullServerCnxnFactory()); File version2 = new File(tmpDir, "version-2"); version2.mkdir(); - new FileOutputStream(new File(version2, "currentEpoch")).write("0\n".getBytes()); - new FileOutputStream(new File(version2, "acceptedEpoch")).write("0\n".getBytes()); + FileOutputStream fos; + fos = new FileOutputStream(new File(version2, "currentEpoch")); + fos.write("0\n".getBytes()); + fos.close(); + fos = new FileOutputStream(new File(version2, "acceptedEpoch")); + fos.write("0\n".getBytes()); + fos.close(); return peer; } } From c50168a1ccd4396db05b781c36ee3a497a6ca370 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 12 Dec 2012 06:51:38 +0000 Subject: [PATCH 127/444] ZOOKEEPER-1513. "Unreasonable length" exception while starting a server (Skye W-M via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420540 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + build.xml | 6 + .../org/apache/jute/BinaryInputArchive.java | 17 +-- .../data/buffersize/create/version-2/log.1 | Bin 0 -> 51216 bytes .../buffersize/create/version-2/snapshot.0 | Bin 0 -> 296 bytes .../test/data/buffersize/set/version-2/log.1 | Bin 0 -> 51216 bytes .../data/buffersize/set/version-2/snapshot.0 | Bin 0 -> 296 bytes .../data/buffersize/snapshot/version-2/log.1 | Bin 0 -> 51216 bytes .../buffersize/snapshot/version-2/snapshot.0 | Bin 0 -> 296 bytes .../buffersize/snapshot/version-2/snapshot.2 | Bin 0 -> 5429 bytes .../apache/zookeeper/test/BufferSizeTest.java | 132 ++++++++++++++++++ 11 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 src/java/test/data/buffersize/create/version-2/log.1 create mode 100644 src/java/test/data/buffersize/create/version-2/snapshot.0 create mode 100644 src/java/test/data/buffersize/set/version-2/log.1 create mode 100644 src/java/test/data/buffersize/set/version-2/snapshot.0 create mode 100644 src/java/test/data/buffersize/snapshot/version-2/log.1 create mode 100644 src/java/test/data/buffersize/snapshot/version-2/snapshot.0 create mode 100644 src/java/test/data/buffersize/snapshot/version-2/snapshot.2 create mode 100644 src/java/test/org/apache/zookeeper/test/BufferSizeTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 943ca99dd35..bb28e1a32a9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,9 @@ BUGFIXES: ZOOKEEPER-1596. Zab1_0Test should ensure that the file is closed (Enis Soztutar via phunt) + ZOOKEEPER-1513. "Unreasonable length" exception while starting a + server (Skye W-M via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/build.xml b/build.xml index 31cd9d26110..081d650061e 100644 --- a/build.xml +++ b/build.xml @@ -68,6 +68,7 @@ + @@ -1126,6 +1127,7 @@ + @@ -1138,6 +1140,10 @@ + + + + diff --git a/src/java/main/org/apache/jute/BinaryInputArchive.java b/src/java/main/org/apache/jute/BinaryInputArchive.java index 8f329ebdef9..6b2cb46a854 100644 --- a/src/java/main/org/apache/jute/BinaryInputArchive.java +++ b/src/java/main/org/apache/jute/BinaryInputArchive.java @@ -83,20 +83,15 @@ public String readString(String tag) throws IOException { return new String(b, "UTF8"); } - static public final int maxBuffer = determineMaxBuffer(); - private static int determineMaxBuffer() { - String maxBufferString = System.getProperty("jute.maxbuffer"); - try { - return Integer.parseInt(maxBufferString); - } catch(Exception e) { - return 0xfffff; - } - - } + static public final int maxBuffer = Integer.getInteger("jute.maxbuffer", 0xfffff); + public byte[] readBuffer(String tag) throws IOException { int len = readInt(tag); if (len == -1) return null; - if (len < 0 || len > maxBuffer) { + // Since this is a rough sanity check, add some padding to maxBuffer to + // make up for extra fields, etc. (otherwise e.g. clients may be able to + // write buffers larger than we can read from disk!) + if (len < 0 || len > maxBuffer + 1024) { throw new IOException("Unreasonable length = " + len); } byte[] arr = new byte[len]; diff --git a/src/java/test/data/buffersize/create/version-2/log.1 b/src/java/test/data/buffersize/create/version-2/log.1 new file mode 100644 index 0000000000000000000000000000000000000000..4f05bc1cb458cee8d7b23e1e4a9118125e6d01f5 GIT binary patch literal 51216 zcmeI$F-ikb5C-5ELxflfl3EKvD+3k*Ez?-&32a133PE;*20{)XSXg)w%N)Q`dmHbe zovicL7;*s9eZwxi8Q#3&EC2J;vy+t4TKtQ;e{J`o*=-z;u8Z%*I3F7E#q{~Ds;aM) z_BQ)@dbt1en9|nA(n4IUD<00%m+k2CY@<6K-i~w|4dPbu!Xp(cXZ{fbvYS5PwumPIEsz;3q^nc0RjXF5FkK+009C7 z2oNAZfWY1bR_WYnd=Fpp@#8QKjfdBB%4gSr5S4`KlUhz4PKAZ9JkFUm;)GT0LHD)aMF zfh;gUG6p7ukA@kItcD55;nFWjEiQ@A%TEECEZm_t3PwX the data size due to extra fields + clientOp.execute(new byte[TEST_MAXBUFFER]); + fail("Request exceeding jute.maxbuffer succeeded!"); + } catch (KeeperException.ConnectionLossException e) {} + try { + clientOp.execute(new byte[TEST_MAXBUFFER + 10]); + fail("Request exceeding jute.maxbuffer succeeded!"); + } catch (KeeperException.ConnectionLossException e) {} + } + + private interface ClientOp { + void execute(byte[] data) throws Exception; + } + + @Test + public void testStartup() throws Exception { + final String path = "/test_node"; + zk.create(path, new byte[TEST_MAXBUFFER - 60], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk.setData(path, new byte[TEST_MAXBUFFER - 50], -1); + + stopServer(); + startServer(); + } + + @Test + public void testStartupFailureCreate() throws Exception { + // Empty snapshot and logfile containing a 5000-byte create + testStartupFailure(new File(TEST_DATA, "create"), + "Server started despite create exceeding jute.maxbuffer!"); + } + + @Test + public void testStartupFailureSet() throws Exception { + // Empty snapshot and logfile containing a 1-byte create and 5000-byte set + testStartupFailure(new File(TEST_DATA, "set"), + "Server started despite set exceeding jute.maxbuffer!"); + } + + @Test + public void testStartupFailureSnapshot() throws Exception { + // Snapshot containing 5000-byte znode and logfile containing create txn + testStartupFailure(new File(TEST_DATA, "snapshot"), + "Server started despite znode exceeding jute.maxbuffer!"); + } + + private void testStartupFailure(File testDir, String failureMsg) throws Exception { + stopServer(); + // Point server at testDir + tmpDir = testDir; + try { + startServer(); + fail(failureMsg); + } catch (IOException e) { + LOG.info("Successfully caught IOException: " + e); + } + } +} From 6d7f3e08238e47fe2b66764ccee9c74deec99da9 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 12 Dec 2012 06:58:55 +0000 Subject: [PATCH 128/444] ZOOKEEPER-1581. change copyright in notice to 2012 (breed via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420544 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ NOTICE.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index bb28e1a32a9..c54ac4135fb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -13,6 +13,8 @@ BUGFIXES: ZOOKEEPER-1513. "Unreasonable length" exception while starting a server (Skye W-M via phunt) + ZOOKEEPER-1581. change copyright in notice to 2012 (breed via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/NOTICE.txt b/NOTICE.txt index 73580508223..35add968965 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2011 The Apache Software Foundation +Copyright 2009-2012 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). From 8f2bc0523ce0638de19cbc3cb6ba1d2ae7b46cf4 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 12 Dec 2012 07:21:00 +0000 Subject: [PATCH 129/444] ZOOKEEPER-1598. Ability to support more digits in the version string (Raja Aluri via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420548 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/version/util/VerGen.java | 8 ++++---- src/java/test/org/apache/zookeeper/VerGenTest.java | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c54ac4135fb..29ffdb97ce5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -20,6 +20,9 @@ IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java (Paulo Ricardo Paz Vital via phunt) + ZOOKEEPER-1598. Ability to support more digits in the version string + (Raja Aluri via phunt) + Release 3.4.5 - 2012-09-30 diff --git a/src/java/main/org/apache/zookeeper/version/util/VerGen.java b/src/java/main/org/apache/zookeeper/version/util/VerGen.java index be494cd8e00..d4cbeb64eaa 100644 --- a/src/java/main/org/apache/zookeeper/version/util/VerGen.java +++ b/src/java/main/org/apache/zookeeper/version/util/VerGen.java @@ -112,11 +112,11 @@ public static class Version { public int micro; public String qualifier; } - + public static Version parseVersionString(String input) { Version result = new Version(); - Pattern p = Pattern.compile("^(\\d+).(\\d+).(\\d+)(-(.+))?$"); + Pattern p = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)((\\.\\d+)*)(-(.+))?$"); Matcher m = p.matcher(input); if (!m.matches()) { @@ -125,8 +125,8 @@ public static Version parseVersionString(String input) { result.maj = Integer.parseInt(m.group(1)); result.min = Integer.parseInt(m.group(2)); result.micro = Integer.parseInt(m.group(3)); - if (m.groupCount() == 5) { - result.qualifier = m.group(5); + if (m.groupCount() == 7) { + result.qualifier = m.group(7); } else { result.qualifier = null; } diff --git a/src/java/test/org/apache/zookeeper/VerGenTest.java b/src/java/test/org/apache/zookeeper/VerGenTest.java index f5757136330..13d3abdd806 100644 --- a/src/java/test/org/apache/zookeeper/VerGenTest.java +++ b/src/java/test/org/apache/zookeeper/VerGenTest.java @@ -43,7 +43,10 @@ public static Collection data() { {"1.2.3", new Object[] {1, 2, 3, null}}, {"1.2.3-dev", new Object[] {1, 2, 3, "dev"}}, {"1.2.3-SNAPSHOT", new Object[] {1, 2, 3, "SNAPSHOT"}}, - {"1.2.3-foo-bar+123", new Object[] {1, 2, 3, "foo-bar+123"}} + {"1.2.3-SNAPSHOT", new Object[] {1, 2, 3, "SNAPSHOT"}}, + {"1.2.3-foo-bar+123", new Object[] {1, 2, 3, "foo-bar+123"}}, + {"1.2.3.4.5-SNAPSHOT", new Object[] {1, 2, 3, "SNAPSHOT"}}, + {"1.2.3.4.5-foo-bar+123", new Object[] {1, 2, 3, "foo-bar+123"}} }); } From ba0a4eee5c5c7f9e54d4c2d15d918f15d2ad24cd Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 12 Dec 2012 07:39:44 +0000 Subject: [PATCH 130/444] ZOOKEEPER-753. log4j dependency in the pom needs to have exclusion lists (Sean Busbey via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420553 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 10 +++++----- ivy.xml | 2 +- ...g4j-1.2.15.LICENSE.txt => log4j-1.2.16.LICENSE.txt} | 0 4 files changed, 9 insertions(+), 6 deletions(-) rename src/java/lib/{log4j-1.2.15.LICENSE.txt => log4j-1.2.16.LICENSE.txt} (100%) diff --git a/CHANGES.txt b/CHANGES.txt index 29ffdb97ce5..b506d35e659 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,9 @@ BUGFIXES: ZOOKEEPER-1581. change copyright in notice to 2012 (breed via phunt) + ZOOKEEPER-753. log4j dependency in the pom needs to have exclusion + lists (Sean Busbey via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/build.xml b/build.xml index 081d650061e..3cbba63ad5b 100644 --- a/build.xml +++ b/build.xml @@ -329,12 +329,12 @@ - - @@ -345,12 +345,12 @@ - - @@ -1366,7 +1366,7 @@ - + diff --git a/ivy.xml b/ivy.xml index b3e8cb763fc..c78124f32c2 100644 --- a/ivy.xml +++ b/ivy.xml @@ -43,7 +43,7 @@ - + diff --git a/src/java/lib/log4j-1.2.15.LICENSE.txt b/src/java/lib/log4j-1.2.16.LICENSE.txt similarity index 100% rename from src/java/lib/log4j-1.2.15.LICENSE.txt rename to src/java/lib/log4j-1.2.16.LICENSE.txt From e11c460e05ff9051e06d4c6a78d7ebf802fa9bb8 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 12 Dec 2012 08:02:34 +0000 Subject: [PATCH 131/444] ZOOKEEPER-1553. Findbugs configuration is missing some dependencies (Sean Busbey via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1420556 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index b506d35e659..af50aabf050 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,9 @@ BUGFIXES: ZOOKEEPER-753. log4j dependency in the pom needs to have exclusion lists (Sean Busbey via phunt) + ZOOKEEPER-1553. Findbugs configuration is missing some dependencies + (Sean Busbey via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/build.xml b/build.xml index 3cbba63ad5b..4f58a07e2db 100644 --- a/build.xml +++ b/build.xml @@ -1366,7 +1366,11 @@ - + + + + + From 3e4534d77dd0734c6d2dce0ba7f41f699d8ce180 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 13 Dec 2012 06:06:31 +0000 Subject: [PATCH 132/444] ZOOKEEPER-1583. Document maxClientCnxns in conf/zoo_sample.cfg (Christopher Tubbs via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1421081 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ conf/zoo_sample.cfg | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index af50aabf050..edff1607e94 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -29,6 +29,9 @@ IMPROVEMENTS: ZOOKEEPER-1598. Ability to support more digits in the version string (Raja Aluri via phunt) + ZOOKEEPER-1583. Document maxClientCnxns in conf/zoo_sample.cfg + (Christopher Tubbs via phunt) + Release 3.4.5 - 2012-09-30 diff --git a/conf/zoo_sample.cfg b/conf/zoo_sample.cfg index aafb3247239..a5a2c0bbe3e 100644 --- a/conf/zoo_sample.cfg +++ b/conf/zoo_sample.cfg @@ -12,6 +12,9 @@ syncLimit=5 dataDir=/tmp/zookeeper # the port at which the clients will connect clientPort=2181 +# the maximum number of client connections. +# increase this if you need to handle more clients +#maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. From 57ee42b35cc7ada5ed87483e1bed1d80919100d9 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 13 Dec 2012 07:18:51 +0000 Subject: [PATCH 133/444] ZOOKEEPER-1478. Small bug in QuorumTest.testFollowersStartAfterLeader( ) (Alexander Shraer via fpj, breed, phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1421095 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/test/QuorumTest.java | 20 +++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index edff1607e94..706ca0ad886 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -21,6 +21,9 @@ BUGFIXES: ZOOKEEPER-1553. Findbugs configuration is missing some dependencies (Sean Busbey via phunt) + ZOOKEEPER-1478. Small bug in QuorumTest.testFollowersStartAfterLeader( ) + (Alexander Shraer via fpj, breed, phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/QuorumTest.java b/src/java/test/org/apache/zookeeper/test/QuorumTest.java index 3ffe82c7b97..85f1759c9c0 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; @@ -313,22 +314,15 @@ public void testFollowersStartAfterLeader() throws Exception { // break the quorum qu.shutdown(index); + // Wait until we disconnect to proceed + watcher.waitForDisconnected(CONNECTION_TIMEOUT); + // try to reestablish the quorum qu.start(index); - Assert.assertTrue("quorum reestablishment failed", - QuorumBase.waitForServerUp( - "127.0.0.1:" + qu.getPeer(2).clientPort, - CONNECTION_TIMEOUT)); - for (int i = 0; i < 30; i++) { - try { - zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - break; - } catch(KeeperException.ConnectionLossException e) { - Thread.sleep(1000); - } - // test fails if we still can't connect to the quorum after 30 seconds. + try{ + watcher.waitForConnected(30000); + } catch(TimeoutException e) { Assert.fail("client could not connect to reestablished quorum: giving up after 30+ seconds."); } From 7db3ef7fab350d5a5c6c51088ad76af0089ce51e Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 13 Dec 2012 07:59:44 +0000 Subject: [PATCH 134/444] ZOOKEEPER-1387. Wrong epoch file created (Benjamin Busjaeger via breed, phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1421109 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../zookeeper/server/quorum/QuorumPeer.java | 2 +- .../zookeeper/server/quorum/Zab1_0Test.java | 47 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 706ca0ad886..8da19a43f77 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -24,6 +24,9 @@ BUGFIXES: ZOOKEEPER-1478. Small bug in QuorumTest.testFollowersStartAfterLeader( ) (Alexander Shraer via fpj, breed, phunt) + ZOOKEEPER-1387. Wrong epoch file created + (Benjamin Busjaeger via breed, phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM 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 72d09d41c59..3eee23dca0b 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -444,7 +444,7 @@ private void loadDataBase() { LOG.info(ACCEPTED_EPOCH_FILENAME + " not found! Creating with a reasonable default of {}. This should only happen when you are upgrading your installation", acceptedEpoch); - writeLongToFile(CURRENT_EPOCH_FILENAME, acceptedEpoch); + writeLongToFile(ACCEPTED_EPOCH_FILENAME, acceptedEpoch); } if (acceptedEpoch < currentEpoch) { throw new IOException("The current epoch, " + ZxidUtils.zxidToString(currentEpoch) + " is less than the accepted epoch, " + ZxidUtils.zxidToString(acceptedEpoch)); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index fe747a5d2a1..bbb600bc8c1 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -20,10 +20,12 @@ import static org.junit.Assert.assertEquals; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Field; import java.net.InetSocketAddress; @@ -43,16 +45,19 @@ import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.ByteBufferOutputStream; +import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.CreateTxn; +import org.apache.zookeeper.txn.ErrorTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.Assert; @@ -1006,4 +1011,46 @@ private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, fos.close(); return peer; } + + private String readContentsOfFile(File f) throws IOException { + return new BufferedReader(new FileReader(f)).readLine(); + } + + @Test + public void testInitialAcceptedCurrent() throws Exception { + File tmpDir = File.createTempFile("test", ".dir"); + tmpDir.delete(); + tmpDir.mkdir(); + try { + FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); + File version2 = new File(tmpDir, "version-2"); + version2.mkdir(); + long zxid = ZxidUtils.makeZxid(3, 3); + + TxnHeader hdr = new TxnHeader(1, 1, zxid, 1, ZooDefs.OpCode.error); + ErrorTxn txn = new ErrorTxn(1); + byte[] buf = Util.marshallTxnEntry(hdr, txn); + Request req = new Request(null, 1, 1, ZooDefs.OpCode.error, + ByteBuffer.wrap(buf), null); + req.hdr = hdr; + req.txn = txn; + logFactory.append(req); + logFactory.commit(); + ZKDatabase zkDb = new ZKDatabase(logFactory); + QuorumPeer peer = new QuorumPeer(); + peer.setZKDatabase(zkDb); + peer.setTxnFactory(logFactory); + peer.getLastLoggedZxid(); + Assert.assertEquals(3, peer.getAcceptedEpoch()); + Assert.assertEquals(3, peer.getCurrentEpoch()); + Assert.assertEquals(3, Integer + .parseInt(readContentsOfFile(new File(version2, + QuorumPeer.CURRENT_EPOCH_FILENAME)))); + Assert.assertEquals(3, Integer + .parseInt(readContentsOfFile(new File(version2, + QuorumPeer.ACCEPTED_EPOCH_FILENAME)))); + } finally { + recursiveDelete(tmpDir); + } + } } From c800b4c9cb411ca5fa0e6a24e02a9776da937ee2 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Sat, 15 Dec 2012 00:43:28 +0000 Subject: [PATCH 135/444] ZOOKEEPER-1584. Adding mvn-install target for deploying the zookeeper artifacts to .m2 repository (Ashish Singh via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1422169 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8da19a43f77..e26ac1c7dfe 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -38,6 +38,9 @@ IMPROVEMENTS: ZOOKEEPER-1583. Document maxClientCnxns in conf/zoo_sample.cfg (Christopher Tubbs via phunt) + ZOOKEEPER-1584. Adding mvn-install target for deploying the + zookeeper artifacts to .m2 repository (Ashish Singh via phunt) + Release 3.4.5 - 2012-09-30 diff --git a/build.xml b/build.xml index 4f58a07e2db..6e264955f2e 100644 --- a/build.xml +++ b/build.xml @@ -17,7 +17,10 @@ limitations under the License. --> - + @@ -108,7 +111,14 @@ - + + + + + + + @@ -1085,6 +1095,41 @@ + + + + + + + + + + + + + + + + + + + + + + + The version is ${zookeeper-pom.version} + + + + + + + + + + From e179d718709afc61e4acfa0247679cd8e34684d0 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Mon, 17 Dec 2012 07:12:12 +0000 Subject: [PATCH 136/444] ZOOKEEPER-1578. org.apache.zookeeper.server.quorum.Zab1_0Test failed due to hard code with 33556 port. (Li Ping via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1422771 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../test/org/apache/zookeeper/server/quorum/Zab1_0Test.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index e26ac1c7dfe..4c9d951c27f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,6 +27,9 @@ BUGFIXES: ZOOKEEPER-1387. Wrong epoch file created (Benjamin Busjaeger via breed, phunt) + ZOOKEEPER-1578. org.apache.zookeeper.server.quorum.Zab1_0Test failed due to + hard code with 33556 port. (Li Ping via mahadev) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index bbb600bc8c1..d59ceba8f89 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -38,6 +38,7 @@ import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; +import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; @@ -957,7 +958,7 @@ private LeaderZooKeeperServer prepareLeader(File tmpDir, QuorumPeer peer) peer.setTxnFactory(logFactory); Field addrField = peer.getClass().getDeclaredField("myQuorumAddr"); addrField.setAccessible(true); - addrField.set(peer, new InetSocketAddress(33556)); + addrField.set(peer, new InetSocketAddress(PortAssignment.unique())); ZKDatabase zkDb = new ZKDatabase(logFactory); LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer, new ZooKeeperServer.BasicDataTreeBuilder(), zkDb); return zk; From 70a01502f040ce102dfeb82eaff0505c376d95ff Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 19 Dec 2012 08:04:33 +0000 Subject: [PATCH 137/444] ZOOKEEPER-1334. Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF is flawed (Claus Ibsen via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1423780 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4c9d951c27f..58341633754 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,6 +30,9 @@ BUGFIXES: ZOOKEEPER-1578. org.apache.zookeeper.server.quorum.Zab1_0Test failed due to hard code with 33556 port. (Li Ping via mahadev) + ZOOKEEPER-1334. Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF + is flawed (Claus Ibsen via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/build.xml b/build.xml index 6e264955f2e..391caa0d6e5 100644 --- a/build.xml +++ b/build.xml @@ -537,8 +537,8 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - - + + From af4d8207f372eb3b6e7a929017c4cdd035fae11c Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Mon, 31 Dec 2012 01:44:18 +0000 Subject: [PATCH 138/444] ZOOKEEPER-1535. ZK Shell/Cli re-executes last command on exit (Edward Ribeiro via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1427035 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/ZooKeeperMain.java | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 58341633754..13bcd3a494a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -32,6 +32,9 @@ BUGFIXES: ZOOKEEPER-1334. Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF is flawed (Claus Ibsen via phunt) + + ZOOKEEPER-1535. ZK Shell/Cli re-executes last command on exit + (Edward Ribeiro via camille) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index 9170a81512b..3a96b82b2d6 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -350,11 +350,6 @@ void run() throws KeeperException, IOException, InterruptedException { } } } - - boolean watch = processCmd(cl); - if (!watch) { - System.exit(0); - } } public void executeLine(String line) From 055e8e71767e287c4073efdbdfef944a7225ec5d Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 18 Jan 2013 14:28:48 +0000 Subject: [PATCH 139/444] ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the Leader to the Follower. (thawan, fpj via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1435159 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../zookeeper/server/quorum/Leader.java | 165 +++++++++++++----- .../server/quorum/LearnerHandler.java | 2 +- .../zookeeper/server/quorum/Zab1_0Test.java | 16 -- 4 files changed, 122 insertions(+), 63 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 13bcd3a494a..9957f17e424 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -50,6 +50,8 @@ IMPROVEMENTS: ZOOKEEPER-1584. Adding mvn-install target for deploying the zookeeper artifacts to .m2 repository (Ashish Singh via phunt) + ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the Leader to the Follower. (thawan, fpj via fpj) + Release 3.4.5 - 2012-09-30 diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index af3096ebf54..04a2f005c2b 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -32,6 +32,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -75,6 +76,8 @@ public String toString() { final QuorumPeer self; + private boolean quorumFormed = false; + // the follower acceptor thread LearnerCnxAcceptor cnxAcceptor; @@ -377,8 +380,6 @@ void lead() throws IOException, InterruptedException { LOG.info("NEWLEADER proposal has Zxid of " + Long.toHexString(newLeaderProposal.packet.getZxid())); } - outstandingProposals.put(newLeaderProposal.packet.getZxid(), newLeaderProposal); - newLeaderProposal.ackSet.add(self.getId()); waitForEpochAck(self.getId(), leaderStateSummary); self.setCurrentEpoch(epoch); @@ -386,33 +387,26 @@ void lead() throws IOException, InterruptedException { // We have to get at least a majority of servers in sync with // us. We do this by waiting for the NEWLEADER packet to get // acknowledged - while (!self.getQuorumVerifier().containsQuorum(newLeaderProposal.ackSet)){ - //while (newLeaderProposal.ackCount <= self.quorumPeers.size() / 2) { - if (self.tick > self.initLimit) { - // Followers aren't syncing fast enough, - // renounce leadership! - StringBuilder ackToString = new StringBuilder(); - for(Long id : newLeaderProposal.ackSet) - ackToString.append(id + ": "); + try { + waitForNewLeaderAck(self.getId(), zk.getZxid(), LearnerType.PARTICIPANT); + } catch (InterruptedException e) { + shutdown("Waiting for a quorum of followers, only synced with sids: [ " + + getSidSetString(newLeaderProposal.ackSet) + " ]"); + HashSet followerSet = new HashSet(); + for (LearnerHandler f : learners) + followerSet.add(f.getSid()); - shutdown("Waiting for a quorum of followers, only synced with: " + ackToString); - HashSet followerSet = new HashSet(); - - for(LearnerHandler f : getLearners()) { - followerSet.add(f.getSid()); - } - - if (self.getQuorumVerifier().containsQuorum(followerSet)) { - //if (followers.size() >= self.quorumPeers.size() / 2) { - LOG.warn("Enough followers present. "+ - "Perhaps the initTicks need to be increased."); - } - return; + if (self.getQuorumVerifier().containsQuorum(followerSet)) { + LOG.warn("Enough followers present. " + + "Perhaps the initTicks need to be increased."); } Thread.sleep(self.tickTime); self.tick++; + return; } + startZkServer(); + /** * WARNING: do not use this for anything other than QA testing * on a real cluster. Specifically to enable verification that quorum @@ -467,9 +461,8 @@ void lead() throws IOException, InterruptedException { if (!tickSkip && !self.getQuorumVerifier().containsQuorum(syncedSet)) { //if (!tickSkip && syncedCount < self.quorumPeers.size() / 2) { // Lost quorum, shutdown - // TODO: message is wrong unless majority quorums used - shutdown("Only " + syncedSet.size() + " followers, need " - + (self.getVotingView().size() / 2)); + shutdown("Not sufficient followers synced, only synced with sids: [ " + + getSidSetString(syncedSet) + " ]"); // make sure the order is the same! // the leader goes to looking return; @@ -542,7 +535,16 @@ synchronized public void processAck(long sid, long zxid, SocketAddress followerA } LOG.trace("outstanding proposals all"); } - + + if ((zxid & 0xffffffffL) == 0) { + /* + * We no longer process NEWLEADER ack by this method. However, + * the learner sends ack back to the leader after it gets UPTODATE + * so we just ignore the message. + */ + return; + } + if (outstandingProposals.size() == 0) { if (LOG.isDebugEnabled()) { LOG.debug("outstanding is 0"); @@ -579,26 +581,17 @@ synchronized public void processAck(long sid, long zxid, SocketAddress followerA if (p.request != null) { toBeApplied.add(p); } - // We don't commit the new leader proposal - if ((zxid & 0xffffffffL) != 0) { - if (p.request == null) { - LOG.warn("Going to commmit null request for proposal: {}", p); - } - commit(zxid); - inform(p); - zk.commitProcessor.commit(p.request); - if(pendingSyncs.containsKey(zxid)){ - for(LearnerSyncRequest r: pendingSyncs.remove(zxid)) { - sendSync(r); - } + + if (p.request == null) { + LOG.warn("Going to commmit null request for proposal: {}", p); + } + commit(zxid); + inform(p); + zk.commitProcessor.commit(p.request); + if(pendingSyncs.containsKey(zxid)){ + for(LearnerSyncRequest r: pendingSyncs.remove(zxid)) { + sendSync(r); } - return; - } else { - lastCommitted = zxid; - LOG.info("Have quorum of supporters; starting up and setting last processed zxid: 0x{}", - Long.toHexString(zk.getZxid())); - zk.startup(); - zk.getZKDatabase().setlastProcessedZxid(zk.getZxid()); } } } @@ -912,6 +905,86 @@ public void waitForEpochAck(long id, StateSummary ss) throws IOException, Interr } } + /** + * Return a list of sid in set as string + */ + private String getSidSetString(Set sidSet) { + StringBuilder sids = new StringBuilder(); + Iterator iter = sidSet.iterator(); + while (iter.hasNext()) { + sids.append(iter.next()); + if (!iter.hasNext()) { + break; + } + sids.append(","); + } + return sids.toString(); + } + + /** + * Start up Leader ZooKeeper server and initialize zxid to the new epoch + */ + private synchronized void startZkServer() { + // Update lastCommitted and Db's zxid to a value representing the new epoch + lastCommitted = zk.getZxid(); + LOG.info("Have quorum of supporters, sids: [ " + + getSidSetString(newLeaderProposal.ackSet) + + " ]; starting up and setting last processed zxid: 0x{}", + Long.toHexString(zk.getZxid())); + zk.startup(); + zk.getZKDatabase().setlastProcessedZxid(zk.getZxid()); + } + + /** + * Process NEWLEADER ack of a given sid and wait until the leader receives + * sufficient acks. + * + * @param sid + * @param learnerType + * @throws InterruptedException + */ + public void waitForNewLeaderAck(long sid, long zxid, LearnerType learnerType) + throws InterruptedException { + + synchronized (newLeaderProposal.ackSet) { + + if (quorumFormed) { + return; + } + + long currentZxid = newLeaderProposal.packet.getZxid(); + if (zxid != currentZxid) { + LOG.error("NEWLEADER ACK from sid: " + sid + + " is from a different epoch - current 0x" + + Long.toHexString(currentZxid) + " receieved 0x" + + Long.toHexString(zxid)); + return; + } + + if (learnerType == LearnerType.PARTICIPANT) { + newLeaderProposal.ackSet.add(sid); + } + + if (self.getQuorumVerifier().containsQuorum( + newLeaderProposal.ackSet)) { + quorumFormed = true; + newLeaderProposal.ackSet.notifyAll(); + } else { + long start = System.currentTimeMillis(); + long cur = start; + long end = start + self.getInitLimit() * self.getTickTime(); + while (!quorumFormed && cur < end) { + newLeaderProposal.ackSet.wait(end - cur); + cur = System.currentTimeMillis(); + } + if (!quorumFormed) { + throw new InterruptedException( + "Timeout while waiting for NEWLEADER to be acked by quorum"); + } + } + } + } + /** * Get string representation of a given packet type * @param packetType diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 2c55b371ae7..c4deee568df 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -452,7 +452,7 @@ public void run() { LOG.error("Next packet was supposed to be an ACK"); return; } - leader.processAck(this.sid, qp.getZxid(), sock.getLocalSocketAddress()); + leader.waitForNewLeaderAck(getSid(), qp.getZxid(), getLearnerType()); // now that the ack has been processed expect the syncLimit sock.setSoTimeout(leader.self.tickTime * leader.self.syncLimit); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index d59ceba8f89..bfe6da0268b 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -827,15 +827,6 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); - readPacketSkippingPing(ia, qp); - Assert.assertEquals(Leader.NEWLEADER, qp.getType()); - Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); - Assert.assertEquals(1, l.self.getAcceptedEpoch()); - Assert.assertEquals(1, l.self.getCurrentEpoch()); - - qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); - oa.writeRecord(qp, null); - readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.UPTODATE, qp.getType()); } @@ -880,13 +871,6 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); - readPacketSkippingPing(ia, qp); - Assert.assertEquals(Leader.NEWLEADER, qp.getType()); - Assert.assertEquals(ZxidUtils.makeZxid(21, 0), qp.getZxid()); - - qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); - oa.writeRecord(qp, null); - readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.UPTODATE, qp.getType()); } From b200af37a7ddeae44bf74fcc96829625bf926a44 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 25 Jan 2013 01:34:19 +0000 Subject: [PATCH 140/444] ZOOKEEPER-1495. ZK client hangs when using a function not available on the server. (Skye W-M via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1438291 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../server/UnimplementedRequestProcessor.java | 48 +++++++++++++++++++ .../zookeeper/server/ZooKeeperServer.java | 4 +- .../apache/zookeeper/TestableZooKeeper.java | 9 ++++ .../org/apache/zookeeper/test/ClientTest.java | 46 +++++++++++++++--- 5 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/server/UnimplementedRequestProcessor.java diff --git a/CHANGES.txt b/CHANGES.txt index 9957f17e424..590eb89ef1c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -36,6 +36,9 @@ BUGFIXES: ZOOKEEPER-1535. ZK Shell/Cli re-executes last command on exit (Edward Ribeiro via camille) + ZOOKEEPER-1495. ZK client hangs when using a function not available + on the server. (Skye W-M via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/UnimplementedRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/UnimplementedRequestProcessor.java new file mode 100644 index 00000000000..aa58e63d4a4 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/UnimplementedRequestProcessor.java @@ -0,0 +1,48 @@ +/** + * 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.server; + +import java.io.IOException; + +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.proto.ReplyHeader; + +/** + * Manages the unknown requests (i.e. unknown OpCode), by: + * - sending back the KeeperException.UnimplementedException() error code to the client + * - closing the connection. + */ +public class UnimplementedRequestProcessor implements RequestProcessor { + + public void processRequest(Request request) throws RequestProcessorException { + KeeperException ke = new KeeperException.UnimplementedException(); + request.setException(ke); + ReplyHeader rh = new ReplyHeader(request.cxid, request.zxid, ke.code().intValue()); + try { + request.cnxn.sendResponse(rh, null, "response"); + } catch (IOException e) { + throw new RequestProcessorException("Can't send the response", e); + } + + request.cnxn.sendCloseSession(); + } + + public void shutdown() { + } +} diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index 98ed7dc031b..a5ebb71347c 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -650,8 +650,8 @@ public void submitRequest(Request si) { incInProcess(); } } else { - LOG.warn("Dropping packet at server of type " + si.type); - // if invalid packet drop the packet. + LOG.warn("Received packet at server of unknown type " + si.type); + new UnimplementedRequestProcessor().processRequest(si); } } catch (MissingSessionException e) { if (LOG.isDebugEnabled()) { diff --git a/src/java/test/org/apache/zookeeper/TestableZooKeeper.java b/src/java/test/org/apache/zookeeper/TestableZooKeeper.java index 092eb4bdda9..dd6e246722b 100644 --- a/src/java/test/org/apache/zookeeper/TestableZooKeeper.java +++ b/src/java/test/org/apache/zookeeper/TestableZooKeeper.java @@ -22,6 +22,10 @@ import java.net.SocketAddress; import java.util.List; +import org.apache.jute.Record; +import org.apache.zookeeper.proto.ReplyHeader; +import org.apache.zookeeper.proto.RequestHeader; + public class TestableZooKeeper extends ZooKeeper { public TestableZooKeeper(String host, int sessionTimeout, @@ -99,4 +103,9 @@ public SocketAddress testableRemoteSocketAddress() { public long testableLastZxid() { return cnxn.getLastZxid(); } + + public ReplyHeader submitRequest(RequestHeader h, Record request, + Record response, WatchRegistration watchRegistration) throws InterruptedException { + return cnxn.submitRequest(h, request, response, watchRegistration); + } } diff --git a/src/java/test/org/apache/zookeeper/test/ClientTest.java b/src/java/test/org/apache/zookeeper/test/ClientTest.java index 9dc544cb4de..5dd0ac9efcb 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientTest.java @@ -18,34 +18,39 @@ package org.apache.zookeeper.test; +import static org.junit.Assert.fail; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.TestableZooKeeper; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.KeeperException.InvalidACLException; +import org.apache.zookeeper.TestableZooKeeper; +import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.Perms; +import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.proto.ExistsRequest; +import org.apache.zookeeper.proto.ExistsResponse; +import org.apache.zookeeper.proto.ReplyHeader; +import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.server.PrepRequestProcessor; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.Assert; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ClientTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(ClientTest.class); @@ -746,4 +751,33 @@ public void testClientCleanup() throws Throwable { LOG.info(logmsg,Long.valueOf(currentCount),Long.valueOf(initialFdCount)); } } + + + /** + * We create a perfectly valid 'exists' request, except that the opcode is wrong. + * @return + * @throws Exception + */ + @Test + public void testNonExistingOpCode() throws Exception { + TestableZooKeeper zk = createClient(); + + final String path = "/m1"; + + RequestHeader h = new RequestHeader(); + h.setType(888); // This code does not exists + ExistsRequest request = new ExistsRequest(); + request.setPath(path); + request.setWatch(false); + ExistsResponse response = new ExistsResponse(); + ReplyHeader r = zk.submitRequest(h, request, response, null); + + Assert.assertEquals(r.getErr(), Code.UNIMPLEMENTED.intValue()); + + try { + zk.exists("/m1", false); + fail("The connection should have been closed"); + } catch (KeeperException.ConnectionLossException expected) { + } + } } From 094d6a8a7741e816222b7d5582a83f9ff84c67e4 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 25 Jan 2013 07:15:19 +0000 Subject: [PATCH 141/444] ZOOKEEPER-1615. minor typos in ZooKeeper Programmer's Guide web page (Evan Zacks via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1438362 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ docs/bookkeeperConfig.pdf | Bin 13807 -> 13807 bytes docs/bookkeeperOverview.pdf | Bin 147574 -> 147574 bytes docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24965 bytes docs/bookkeeperStarted.pdf | Bin 17115 -> 17115 bytes docs/bookkeeperStream.pdf | Bin 13200 -> 13200 bytes docs/index.pdf | Bin 13517 -> 13517 bytes docs/javaExample.pdf | Bin 33802 -> 33802 bytes docs/linkmap.pdf | Bin 12462 -> 12462 bytes docs/recipes.pdf | Bin 31044 -> 31044 bytes docs/releasenotes.html | 12 ++++++++++++ docs/releasenotes.pdf | Bin 118218 -> 118516 bytes docs/skin/images/apache-thanks.png | Bin 0 -> 4840 bytes docs/skin/images/built-with-cocoon.gif | Bin 0 -> 2252 bytes docs/zookeeperAdmin.pdf | Bin 72882 -> 72882 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6655 bytes docs/zookeeperInternals.pdf | Bin 48834 -> 48834 bytes docs/zookeeperJMX.pdf | Bin 16482 -> 16482 bytes docs/zookeeperObservers.pdf | Bin 12873 -> 12873 bytes docs/zookeeperOver.pdf | Bin 302494 -> 302494 bytes docs/zookeeperProgrammers.html | 18 +++++++++--------- docs/zookeeperProgrammers.pdf | Bin 133787 -> 133791 bytes docs/zookeeperQuotas.pdf | Bin 11262 -> 11262 bytes docs/zookeeperStarted.pdf | Bin 27556 -> 27556 bytes docs/zookeeperTutorial.pdf | Bin 30504 -> 30504 bytes .../content/xdocs/zookeeperProgrammers.xml | 18 +++++++++--------- 26 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 docs/skin/images/apache-thanks.png create mode 100644 docs/skin/images/built-with-cocoon.gif diff --git a/CHANGES.txt b/CHANGES.txt index 590eb89ef1c..4ebce0f099a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -55,6 +55,9 @@ IMPROVEMENTS: ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the Leader to the Follower. (thawan, fpj via fpj) + ZOOKEEPER-1615. minor typos in ZooKeeper Programmer's Guide web page + (Evan Zacks via phunt) + Release 3.4.5 - 2012-09-30 diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 3a8f32d252bfb03e9d4214364a4c79c70400c183..1592ec55677c877bd1059c54614277842f049d93 100644 GIT binary patch delta 194 zcmaE#{XTobL_T8!Ln9L-V?$#TOI-ttjq|VY2pj7f80s3CgcuoH85&!G#W%O{_A+9V zn5@RHg-v{OGQXZ$oTZVuxsj`@rLmcriTSTmZ~yFKhq+ delta 194 zcmaE#{XTobL_Q+}OJf5=0~1qYV_gIDjq|VY2pj1dSn3)ZgcurFnV4FE#W%O{_A+9V zn5@RHg-v{OGQXZ$oRfvIqlKk`v751}k&&B`fuW<3fr+t+iHVV+rK!1_oq~-MAtkX~ dc6MCFC5c5P6-B9OT!uzwmPTBvs;>TSTmYqPF3JD^ diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index 439e041aaa53cfd95cec3e5930c874e5fd815dbb..d6c9f67bd27a70ee7e09348f633b111664e42c87 100644 GIT binary patch delta 182 zcmey?!1=9#b3zNBv4NqHiIK6Pv8k!9fyKs2$9aT}bqx%4jZ8v}jI9ift-#`&b9j3h zu}Mtk;n%_@-t5WW?#a)%-IJf`tbwzkvyp+DqpPctv6GRhxuuJto28S5nT4~7vxSkP RlZmmNf(;=h(<2?2WC3jXECT=l delta 182 zcmey?!1=9#b3zNBk%6VLfuVtksj-Ewf%(Qs$9aT}bPX(ZjSWH!4XjK|t-#`&b9j3h zu}Mtk;n%_@-t5WW?#a)%-IJf`tbwzon~|BJxuK(hp^Jrusi}dZnW3eVsj-2jfq|)+ RiIc0Hf(;=h(<2?2WC2(YD@_0Z diff --git a/docs/bookkeeperProgrammer.pdf b/docs/bookkeeperProgrammer.pdf index 6487743a04ebf5c095a80e4c1137dea0cf4bc13f..c6eb095bce993938e22ac2a6430278a8a218adb0 100644 GIT binary patch delta 173 zcmZoY%-DLEal%ACV*^7Y6C-0oV^d>Y1B;FGukZ*18HTz>CLu<~R))q_VDZguyuFOr zBqpozYhe@LoXmeX(AmYr(#6==+|9|t(#X-p(ap`m)yde!z{1ka(bd${$<Z<2=5Ed|#xBNgMlL4C#*TIhHiVQ+ I&Plzs98kvL`8Cw||TY<$lxAFEe zVw0Gx#;=7o1M`jZukZ*P=^9w-8XJTd8d#Z_T7ktkxAFEe zVw0Gx#;=7rn}LCuiJ_~pp|O*pft#tRtE;)Ofw_g9f(;=h JllMEx006M0DN_Id diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index 39ed2deb8384d953967f7f0491bce9b8c05cdbdd..5c5625df709447027c4316333d850c89e2dfd214 100644 GIT binary patch delta 195 zcmbP`J|TTV7oV|#p^=G^v7xc4nXZAw#u;aMgpG9#40Vl6LX3>942`Y8;+rdYdl|7w zOqSr+!X~~sl>d%$yt9#$xuLV6tEIVTSTmY*eFZBQb delta 195 zcmbP`J|TTV7oU-VrLlpbfr+WHp{{}X#u;aMgpG6!EOm_yLJSS8OiZo7;+rdYdl|7w zOqSr+!X~~sl>d%$ysLqWnTdg!qp^jtsim2ztDCv8lbfTNg`0(|o2i+RlbwPMK_#(V dc6MCFC5c5P6-B9OT!uzQ2Buu9s;>TSTmXwbFNFXA diff --git a/docs/index.pdf b/docs/index.pdf index dce8ac75bb890a8b5e59ec947c7b11ffb7909c2a..89f251beee855817ff71a76a7f3da9a44dac7051 100644 GIT binary patch delta 171 zcmX?`c{X#xT0Ua~Ln9L-V?$$8GhG9Vjk`ba2pj7f80s3CgcuoH85&!G#W(Nc?PbI! zF*%N33!C`n+5DGPoLx|Eja diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index 221c303717d4aa66c184355750465e3d517d114b..326d54b03ae94af50b5eb797daeefc15a58db660 100644 GIT binary patch delta 195 zcmZ3NxGr%*KcBIIp^=G^v7xc4sjh*=#<`bxgpG9#40Vl6LX3>942`Y8;+vazdl|7w zOjhF8!X~~sp8tbFyrH>^nUkrDk&COTlZmsri=m^jv5Tpbsgb#vxsj!*tDS-kK_#(V dc6MCFC5c5P6-B9OT!w}w2IgFTSTmaDeFWdkC delta 195 zcmZ3NxGr%*KcA6-rLlpbfr+WHsjh+f#<`bxgpG6!EOm_yLJSS8OiZo7;+vazdl|7w zOjhF8!X~~sp8tbFytA>hlcB4NnYo*ViMgApxsj=(sfD?bv5}*pfs3Jug`I*8K_#(V dc6MCFC5c5P6-B9OT!w}w2IgFTSTmZ@%FQNbd diff --git a/docs/recipes.pdf b/docs/recipes.pdf index 503806f946d628f9e1ace55026dddfe1751dc7fb..088e7dc81d275989f9c5aa9cca84e314fbaa98ec 100644 GIT binary patch delta 173 zcmX@|iSfuM#t9Snj13HpOpJ^TjZIB-4J_4tD}pNi;20TrHh%drG=e>4Iw3y HH7aEQh(#;I diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 6ab682767fb..35672bba19c 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -288,6 +288,18 @@

    Changes Since 3.4.4

    +
    + + + + + +
    + + ZOOKEEPER-1560 + +Zookeeper client hangs on creation of large nodes +
    diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 029fd3e0023f67e9eaebb4e47b73d0b5585581d7..b7219dcd2c92b238789632a14ca35760330bb681 100644 GIT binary patch delta 48916 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

    uFaeJ%q4-dN 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 z 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 LO%~=){ Vw#>&@h1DTRsSF!t=R98J{{U)BA?N@A delta 48775 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

    Gj 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 diff --git a/docs/skin/images/apache-thanks.png b/docs/skin/images/apache-thanks.png new file mode 100644 index 0000000000000000000000000000000000000000..c0bea09cc8bcb9783fa3a67cb35db96f761002c3 GIT binary patch literal 4840 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 literal 0 HcmV?d00001 diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index b062e14078932145d35ef922f050b86f29c742ee..3de56b555e454896bc1fa57e0afcb869b32d3025 100644 GIT binary patch delta 201 zcmdnAlV#IRmI)L1j13HpOpJ^TjZKYo4Jgo($r4D ihMV>44ORaIAiH!c7O^)WU8 delta 201 zcmdnAlV#IRmI)L1j0`M|4GaxTOpQ%-4a_&ryUZhOq-$WQYitl=XkcYxY6TYG+``+- zh)rU$3cnUM@#aMS?TP%1pLpUOjm({03@lv@ERD>Jj4j-ZjNM#}oSc9RM^|SjLo+)C i8-hw=x$Nw?ic1oUN-By{)40qmjLl5BR8?L5-M9cWQZdc| diff --git a/docs/zookeeperHierarchicalQuorums.pdf b/docs/zookeeperHierarchicalQuorums.pdf index 87a24e866ee7c265f8db25ae9bebee0baca749fb..ee0a88d729679db48ec146d2ab97e0eeb89c296d 100644 GIT binary patch delta 194 zcmexw{NH%OR6b(^Ln9L-V?$$816>1)jf-#a2pj7f80s3CgcuoH85&!G#W(ly_A+9V zn5@UIg-v{OHoqBHoSCV!rJIS7vzw8Dxrw=}tAT;Dg^7icsi~>CftjPZrJaHeK_#(V cc6MCFC5c5P6-B9OTxNzQhFq$uuKsRZ0D;FY+5i9m delta 194 zcmexw{NH%OR6ZjEOJf5=0~1qY6I}!Ijf-#a2pj1dSn3)ZgcurFnV4FE#W(ly_A+9V zn5@UIg-v{OHoqBHoQ0#aiKUa7v#X(@g^`7si>s@flc}kNrMZ)Zo1?3lk)47KK_#(V cc6MCFC5c5P6-B9OTxNzQhFq$uuKsRZ0KN+^!~g&Q diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index fefd90227789133e66dbb412406f17ea75b5ed29..13923146e3fcfa55366307aee26e59d5bda8f091 100644 GIT binary patch delta 197 zcmX@~m+8=7rU@;4#s-E)CPv1F#-=8^1{ND99p@1?)-^EHH8Ke?GPW`_wgQW9&f)E4 z#3nJBhhGbu_-0RjsrB)W&aO@trmn^o=H|`@Cgv_C#uf&SCWfYl<_6|YhUTVr3N{3l g#B$l$aTS*&7L`;KrKWM2n3S2=rbZ?f=BAEz3N{3l g#B$l$aTS*&7L`;KrKWM2n3nUIoL eE;~D};*!Lol8U0#G%iC^V>5FuRaIAiH!c9o1~41| delta 195 zcmaFV!1$ bxQa^>i%KerQq#B$O^waWxl~nM{oS|#*BvlG diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf index 33732d89ce940665ccc22fb2ffe7086bcf558811..aa470713a9cfd6f9d45e99188efadbf1dfdc7db2 100644 GIT binary patch delta 171 zcmX?^ax!H?3!kxpp^=G^v7xc4xvqi5#!1I{gpG9#40Vl6LX3>942`Y8;+u1Ldl|7w zOy=R&!Y01ilYgO-v#X`0lZ&f~tFx(zn}L&~le2-DtFy6*v$3n8xs!>rk)47KAtjS_ Gjbs3c7b%?p delta 171 zcmX?^ax!H?3!jmJrLlpbfr+WHnXZBP#!1I{gpG6!EOm_yLJSS8OiZo7;+u1Ldl|7w zOy=R&!Y01ilYgO-vzd{Bk*S5Lo0F-bixH6RZ0h7_X=!3)?B?v^DC+Hp$S!d#s-E)CPv1F#wO;v1{NDT_VNfD>lzs98kvL`8Cw||TY<$lNAUJC zVw0HsgHH>ac(W;gyD2{-5HkTW^LA5y7NG_XQw4KN1p|ek>5ly@NHp$S!dMh2F~28ISErpBha2Id<(_VNfD=^9w-8XJTd8d#Z_T7ktkNAUJC zVw0HsgHH>ac(W;gyD2{-5HkTW^LA5y7NLgeq5Ui}9EJ*p#tH@sLDL=kSwvWj6pReF zH}|vb=5=!XbGzW@V8rdn>5K=O|aW0E20MNHJ A`Tzg` diff --git a/docs/zookeeperProgrammers.html b/docs/zookeeperProgrammers.html index ee4522de0de..293e0a2c76a 100644 --- a/docs/zookeeperProgrammers.html +++ b/docs/zookeeperProgrammers.html @@ -496,7 +496,7 @@

    ZNodes

    znode. Znodes maintain a stat structure that includes version numbers for data changes, acl changes. The stat structure also has timestamps. The version number, together with the - timestamp allow ZooKeeper to validate the cache and to coordinate + timestamp, allows ZooKeeper to validate the cache and to coordinate updates. Each time a znode's data changes, the version number increases. For instance, whenever a client retrieves data, it also receives the version of the data. And when a client performs an update or a delete, @@ -597,7 +597,7 @@

    Time in ZooKeeper

    -

    Every change to a a node will cause an increase to one of the +

    Every change to a node will cause an increase to one of the version numbers of that node. The three version numbers are version (number of changes to the data of a znode), cversion (number of changes to the children of a znode), and aversion (number of changes @@ -942,7 +942,7 @@

    ZooKeeper Sessions

    Added in 3.2.0 -- SessionMovedException. There is an internal exception that is generally not seen by clients called the SessionMovedException. This exception occurs because a request was received on a connection for a session - which has be reestablished on a different server. The normal cause of this error is + which has been reestablished on a different server. The normal cause of this error is a client that sends a request to a server, but the network packet gets delayed, so the client times out and connects to a new server. When the delayed packet arrives at the first server, the old server detects that the session has moved, and closes the @@ -951,7 +951,7 @@

    ZooKeeper Sessions

    condition can be seen is when two clients try to reestablish the same connection using a saved session id and password. One of the clients will reestablish the connection and the second client will be disconnected (causing the pair to attempt to re-establish - it's connection/session indefinitely).

    + its connection/session indefinitely).

    @@ -1030,13 +1030,13 @@

    ZooKeeper Watches

    Watches are maintained locally at the ZooKeeper server to which the - client is connected. This allows watches to be light weight to set, + client is connected. This allows watches to be lightweight to set, maintain, and dispatch. When a client connects to a new server, the watch will be triggered for any session events. Watches will not be received while disconnected from a server. When a client reconnects, any previously registered watches will be reregistered and triggered if needed. In general this all occurs transparently. There is one case where a watch - may be missed: a watch for the existance of a znode not yet created will + may be missed: a watch for the existence of a znode not yet created will be missed if the znode is created and deleted while disconnected.

    What ZooKeeper Guarantees about Watches

    @@ -1632,7 +1632,7 @@

    Consistency Guarantees

    have been applied. On some failures (communication errors, timeouts, etc) the client will not know if the update has applied or not. We take steps to minimize the failures, but the - only guarantee is only present with successful return codes. + guarantee is only present with successful return codes. (This is called the monotonicity condition in Paxos.)

    @@ -1655,7 +1655,7 @@

    Consistency Guarantees

    The clients view of the system is guaranteed to be up-to-date - within a certain time bound. (On the order of tens of seconds.) + within a certain time bound (on the order of tens of seconds). Either system changes will be seen by a client within this bound, or the client will detect a service outage.

    @@ -1958,7 +1958,7 @@

    Using the C Client

  • Include ZooKeeper header: #include - <zookeeper/zookeeper.h

    + <zookeeper/zookeeper.h>

  • diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index b6ad0e541a5c353e8297ce3aca6d8d8ff3e954e7..168010d6121bc5b3cb656a5857bff9beaf4ebc34 100644 GIT binary patch delta 18942 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 D>h_|F diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index 0686de6dc5aa22897777d6a5b206aa0433ab65a6..bec64c1656b01d12463611e9ddcd435f87615d19 100644 GIT binary patch delta 195 zcmewt{x5t&2cNNlp^=G^v7xc4v95u|#%ZT{gnxsb8~ibbuut9w6HWXH8FQIcXc$eQ?MbZB$mt0 cj;pvNv8be?C^e1C(7@QhoJ&>J)!&T^0MZgKc>n+a delta 195 zcmewt{x5t&2cMCFrLlpbfr+WHk*c6D<$G;uMtQ?MbZB$mt0 cj;pvNv8be?C^e1C(7@QhoJ&>J)!&T^0N*Mvs{jB1 diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf index 2cc1f9aadfba9c5b6da0a752ddaa9ffe94359143..9b18a97127180be731caec15f1cb79c2264d1899 100644 GIT binary patch delta 172 zcmZ2-opH%^#t9Snj13HpOpJ^TjZKYp4JY_TNxT#fyFns@b)rd zlbEc+uZ2x~b0YtfXeSFx6JrxYR|6+g7dJyE6IT;c3j<3dS3?UIOEVV>LvuR?8v;ru Iw`a=&0QTi7p8x;= delta 172 zcmZ2-opH%^#t9Snj0`M|4GaxTOpVQS4a_&ryUZhOq-$WQYitl=XkcYxY6TYG+``+- zh)rU$3cnUM@y&_+PokaNTn&sY%`D9vja*#Kj7;3jObiW8jZDlEX~dB6l@46 JncSW&3jhJBDyIMd diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf index 6bfda861702bb68b4341ddfce7b6528b5e291ce9..d4bfca7bd9d90b135a08b316898ac51c3dd30748 100644 GIT binary patch delta 172 zcmZ4Sj&a31#tHNIj13HpOpJ^TjZG|d4Jznode. Znodes maintain a stat structure that includes version numbers for data changes, acl changes. The stat structure also has timestamps. The version number, together with the - timestamp allow ZooKeeper to validate the cache and to coordinate + timestamp, allows ZooKeeper to validate the cache and to coordinate updates. Each time a znode's data changes, the version number increases. For instance, whenever a client retrieves data, it also receives the version of the data. And when a client performs an update or a delete, @@ -265,7 +265,7 @@ Version numbers - Every change to a a node will cause an increase to one of the + Every change to a node will cause an increase to one of the version numbers of that node. The three version numbers are version (number of changes to the data of a znode), cversion (number of changes to the children of a znode), and aversion (number of changes @@ -530,7 +530,7 @@ Added in 3.2.0 -- SessionMovedException. There is an internal exception that is generally not seen by clients called the SessionMovedException. This exception occurs because a request was received on a connection for a session - which has be reestablished on a different server. The normal cause of this error is + which has been reestablished on a different server. The normal cause of this error is a client that sends a request to a server, but the network packet gets delayed, so the client times out and connects to a new server. When the delayed packet arrives at the first server, the old server detects that the session has moved, and closes the @@ -539,7 +539,7 @@ condition can be seen is when two clients try to reestablish the same connection using a saved session id and password. One of the clients will reestablish the connection and the second client will be disconnected (causing the pair to attempt to re-establish - it's connection/session indefinitely). + its connection/session indefinitely). @@ -604,13 +604,13 @@ Watches are maintained locally at the ZooKeeper server to which the - client is connected. This allows watches to be light weight to set, + client is connected. This allows watches to be lightweight to set, maintain, and dispatch. When a client connects to a new server, the watch will be triggered for any session events. Watches will not be received while disconnected from a server. When a client reconnects, any previously registered watches will be reregistered and triggered if needed. In general this all occurs transparently. There is one case where a watch - may be missed: a watch for the existance of a znode not yet created will + may be missed: a watch for the existence of a znode not yet created will be missed if the znode is created and deleted while disconnected.
    @@ -1120,7 +1120,7 @@ authProvider.2=com.f.MyAuth2 have been applied. On some failures (communication errors, timeouts, etc) the client will not know if the update has applied or not. We take steps to minimize the failures, but the - only guarantee is only present with successful return codes. + guarantee is only present with successful return codes. (This is called the monotonicity condition in Paxos.) @@ -1138,7 +1138,7 @@ authProvider.2=com.f.MyAuth2 The clients view of the system is guaranteed to be up-to-date - within a certain time bound. (On the order of tens of seconds.) + within a certain time bound (on the order of tens of seconds). Either system changes will be seen by a client within this bound, or the client will detect a service outage. @@ -1402,7 +1402,7 @@ authProvider.2=com.f.MyAuth2 Include ZooKeeper header: #include - <zookeeper/zookeeper.h + <zookeeper/zookeeper.h> From 6386376b9bc5ff0d3e92f49cba3010a0f39f1226 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 25 Jan 2013 07:25:42 +0000 Subject: [PATCH 142/444] ZOOKEEPER-1613. The documentation still points to 2008 in the copyright notice (Edward Ribeiro via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1438367 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ docs/bookkeeperConfig.html | 2 +- docs/bookkeeperConfig.pdf | Bin 13807 -> 13824 bytes docs/bookkeeperOverview.html | 2 +- docs/bookkeeperOverview.pdf | Bin 147574 -> 147596 bytes docs/bookkeeperProgrammer.html | 2 +- docs/bookkeeperProgrammer.pdf | Bin 24965 -> 24991 bytes docs/bookkeeperStarted.html | 2 +- docs/bookkeeperStarted.pdf | Bin 17115 -> 17130 bytes docs/bookkeeperStream.html | 2 +- docs/bookkeeperStream.pdf | Bin 13200 -> 13215 bytes docs/index.html | 2 +- docs/index.pdf | Bin 13517 -> 13525 bytes docs/javaExample.html | 2 +- docs/javaExample.pdf | Bin 33802 -> 33852 bytes docs/linkmap.html | 2 +- docs/linkmap.pdf | Bin 12462 -> 12472 bytes docs/recipes.html | 2 +- docs/recipes.pdf | Bin 31044 -> 31075 bytes docs/releasenotes.html | 2 +- docs/releasenotes.pdf | Bin 118516 -> 118640 bytes docs/zookeeperAdmin.html | 2 +- docs/zookeeperAdmin.pdf | Bin 72882 -> 72942 bytes docs/zookeeperHierarchicalQuorums.html | 2 +- docs/zookeeperHierarchicalQuorums.pdf | Bin 6655 -> 6660 bytes docs/zookeeperInternals.html | 2 +- docs/zookeeperInternals.pdf | Bin 48834 -> 48872 bytes docs/zookeeperJMX.html | 2 +- docs/zookeeperJMX.pdf | Bin 16482 -> 16498 bytes docs/zookeeperObservers.html | 2 +- docs/zookeeperObservers.pdf | Bin 12873 -> 12884 bytes docs/zookeeperOver.html | 2 +- docs/zookeeperOver.pdf | Bin 302494 -> 302521 bytes docs/zookeeperProgrammers.html | 2 +- docs/zookeeperProgrammers.pdf | Bin 133791 -> 133888 bytes docs/zookeeperQuotas.html | 2 +- docs/zookeeperQuotas.pdf | Bin 11262 -> 11269 bytes docs/zookeeperStarted.html | 2 +- docs/zookeeperStarted.pdf | Bin 27556 -> 27581 bytes docs/zookeeperTutorial.html | 2 +- docs/zookeeperTutorial.pdf | Bin 30504 -> 30557 bytes src/docs/src/documentation/skinconf.xml | 2 +- 42 files changed, 24 insertions(+), 21 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4ebce0f099a..d938204fe9f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -39,6 +39,9 @@ BUGFIXES: ZOOKEEPER-1495. ZK client hangs when using a function not available on the server. (Skye W-M via phunt) + ZOOKEEPER-1613. The documentation still points to 2008 in the + copyright notice (Edward Ribeiro via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/docs/bookkeeperConfig.html b/docs/bookkeeperConfig.html index fd925d3d193..d3a35af51c4 100644 --- a/docs/bookkeeperConfig.html +++ b/docs/bookkeeperConfig.html @@ -374,7 +374,7 @@

    ZooKeeper Metadata

    - 2008 + 2008-2013 The Apache Software Foundation. http://www.apache.org/licenses/ From 2bbba7b876dab45f8d36888890cc37942f220cf2 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Sun, 3 Feb 2013 06:41:11 +0000 Subject: [PATCH 143/444] ZOOKEEPER-1562. Memory leaks in zoo_multi API (Deepak Jagtap via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1441863 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index d938204fe9f..bde67fd7d8d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -42,6 +42,9 @@ BUGFIXES: ZOOKEEPER-1613. The documentation still points to 2008 in the copyright notice (Edward Ribeiro via phunt) + ZOOKEEPER-1562. Memory leaks in zoo_multi API + (Deepak Jagtap via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index de58c62b55e..758e49b0a4c 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1979,6 +1979,10 @@ static int deserialize_multi(int xid, completion_list_t *cptr, struct iarchive * deserialize_response(entry->c.type, xid, mhdr.type == -1, mhdr.err, entry, ia); deserialize_MultiHeader(ia, "multiheader", &mhdr); + //While deserializing the response we must destroy completion entry for each operation in + //the zoo_multi transaction. Otherwise this results in memory leak when client invokes zoo_multi + //operation. + destroy_completion_entry(entry); } return rc; From da74b8df26a4c80f405bd2ba37cac4dc7a594b4a Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Sat, 16 Feb 2013 00:50:18 +0000 Subject: [PATCH 144/444] ZOOKEEPER-1645. ZooKeeper OSGi package imports not complete (Arnoud Glimmerveen via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1446829 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index bde67fd7d8d..fac50c6b8d5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -45,6 +45,9 @@ BUGFIXES: ZOOKEEPER-1562. Memory leaks in zoo_multi API (Deepak Jagtap via phunt) + ZOOKEEPER-1645. ZooKeeper OSGi package imports not complete + (Arnoud Glimmerveen via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/build.xml b/build.xml index 391caa0d6e5..244e075d248 100644 --- a/build.xml +++ b/build.xml @@ -537,7 +537,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 36724d557a18fa9ee47873b18f6e963516930dbd Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 19 Feb 2013 07:55:58 +0000 Subject: [PATCH 145/444] ZOOKEEPER-1648. Fix WatcherTest in JDK7 (Thawan Kooburat via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1447614 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/test/org/apache/zookeeper/test/WatcherTest.java | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index fac50c6b8d5..b74d7737b06 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -48,6 +48,9 @@ BUGFIXES: ZOOKEEPER-1645. ZooKeeper OSGi package imports not complete (Arnoud Glimmerveen via phunt) + ZOOKEEPER-1648. Fix WatcherTest in JDK7 + (Thawan Kooburat via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/WatcherTest.java b/src/java/test/org/apache/zookeeper/test/WatcherTest.java index 90ec5136ae4..6bead898bed 100644 --- a/src/java/test/org/apache/zookeeper/test/WatcherTest.java +++ b/src/java/test/org/apache/zookeeper/test/WatcherTest.java @@ -39,6 +39,7 @@ import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; public class WatcherTest extends ClientBase { @@ -68,6 +69,14 @@ public void process(WatchedEvent event) { } } + @Before + public void setUp() throws Exception { + super.setUp(); + // Reset to default value since some test cases set this to true. + // Needed for JDK7 since unit test can run is random order + ClientCnxn.setDisableAutoResetWatch(false); + } + /** * Verify that we get all of the events we expect to get. This particular * case verifies that we see all of the data events on a particular node. From 7b4847231e4d890b7c4b256f2995993b821d697d Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 19 Feb 2013 08:18:42 +0000 Subject: [PATCH 146/444] ZOOKEEPER-1606. intermittent failures in ZkDatabaseCorruptionTest on jenkins (lixiaofeng via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1447619 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../test/ZkDatabaseCorruptionTest.java | 45 ++++++++++++------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b74d7737b06..fbc8ef1be72 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -51,6 +51,9 @@ BUGFIXES: ZOOKEEPER-1648. Fix WatcherTest in JDK7 (Thawan Kooburat via phunt) + ZOOKEEPER-1606. intermittent failures in ZkDatabaseCorruptionTest on + jenkins (lixiaofeng via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java b/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java index d8f4138123e..a45d8b47bf4 100644 --- a/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java +++ b/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.Arrays; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; @@ -83,30 +84,42 @@ public void process(WatchedEvent event) { zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); - QuorumPeer leader; + + long leaderSid = 1; + QuorumPeer leader = null; //find out who is the leader and kill it - if ( qb.s5.getPeerState() != ServerState.LEADING) { - throw new Exception("the last server is not the leader"); + for (QuorumPeer quorumPeer : Arrays.asList(qb.s1, qb.s2, qb.s3, qb.s4, qb.s5)) { + if (quorumPeer.getPeerState() == ServerState.LEADING) { + leader = quorumPeer; + break; + } + ++leaderSid; } - leader = qb.s5; - // now corrupt the qurompeer database + + Assert.assertNotNull("Cannot find the leader.", leader); + leader.shutdown(); + + // now corrupt the leader's database FileTxnSnapLog snapLog = leader.getTxnFactory(); File snapDir= snapLog.getSnapDir(); //corrupt all the snapshot in the snapshot directory corruptAllSnapshots(snapDir); qb.shutdownServers(); qb.setupServers(); - qb.s1.start(); - qb.s2.start(); - qb.s3.start(); - qb.s4.start(); + + if (leaderSid != 1)qb.s1.start(); else leader = qb.s1; + if (leaderSid != 2)qb.s2.start(); else leader = qb.s2; + if (leaderSid != 3)qb.s3.start(); else leader = qb.s3; + if (leaderSid != 4)qb.s4.start(); else leader = qb.s4; + if (leaderSid != 5)qb.s5.start(); else leader = qb.s5; + try { - qb.s5.start(); + leader.start(); Assert.assertTrue(false); } catch(RuntimeException re) { LOG.info("Got an error: expected", re); } - //waut for servers to be up + //wait for servers to be up String[] list = qb.hostPort.split(","); for (int i =0; i < 4; i++) { String hp = list[i]; @@ -122,10 +135,12 @@ public void process(WatchedEvent event) { zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); - QuorumBase.shutdown(qb.s1); - QuorumBase.shutdown(qb.s2); - QuorumBase.shutdown(qb.s3); - QuorumBase.shutdown(qb.s4); + + if (leaderSid != 1)QuorumBase.shutdown(qb.s1); + if (leaderSid != 2)QuorumBase.shutdown(qb.s2); + if (leaderSid != 3)QuorumBase.shutdown(qb.s3); + if (leaderSid != 4)QuorumBase.shutdown(qb.s4); + if (leaderSid != 5)QuorumBase.shutdown(qb.s5); } From 85f8053448871b9777e54e546a968f9c4d19ebbc Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 19 Feb 2013 08:28:54 +0000 Subject: [PATCH 147/444] ZOOKEEPER-1647. OSGi package import/export changes not applied to bin-jar (Arnoud Glimmerveen via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1447622 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index fbc8ef1be72..07e1e31a4f0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,9 @@ BUGFIXES: ZOOKEEPER-1606. intermittent failures in ZkDatabaseCorruptionTest on jenkins (lixiaofeng via phunt) + ZOOKEEPER-1647. OSGi package import/export changes not applied to + bin-jar (Arnoud Glimmerveen via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/build.xml b/build.xml index 244e075d248..57d3171d4ad 100644 --- a/build.xml +++ b/build.xml @@ -578,8 +578,8 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - - + + From b8306fa5a9e16da6981a2fdf2df059149687c7e4 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Tue, 2 Apr 2013 06:31:43 +0000 Subject: [PATCH 148/444] ZOOKEEPER-1633. Introduce a protocol version to connection initiation message (Alexander Shraer via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1463399 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../server/quorum/QuorumCnxManager.java | 11 ++ .../quorum}/CnxManagerTest.java | 107 +++++++++++++++++- 3 files changed, 119 insertions(+), 2 deletions(-) rename src/java/test/org/apache/zookeeper/{test => server/quorum}/CnxManagerTest.java (77%) diff --git a/CHANGES.txt b/CHANGES.txt index 07e1e31a4f0..d3e78463a71 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -57,6 +57,9 @@ BUGFIXES: ZOOKEEPER-1647. OSGi package import/export changes not applied to bin-jar (Arnoud Glimmerveen via phunt) + ZOOKEEPER-1633. Introduce a protocol version to connection initiation + message (Alexander Shraer via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 97f2a054576..f897ce2fa4e 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -232,6 +232,17 @@ public boolean receiveConnection(Socket sock) { // Read server id DataInputStream din = new DataInputStream(sock.getInputStream()); sid = din.readLong(); + if (sid < 0) { // this is not a server id but a protocol version (see ZOOKEEPER-1633) + sid = din.readLong(); + // next comes the #bytes in the remainder of the message + int num_remaining_bytes = din.readInt(); + byte[] b = new byte[num_remaining_bytes]; + // remove the remainder of the message from din + int num_read = din.read(b); + if (num_read != num_remaining_bytes) { + LOG.error("Read only " + num_read + " bytes out of " + num_remaining_bytes + " sent by server " + sid); + } + } if (sid == QuorumPeer.OBSERVER_ID) { /* * Choose identifier at random. We need a value to identify diff --git a/src/java/test/org/apache/zookeeper/test/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java similarity index 77% rename from src/java/test/org/apache/zookeeper/test/CnxManagerTest.java rename to src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java index fed68c16935..866f8d0e2d8 100644 --- a/src/java/test/org/apache/zookeeper/test/CnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java @@ -16,8 +16,9 @@ * limitations under the License. */ -package org.apache.zookeeper.test; +package org.apache.zookeeper.server.quorum; +import java.io.DataOutputStream; import java.io.File; import java.net.InetSocketAddress; import java.nio.ByteBuffer; @@ -38,12 +39,13 @@ import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.apache.zookeeper.test.ClientBase; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class CnxManagerTest extends ZKTestCase { - protected static final Logger LOG = LoggerFactory.getLogger(FLENewEpochTest.class); + protected static final Logger LOG = LoggerFactory.getLogger(CnxManagerTest.class); protected static final int THRESHOLD = 4; int count; @@ -264,6 +266,107 @@ public void testCnxManagerSpinLock() throws Exception { cnxManager.halt(); } + /* + * Class used with testCnxFromFutureVersion + */ + class TestCnxManager extends QuorumCnxManager { + + TestCnxManager(QuorumPeer self) { + super(self); + } + + boolean senderWorkerMapContains(Long l){ + return senderWorkerMap.containsKey(l); + } + + long getSid(Message m){ + return m.sid; + } + + String getMsgString(Message m){ + return new String(m.buffer.array()); + } + } + + /** + * Before 3.5.0 a server sends its id when connecting to another server. + * Starting with 3.5.0 a server will send a protocol version, followed by + * its id, then number of bytes in the remainder of the message and finally + * the rest of the message. The test makes sure that a 3.4.6 server is able + * to detect that a connection message has this new format, extract the id, + * and skip the remainder of the message. + * + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1633} + * + * @throws Exception + */ + @Test + public void testCnxFromFutureVersion() throws Exception { + QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2); + TestCnxManager cnxManager = new TestCnxManager(peer); + QuorumCnxManager.Listener listener = cnxManager.listener; + if(listener != null){ + listener.start(); + } else { + Assert.fail("Null listener when initializing cnx manager"); + } + + int port = peers.get(peer.getId()).electionAddr.getPort(); + LOG.info("Election port: " + port); + + Thread.sleep(1000); + + SocketChannel sc = SocketChannel.open(); + sc.socket().connect(peers.get(new Long(1)).electionAddr, 5000); + + InetSocketAddress otherAddr = peers.get(new Long(2)).electionAddr; + DataOutputStream dout = new DataOutputStream(sc.socket().getOutputStream()); + // protocol version - a negative number + dout.writeLong(0xffff0000); + // server id + dout.writeLong(new Long(2)); + // other stuff that a 3.5.0 server will send - not important for 3.4.6 + // the 3.4.6 server should just skip it + String addr = otherAddr.getHostName()+ ":" + otherAddr.getPort(); + byte[] addr_bytes = addr.getBytes(); + dout.writeInt(addr_bytes.length); + dout.write(addr_bytes); + dout.flush(); + + Thread.sleep(1000); + + Assert.assertEquals("Server 1 got connection request from server 2", + true, cnxManager.senderWorkerMapContains(new Long(2))); + + // send another message to make sure the connection message was processed + // properly (mainly that its suffix was removed from the stream) + String testStr = "this is a test message string"; + byte[] testStr_bytes = testStr.getBytes(); + dout.writeInt(testStr_bytes.length); + dout.write(testStr_bytes); + dout.flush(); + + Message m = null; + int numRetries = 1; + while((m == null) && (numRetries++ <= THRESHOLD)){ + m = cnxManager.pollRecvQueue(3000, TimeUnit.MILLISECONDS); + if(m == null) cnxManager.connectAll(); + } + + if(numRetries > THRESHOLD){ + Assert.fail("Test message hasn't been found in recvQueue"); + } + + //Assert.assertEquals("Message sender should be 2", 2, m.sid); + Assert.assertEquals("Message sender should be 2", 2, cnxManager.getSid(m)); + Assert.assertEquals("Message from 2 doesn't match test sring", testStr, + cnxManager.getMsgString(m)); + + peer.shutdown(); + cnxManager.halt(); + } + + /* * Test if a receiveConnection is able to timeout on socket errors */ From a51d8a94bc1150b55cd9fb6992cc5187e600184b Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 11 May 2013 13:32:38 +0000 Subject: [PATCH 149/444] ZOOKEEPER-1697: large snapshots can cause continuous quorum failure (phunt via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1481322 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../server/quorum/LearnerHandler.java | 22 +++++++++++++------ .../zookeeper/server/quorum/QuorumPeer.java | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d3e78463a71..7b9d04c27a2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -60,6 +60,9 @@ BUGFIXES: ZOOKEEPER-1633. Introduce a protocol version to connection initiation message (Alexander Shraer via michim) + ZOOKEEPER-1697: large snapshots can cause continuous quorum failure + (phunt via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index c4deee568df..1ddf6a6a7b1 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -64,7 +64,11 @@ public Socket getSocket() { final Leader leader; - long tickOfLastAck; + /** Deadline for receiving the next ack. If we are bootstrapping then + * it's based on the initLimit, if we are done bootstrapping it's based + * on the syncLimit. Once the deadline is past this learner should + * be considered no longer "sync'd" with the leader. */ + volatile long tickOfNextAckDeadline; /** * ZooKeeper server identifier of this learner @@ -104,7 +108,7 @@ int getVersion() { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("LearnerHandler ").append(sock); - sb.append(" tickOfLastAck:").append(tickOfLastAck()); + sb.append(" tickOfNextAckDeadline:").append(tickOfNextAckDeadline()); sb.append(" synced?:").append(synced()); sb.append(" queuedPacketLength:").append(queuedPackets.size()); return sb.toString(); @@ -232,7 +236,10 @@ static public String packetToString(QuorumPacket p) { */ @Override public void run() { - try { + try { + tickOfNextAckDeadline = leader.self.tick + + leader.self.initLimit + leader.self.syncLimit; + ia = BinaryInputArchive.getArchive(new BufferedInputStream(sock .getInputStream())); bufferedOutput = new BufferedOutputStream(sock.getOutputStream()); @@ -452,6 +459,7 @@ public void run() { LOG.error("Next packet was supposed to be an ACK"); return; } + LOG.info("Received NEWLEADER-ACK message from " + getSid()); leader.waitForNewLeaderAck(getSid(), qp.getZxid(), getLearnerType()); // now that the ack has been processed expect the syncLimit @@ -482,7 +490,7 @@ public void run() { if (LOG.isTraceEnabled()) { ZooTrace.logQuorumPacket(LOG, traceMask, 'i', qp); } - tickOfLastAck = leader.self.tick; + tickOfNextAckDeadline = leader.self.tick + leader.self.syncLimit; ByteBuffer bb; @@ -597,8 +605,8 @@ public void shutdown() { leader.removeLearnerHandler(this); } - public long tickOfLastAck() { - return tickOfLastAck; + public long tickOfNextAckDeadline() { + return tickOfNextAckDeadline; } /** @@ -620,6 +628,6 @@ void queuePacket(QuorumPacket p) { public boolean synced() { return isAlive() - && tickOfLastAck >= leader.self.tick - leader.self.syncLimit; + && leader.self.tick <= tickOfNextAckDeadline; } } 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 3eee23dca0b..22c4aecf8a2 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -246,7 +246,7 @@ public synchronized void setCurrentVote(Vote v){ /** * The current tick */ - protected int tick; + protected volatile int tick; /** * @deprecated As of release 3.4.0, this class has been deprecated, since From f26bda98b95e5c3fc35c177cf8fb7a3898773d70 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Mon, 13 May 2013 07:40:13 +0000 Subject: [PATCH 150/444] ZOOKEEPER-1706. Typo in Double Barriers example (Jingguo Yao via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1481722 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 +++- src/docs/src/documentation/content/xdocs/recipes.xml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7b9d04c27a2..44050852a32 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -60,9 +60,11 @@ BUGFIXES: ZOOKEEPER-1633. Introduce a protocol version to connection initiation message (Alexander Shraer via michim) - ZOOKEEPER-1697: large snapshots can cause continuous quorum failure + ZOOKEEPER-1697. large snapshots can cause continuous quorum failure (phunt via fpj) + ZOOKEEPER-1706. Typo in Double Barriers example (Jingguo Yao via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/docs/src/documentation/content/xdocs/recipes.xml b/src/docs/src/documentation/content/xdocs/recipes.xml index c982d216340..ead041beb10 100644 --- a/src/docs/src/documentation/content/xdocs/recipes.xml +++ b/src/docs/src/documentation/content/xdocs/recipes.xml @@ -203,7 +203,7 @@ if p is the lowest process - node in L, wait on highest process node in P + node in L, wait on highest process node in L From aeda031727655806523fffec84973cd65d4bae62 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Thu, 16 May 2013 16:55:41 +0000 Subject: [PATCH 151/444] ZOOKEEPER-1642. Leader loading database twice (fpj via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1483441 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../zookeeper/server/ZooKeeperServer.java | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 44050852a32..71ec474bab8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -64,6 +64,8 @@ BUGFIXES: (phunt via fpj) ZOOKEEPER-1706. Typo in Double Barriers example (Jingguo Yao via fpj) + + ZOOKEEPER-1642. Leader Loading Database Twice (fpj via camille) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index a5ebb71347c..4121ee408df 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -247,7 +247,31 @@ public void setZKDatabase(ZKDatabase zkDb) { * Restore sessions and data */ public void loadData() throws IOException, InterruptedException { - setZxid(zkDb.loadDataBase()); + /* + * When a new leader starts executing Leader#lead, it + * invokes this method. The database, however, has been + * initialized before running leader election so that + * the server could pick its zxid for its initial vote. + * It does it by invoking QuorumPeer#getLastLoggedZxid. + * Consequently, we don't need to initialize it once more + * and avoid the penalty of loading it a second time. Not + * reloading it is particularly important for applications + * that host a large database. + * + * The following if block checks whether the database has + * been initialized or not. Note that this method is + * invoked by at least one other method: + * ZooKeeperServer#startdata. + * + * See ZOOKEEPER-1642 for more detail. + */ + if(zkDb.isInitialized()){ + setZxid(zkDb.getDataTreeLastProcessedZxid()); + } + else { + setZxid(zkDb.loadDataBase()); + } + // Clean up dead sessions LinkedList deadSessions = new LinkedList(); for (Long session : zkDb.getSessions()) { From b1f252d4bc795dbd4f39e40f00398bd6a9b9d0c1 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Mon, 20 May 2013 17:09:28 +0000 Subject: [PATCH 152/444] ZOOKEEPER-1663. scripts don't work when path contains spaces (Amichai Rothman via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1484528 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ bin/zkCleanup.sh | 12 ++++++------ bin/zkCli.sh | 6 +++--- bin/zkEnv.sh | 13 +++++++------ bin/zkServer.sh | 36 ++++++++++++++++++------------------ 5 files changed, 36 insertions(+), 33 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 71ec474bab8..7596b764e01 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -66,6 +66,8 @@ BUGFIXES: ZOOKEEPER-1706. Typo in Double Barriers example (Jingguo Yao via fpj) ZOOKEEPER-1642. Leader Loading Database Twice (fpj via camille) + + ZOOKEEPER-1663. scripts don't work when path contains spaces (Amichai Rothman via camille) IMPROVEMENTS: diff --git a/bin/zkCleanup.sh b/bin/zkCleanup.sh index f75731f64bd..3d47b98c0d4 100755 --- a/bin/zkCleanup.sh +++ b/bin/zkCleanup.sh @@ -27,8 +27,8 @@ # use POSTIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN=`dirname ${ZOOBIN}` -ZOOBINDIR=`cd ${ZOOBIN}; pwd` +ZOOBIN="$(dirname "${ZOOBIN}")" +ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh @@ -36,16 +36,16 @@ else . "$ZOOBINDIR"/zkEnv.sh fi -ZOODATADIR=$(grep "^[[:space:]]*dataDir=" "$ZOOCFG" | sed -e 's/.*=//') -ZOODATALOGDIR=$(grep "^[[:space:]]*dataLogDir=" "$ZOOCFG" | sed -e 's/.*=//') +ZOODATADIR="$(grep "^[[:space:]]*dataDir=" "$ZOOCFG" | sed -e 's/.*=//')" +ZOODATALOGDIR="$(grep "^[[:space:]]*dataLogDir=" "$ZOOCFG" | sed -e 's/.*=//')" if [ "x$ZOODATALOGDIR" = "x" ] then -$JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ +"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.PurgeTxnLog "$ZOODATADIR" $* else -$JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ +"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.PurgeTxnLog "$ZOODATALOGDIR" "$ZOODATADIR" $* fi diff --git a/bin/zkCli.sh b/bin/zkCli.sh index 6fdb863d955..0b0682472c7 100755 --- a/bin/zkCli.sh +++ b/bin/zkCli.sh @@ -27,8 +27,8 @@ # use POSTIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN=`dirname ${ZOOBIN}` -ZOOBINDIR=`cd ${ZOOBIN}; pwd` +ZOOBIN="$(dirname "${ZOOBIN}")" +ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh @@ -36,6 +36,6 @@ else . "$ZOOBINDIR"/zkEnv.sh fi -$JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ +"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \ org.apache.zookeeper.ZooKeeperMain "$@" diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh index c6c5d21c94a..267f10ea27e 100755 --- a/bin/zkEnv.sh +++ b/bin/zkEnv.sh @@ -23,8 +23,8 @@ # or the conf directory that is # a sibling of this script's directory -ZOOBINDIR=${ZOOBINDIR:-/usr/bin} -ZOOKEEPER_PREFIX=${ZOOBINDIR}/.. +ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" +ZOOKEEPER_PREFIX="${ZOOBINDIR}/.." if [ "x$ZOOCFGDIR" = "x" ] then @@ -76,18 +76,19 @@ do done #make it work in the binary package -if [ -e ${ZOOKEEPER_PREFIX}/share/zookeeper/zookeeper-*.jar ]; then - LIBPATH="${ZOOKEEPER_PREFIX}"/share/zookeeper/*.jar +#(use array for LIBPATH to account for spaces within wildcard expansion) +if [ -e "${ZOOKEEPER_PREFIX}"/share/zookeeper/zookeeper-*.jar ]; then + LIBPATH=("${ZOOKEEPER_PREFIX}"/share/zookeeper/*.jar) else #release tarball format for i in "$ZOOBINDIR"/../zookeeper-*.jar do CLASSPATH="$i:$CLASSPATH" done - LIBPATH="${ZOOBINDIR}"/../lib/*.jar + LIBPATH=("${ZOOBINDIR}"/../lib/*.jar) fi -for i in ${LIBPATH} +for i in "${LIBPATH[@]}" do CLASSPATH="$i:$CLASSPATH" done diff --git a/bin/zkServer.sh b/bin/zkServer.sh index aa6cbaa45f4..c1880b1aecd 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -44,13 +44,13 @@ fi # use POSTIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN=`dirname ${ZOOBIN}` -ZOOBINDIR=`cd ${ZOOBIN}; pwd` +ZOOBIN="$(dirname "${ZOOBIN}")" +ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then - . "$ZOOBINDIR"/../libexec/zkEnv.sh + . "$ZOOBINDIR/../libexec/zkEnv.sh" else - . "$ZOOBINDIR"/zkEnv.sh + . "$ZOOBINDIR/zkEnv.sh" fi if [ "x$SERVER_JVMFLAGS" != "x" ] @@ -64,7 +64,7 @@ then fi # if we give a more complicated path to the config, don't screw around in $ZOOCFGDIR -if [ "x`dirname $ZOOCFG`" != "x$ZOOCFGDIR" ] +if [ "x$(dirname "$ZOOCFG")" != "x$ZOOCFGDIR" ] then ZOOCFG="$2" fi @@ -80,15 +80,15 @@ fi echo "Using config: $ZOOCFG" >&2 -if [ -z $ZOOPIDFILE ]; then - ZOO_DATADIR=$(grep "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//') +if [ -z "$ZOOPIDFILE" ]; then + ZOO_DATADIR="$(grep "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" if [ ! -d "$ZOO_DATADIR" ]; then mkdir -p "$ZOO_DATADIR" fi ZOOPIDFILE="$ZOO_DATADIR/zookeeper_server.pid" else # ensure it exists, otw stop will fail - mkdir -p $(dirname "$ZOOPIDFILE") + mkdir -p "$(dirname "$ZOOPIDFILE")" fi if [ ! -w "$ZOO_LOG_DIR" ] ; then @@ -100,13 +100,13 @@ _ZOO_DAEMON_OUT="$ZOO_LOG_DIR/zookeeper.out" case $1 in start) echo -n "Starting zookeeper ... " - if [ -f $ZOOPIDFILE ]; then - if kill -0 `cat $ZOOPIDFILE` > /dev/null 2>&1; then - echo $command already running as process `cat $ZOOPIDFILE`. + if [ -f "$ZOOPIDFILE" ]; then + if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then + echo $command already running as process `cat "$ZOOPIDFILE"`. exit 0 fi fi - nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ + nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null & if [ $? -eq 0 ] then @@ -124,15 +124,15 @@ start) fi ;; start-foreground) - ZOO_CMD="exec $JAVA" + ZOO_CMD=(exec "$JAVA") if [ "${ZOO_NOEXEC}" != "" ]; then - ZOO_CMD="$JAVA" + ZOO_CMD=("$JAVA") fi - $ZOO_CMD "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ + "${ZOO_CMD[@]}" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" ;; print-cmd) - echo "$JAVA -Dzookeeper.log.dir=\"${ZOO_LOG_DIR}\" -Dzookeeper.root.logger=\"${ZOO_LOG4J_PROP}\" -cp \"$CLASSPATH\" $JVMFLAGS $ZOOMAIN \"$ZOOCFG\" > \"$_ZOO_DAEMON_OUT\" 2>&1 < /dev/null" + echo "\"$JAVA\" -Dzookeeper.log.dir=\"${ZOO_LOG_DIR}\" -Dzookeeper.root.logger=\"${ZOO_LOG4J_PROP}\" -cp \"$CLASSPATH\" $JVMFLAGS $ZOOMAIN \"$ZOOCFG\" > \"$_ZOO_DAEMON_OUT\" 2>&1 < /dev/null" ;; stop) echo -n "Stopping zookeeper ... " @@ -149,7 +149,7 @@ stop) upgrade) shift echo "upgrading the servers to 3.*" - $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ + "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.server.upgrade.UpgradeMain ${@} echo "Upgrading ... " ;; @@ -161,7 +161,7 @@ restart) ;; status) # -q is necessary on some versions of linux where nc returns too quickly, and no stat result is output - STAT=`$JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ + STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain localhost \ $(grep "^[[:space:]]*clientPort" "$ZOOCFG" | sed -e 's/.*=//') srvr 2> /dev/null \ | grep Mode` From a29816358f6ab72d47aa100ba3c8ca37ab360a21 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Fri, 21 Jun 2013 17:58:12 +0000 Subject: [PATCH 153/444] ZOOKEEPER-1714 perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used (Botond Hejj via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1495523 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/contrib/zkperl/ZooKeeper.xs | 6 +++--- src/contrib/zkperl/t/50_access.t | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7596b764e01..c24a6861cc9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -69,6 +69,8 @@ BUGFIXES: ZOOKEEPER-1663. scripts don't work when path contains spaces (Amichai Rothman via camille) + ZOOKEEPER-1714 perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used + (Botond Hejj via camille) IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/contrib/zkperl/ZooKeeper.xs b/src/contrib/zkperl/ZooKeeper.xs index dbc13827e48..2b475e1e5dc 100644 --- a/src/contrib/zkperl/ZooKeeper.xs +++ b/src/contrib/zkperl/ZooKeeper.xs @@ -695,13 +695,13 @@ zk_acl_constant(alias=Nullch) alias = GvNAME(CvGV(cv)); } - if (ix == 1 || strEQ(alias, "ZOO_OPEN_ACL_UNSAFE")) { + if (ix == 1 || (alias != NULL && strEQ(alias, "ZOO_OPEN_ACL_UNSAFE"))) { acl = ZOO_OPEN_ACL_UNSAFE; } - else if (ix == 2 || strEQ(alias, "ZOO_READ_ACL_UNSAFE")) { + else if (ix == 2 || (alias != NULL && strEQ(alias, "ZOO_READ_ACL_UNSAFE"))) { acl = ZOO_READ_ACL_UNSAFE; } - else if (ix == 3 || strEQ(alias, "ZOO_CREATOR_ALL_ACL")) { + else if (ix == 3 || (alias != NULL && strEQ(alias, "ZOO_CREATOR_ALL_ACL"))) { acl = ZOO_CREATOR_ALL_ACL; } else { diff --git a/src/contrib/zkperl/t/50_access.t b/src/contrib/zkperl/t/50_access.t index 1610319f898..ef61ed6688f 100644 --- a/src/contrib/zkperl/t/50_access.t +++ b/src/contrib/zkperl/t/50_access.t @@ -17,7 +17,7 @@ # limitations under the License. use File::Spec; -use Test::More tests => 38; +use Test::More tests => 40; BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; @@ -51,6 +51,22 @@ SKIP: { $no_read_acl->[0]->{'perms'} == ZOO_PERM_ALL), '_zk_acl_constant(): returned default ACL'); + my $zoo_read_acl_unsafe = ZOO_READ_ACL_UNSAFE; + ok((ref($zoo_read_acl_unsafe) eq 'ARRAY' and + @{$zoo_read_acl_unsafe} == 1 and + ref($zoo_read_acl_unsafe->[0]) eq 'HASH' and + keys(%{$zoo_read_acl_unsafe->[0]}) == 3 and + $zoo_read_acl_unsafe->[0]->{'perms'} == ZOO_PERM_READ), + '_zk_acl_constant(): returned good ACL'); + + my $zoo_creator_all_acl = ZOO_CREATOR_ALL_ACL; + ok((ref($zoo_creator_all_acl) eq 'ARRAY' and + @{$zoo_creator_all_acl} == 1 and + ref($zoo_creator_all_acl->[0]) eq 'HASH' and + keys(%{$zoo_creator_all_acl->[0]}) == 3 and + $zoo_creator_all_acl->[0]->{'perms'} == ZOO_PERM_ALL), + '_zk_acl_constant(): returned good ACL'); + $no_read_acl->[0]->{'perms'} &= ~ZOO_PERM_READ; is($no_read_acl->[0]->{'perms'}, ((ZOO_PERM_ALL) & ~ZOO_PERM_READ), 'assign: altered default ACL'); From 38e3a782acc746e8d7aef56983c21c13e6486c2b Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Fri, 28 Jun 2013 16:28:09 +0000 Subject: [PATCH 154/444] ZOOKEEPER-1719. zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 (Marshall McMullen via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1497831 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++++ bin/zkCleanup.sh | 2 +- bin/zkCli.sh | 2 +- bin/zkEnv.sh | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c24a6861cc9..84b0bec6efd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -71,6 +71,10 @@ BUGFIXES: ZOOKEEPER-1714 perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used (Botond Hejj via camille) + + ZOOKEEPER-1719. zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 + (Marshall McMullen via camille) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/bin/zkCleanup.sh b/bin/zkCleanup.sh index 3d47b98c0d4..38ee2e8be4a 100755 --- a/bin/zkCleanup.sh +++ b/bin/zkCleanup.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/bin/zkCli.sh b/bin/zkCli.sh index 0b0682472c7..992a91367d9 100755 --- a/bin/zkCli.sh +++ b/bin/zkCli.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh index 267f10ea27e..e6e90dfe8c5 100755 --- a/bin/zkEnv.sh +++ b/bin/zkEnv.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with From ab720e321cff0e237b45e4b8d00ebf61cf66ab0b Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 1 Jul 2013 23:22:45 +0000 Subject: [PATCH 155/444] ZOOKEEPER-1702. ZooKeeper client may write operation packets before receiving successful response to connection request, can cause TCP RST (Chris Nauroth via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1498738 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++++ .../org/apache/zookeeper/ClientCnxnSocketNIO.java | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 84b0bec6efd..64df193e0b7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -75,6 +75,10 @@ BUGFIXES: ZOOKEEPER-1719. zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 (Marshall McMullen via camille) + ZOOKEEPER-1702. ZooKeeper client may write operation packets before + receiving successful response to connection request, can cause TCP + RST (Chris Nauroth via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java index 89c4b4155cb..720619d8925 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java @@ -134,6 +134,17 @@ void doIO(List pendingQueue, LinkedList outgoingQueue, ClientCnx // to attempt SASL authentication), or in either doIO() or // in doTransport() if not. disableWrite(); + } else if (!initialized && p != null && !p.bb.hasRemaining()) { + // On initial connection, write the complete connect request + // packet, but then disable further writes until after + // receiving a successful connection response. If the + // session is expired, then the server sends the expiration + // response and immediately closes its end of the socket. If + // the client is simultaneously writing on its end, then the + // TCP stack may choose to abort with RST, in which case the + // client would never receive the session expired event. See + // http://docs.oracle.com/javase/6/docs/technotes/guides/net/articles/connection_release.html + disableWrite(); } else { // Just in case enableWrite(); From a4211cadba07476bc4a3a49a837af992dee21a7b Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Sun, 14 Jul 2013 22:20:49 +0000 Subject: [PATCH 156/444] ZOOKEEPER-1629. testTransactionLogCorruption occasionally fails. (shralex via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1503073 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/TruncateCorruptionTest.java | 323 ------------------ .../zookeeper/server/util/PortForwarder.java | 49 +++ .../apache/zookeeper/test/TruncateTest.java | 3 +- 4 files changed, 53 insertions(+), 324 deletions(-) delete mode 100644 src/java/test/org/apache/zookeeper/server/TruncateCorruptionTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 64df193e0b7..1d64062b1f6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -79,6 +79,8 @@ BUGFIXES: receiving successful response to connection request, can cause TCP RST (Chris Nauroth via phunt) + ZOOKEEPER-1629. testTransactionLogCorruption occasionally fails. (shralex via camille) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/server/TruncateCorruptionTest.java b/src/java/test/org/apache/zookeeper/server/TruncateCorruptionTest.java deleted file mode 100644 index 2f9f9ef24e2..00000000000 --- a/src/java/test/org/apache/zookeeper/server/TruncateCorruptionTest.java +++ /dev/null @@ -1,323 +0,0 @@ -/** - * 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.server; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZKTestCase; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooKeeper.States; -import org.apache.zookeeper.client.FourLetterWordMain; -import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; -import org.apache.zookeeper.server.util.PortForwarder; -import org.apache.zookeeper.test.ClientBase; -import org.junit.Assert; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Verify ZOOKEEPER-1489 - cause truncation followed by continued append to the - * snaplog, verify that the newly appended information (after the truncation) is - * readable. - */ -public class TruncateCorruptionTest extends ZKTestCase { - - private static final Logger LOG = LoggerFactory - .getLogger(TruncateCorruptionTest.class); - - public interface Check { - boolean doCheck(); - } - - public static boolean await(Check check, long timeoutMillis) - throws InterruptedException { - long end = System.currentTimeMillis() + timeoutMillis; - while (end > System.currentTimeMillis()) { - if (check.doCheck()) { - LOG.debug("await succeeded after " - + (System.currentTimeMillis() - end + timeoutMillis)); - return true; - } - Thread.sleep(50); - } - LOG.debug("await failed in {}", timeoutMillis); - return false; - } - - @Test - public void testTransactionLogCorruption() throws Exception { - // configure the ports for that test in a way so that we can disrupt the - // connection for wrapper1 - ZookeeperServerWrapper wrapper1 = new ZookeeperServerWrapper(1, 7000); - ZookeeperServerWrapper wrapper2 = new ZookeeperServerWrapper(2, 8000); - ZookeeperServerWrapper wrapper3 = new ZookeeperServerWrapper(3, 8000); - - wrapper2.start(); - wrapper3.start(); - - try { - wrapper2.await(ClientBase.CONNECTION_TIMEOUT); - wrapper3.await(ClientBase.CONNECTION_TIMEOUT); - } catch (Exception e) { - ClientBase.logAllStackTraces(); - throw e; - } - List pfs = startForwarding(); - Thread.sleep(1000); - wrapper1.start(); - wrapper1.await(ClientBase.CONNECTION_TIMEOUT); - - final ZooKeeper zk1 = new ZooKeeper("localhost:8201", - ClientBase.CONNECTION_TIMEOUT, new ZkWatcher("zk1")); - waitForConnection(zk1); - zk1.create("/test", "testdata".getBytes(), Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - - // wait a little until stuff is synced in between servers - Thread.sleep(1000); - wrapper2.stop(); - // wait for reconnect - waitForConnection(zk1); - zk1.create("/test2", "testdata".getBytes(), Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - // now we stop them to force a situation where a TRUNC event is sent to - // the followers - wrapper3.stop(); - // simulate a short interruption in network in between - - stopForwarding(pfs); - LOG.info("interrupted network connection ... waiting for zk1 and zk2 to realize"); - - Assert.assertTrue(await(new Check() { - - public boolean doCheck() { - if (zk1.getState() == States.CONNECTING) { - List children; - try { - children = zk1.getChildren("/", false); - - return children.size() != 0; - } catch (KeeperException.ConnectionLossException e) { - // just to be sure - return true; - } catch (Exception e) { - // silently fail - } - } - return false; - } - }, TimeUnit.MINUTES.toMillis(2))); - - // let's clean the data dir of zk3 so that an ensemble of 2 and 3 is - // less advanced than 1 (just to force an event where we get a TRUNCATE - // message) - wrapper3.clean(); - wrapper2.start(); - wrapper3.start(); - LOG.info("Waiting for zk2 and zk3 to form a quorum"); - - wrapper2.await(ClientBase.CONNECTION_TIMEOUT); - wrapper3.await(ClientBase.CONNECTION_TIMEOUT); - ZooKeeper zk2 = new ZooKeeper("localhost:8202", - ClientBase.CONNECTION_TIMEOUT, new ZkWatcher("zk2")); - waitForConnection(zk2); - - LOG.info("re-establishing network connection and waiting for zk1 to reconnect"); - pfs = startForwarding(); - waitForConnection(zk1); - - // create more data ... - LOG.info("Creating node test3"); - zk1.create("/test3", "testdata".getBytes(), Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - Thread.sleep(250); - LOG.info("List of children at zk2 before zk1 became master"); - List children2 = zk2.getChildren("/", false); - LOG.info(children2.toString()); - - LOG.info("List of children at zk1 before zk1 became master"); - List children1 = zk1.getChildren("/", false); - LOG.info(children1.toString()); - - // now cause zk1 to become master and test3 will be lost - LOG.info("restarting zk2 and zk3 while cleaning zk3 to enforce zk1 to become master"); - wrapper2.stop(); - wrapper3.stop(); - wrapper3.clean(); - wrapper3.start(); - wrapper3.await(TimeUnit.MINUTES.toMillis(2)); - ZooKeeper zk3 = new ZooKeeper("localhost:8203", - ClientBase.CONNECTION_TIMEOUT, new ZkWatcher("zk3")); - waitForConnection(zk3); - LOG.info("Zk1 and zk3 have a quorum, now starting zk2"); - wrapper2.start(); - waitForConnection(zk2); - LOG.info("List of children at zk2"); - children2 = zk2.getChildren("/", false); - LOG.info(children2.toString()); - - waitForConnection(zk1); - LOG.info("List of children at zk1"); - children1 = zk1.getChildren("/", false); - Assert.assertTrue("test3 node is missing on zk1", - children1.contains("test3")); - Assert.assertTrue("test3 node is missing on zk2", - children2.contains("test3")); - Assert.assertEquals(children1, children2); - stopForwarding(pfs); - } - - /** - * @param pfs - * @throws Exception - */ - private void stopForwarding(List pfs) throws Exception { - for (PortForwarder pf : pfs) { - pf.shutdown(); - } - } - - /** - * @return - * @throws IOException - */ - private List startForwarding() throws IOException { - List res = new ArrayList(); - res.add(new PortForwarder(8301, 7301)); - res.add(new PortForwarder(8401, 7401)); - res.add(new PortForwarder(7302, 8302)); - res.add(new PortForwarder(7402, 8402)); - res.add(new PortForwarder(7303, 8303)); - res.add(new PortForwarder(7403, 8403)); - return res; - } - - /** - * @param zk - * @throws InterruptedException - */ - private void waitForConnection(final ZooKeeper zk) - throws InterruptedException { - Assert.assertTrue(await(new Check() { - - public boolean doCheck() { - if (zk.getState() == States.CONNECTED) { - List children; - try { - children = zk.getChildren("/", false); - - return children.size() != 0; - } catch (Exception e) { - // silently fail - } - } - return false; - } - }, TimeUnit.MINUTES.toMillis(2))); - } - - static class ZkWatcher implements Watcher { - - private final String clientId; - - ZkWatcher(String clientId) { - this.clientId = clientId; - } - - public void process(WatchedEvent event) { - LOG.info("<<>> " + clientId + " - WatchedEvent: " - + event); - } - } - - public static class ZookeeperServerWrapper { - - private static final Logger LOG = LoggerFactory - .getLogger(ZookeeperServerWrapper.class); - - private final MainThread server; - private final int clientPort; - - public ZookeeperServerWrapper(int serverId, int portBase) - throws IOException { - clientPort = 8200 + serverId; - - // start client port on 8200 + serverId - // start servers on portbase + 300 or + 400 (+serverId) - String quorumCfgSection = "server.1=127.0.0.1:" + (portBase + 301) - + ":" + (portBase + 401) - + "\nserver.2=127.0.0.1:" + (portBase + 302) + ":" - + (portBase + 402) - + "\nserver.3=127.0.0.1:" + (portBase + 303) + ":" - + (portBase + 403); - - server = new MainThread(serverId, clientPort, quorumCfgSection); - } - - public void start() throws Exception { - server.start(); - } - - public void await(long timeout) throws Exception { - long deadline = System.currentTimeMillis() + timeout; - String result = "?"; - while (deadline > System.currentTimeMillis()) { - try { - result = FourLetterWordMain.send4LetterWord("127.0.0.1", - clientPort, "stat"); - if (result.startsWith("Zookeeper version:")) { - LOG.info("Started zookeeper server on port " - + clientPort); - return; - } - } catch (IOException e) { - // ignore as this is expected - } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // ignore - } - } - LOG.info(result); - throw new Exception("Failed to connect to zookeeper server"); - } - - public void stop() { - try { - server.shutdown(); - } catch (InterruptedException e) { - LOG.info("Interrupted while shutting down"); - } - } - - public void clean() throws IOException { - server.clean(); - } - } -} diff --git a/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java b/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java index 2974ef30bee..96659424cfa 100644 --- a/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java +++ b/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java @@ -36,6 +36,55 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * A utility that does bi-directional forwarding between two ports. + * Useful, for example, to simulate network failures. + * Example: + * + * Server 1 config file: + * + * server.1=127.0.0.1:7301:7401;8201 + * server.2=127.0.0.1:7302:7402;8202 + * server.3=127.0.0.1:7303:7403;8203 + * + * Server 2 and 3 config files: + * + * server.1=127.0.0.1:8301:8401;8201 + * server.2=127.0.0.1:8302:8402;8202 + * server.3=127.0.0.1:8303:8403;8203 + * + * Initially forward traffic between 730x and 830x and between 740x and 830x + * This way server 1 can communicate with servers 2 and 3 + * .... + * + * List pfs = startForwarding(); + * .... + * // simulate a network interruption for server 1 + * stopForwarding(pfs); + * .... + * // restore connection + * pfs = startForwarding(); + * + * + * private List startForwarding() throws IOException { + * List res = new ArrayList(); + * res.add(new PortForwarder(8301, 7301)); + * res.add(new PortForwarder(8401, 7401)); + * res.add(new PortForwarder(7302, 8302)); + * res.add(new PortForwarder(7402, 8402)); + * res.add(new PortForwarder(7303, 8303)); + * res.add(new PortForwarder(7403, 8403)); + * return res; + * } + * + * private void stopForwarding(List pfs) throws Exception { + * for (PortForwarder pf : pfs) { + * pf.shutdown(); + * } + * } + * + * + */ public class PortForwarder extends Thread { private static final Logger LOG = LoggerFactory .getLogger(PortForwarder.class); diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index bb1fdf8a33b..14a793c1eee 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -28,6 +28,7 @@ import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; @@ -54,7 +55,7 @@ public class TruncateTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(TruncateTest.class); File dataDir1, dataDir2, dataDir3; - final int baseHostPort = 12233; + final int baseHostPort = PortAssignment.unique(); @Before public void setUp() throws IOException { From 59d34fd3f85c4453dae93eb5c10b8dd24a388e25 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Mon, 15 Jul 2013 01:28:22 +0000 Subject: [PATCH 157/444] ZOOKEEPER-1629. testTransactionLogCorruption occasionally fails. (shralex via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1503100 13f79535-47bb-0310-9956-ffa450edef68 --- .../zookeeper/server/util/PortForwarder.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java b/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java index 96659424cfa..acbad809d1f 100644 --- a/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java +++ b/src/java/test/org/apache/zookeeper/server/util/PortForwarder.java @@ -211,24 +211,26 @@ public void run() { sock.getInputStream(), target.getOutputStream())); this.workers.execute(new PortForwardWorker(target, sock, target.getInputStream(), sock.getOutputStream())); - } catch (SocketTimeoutException e) { - LOG.warn("socket timed out local:" + sock.getLocalPort() - + " from:" + sock.getPort() + } catch (SocketTimeoutException e) { + LOG.warn("socket timed out local:" + + (sock != null ? sock.getLocalPort(): "") + + " from:" + (sock != null ? sock.getPort(): "") + " to:" + to, e); } catch (ConnectException e) { - LOG.warn("connection exception local:" + sock.getLocalPort() - + " from:" + sock.getPort() + LOG.warn("connection exception local:" + + (sock != null ? sock.getLocalPort(): "") + + " from:" + (sock != null ? sock.getPort(): "") + " to:" + to, e); sock.close(); } catch (IOException e) { if (!"Socket closed".equals(e.getMessage())) { - LOG.warn("unexpected exception local:" + sock.getLocalPort() - + " from:" + sock.getPort() - + " to:" + to, e); + LOG.warn("unexpected exception local:" + + (sock != null ? sock.getLocalPort(): "") + + " from:" + (sock != null ? sock.getPort(): "") + + " to:" + to, e); throw e; } } - } } catch (IOException e) { LOG.error("Unexpected exception to:" + to, e); From c3809a4187275f14790b1f84808389d55f01ec80 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Fri, 2 Aug 2013 17:07:06 +0000 Subject: [PATCH 158/444] ZOOKEEPER-1731. Unsynchronized access to ServerCnxnFactory.connectionBeans results in deadlock. (Dave Latham via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1509780 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/server/ServerCnxnFactory.java | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1d64062b1f6..80cc2042e14 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -81,6 +81,9 @@ BUGFIXES: ZOOKEEPER-1629. testTransactionLogCorruption occasionally fails. (shralex via camille) + ZOOKEEPER-1731. Unsynchronized access to ServerCnxnFactory.connectionBeans results in + deadlock. (Dave Latham via camille) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java index e5c65652513..9b7fd873837 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java @@ -21,8 +21,9 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; -import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; @@ -128,7 +129,8 @@ static public ServerCnxnFactory createFactory(InetSocketAddress addr, public abstract InetSocketAddress getLocalAddress(); - private final HashMap connectionBeans = new HashMap(); + private final Map connectionBeans + = new ConcurrentHashMap(); protected final HashSet cnxns = new HashSet(); public void unregisterConnection(ServerCnxn serverCnxn) { From 89fe3cda9b40fc457afecec313661e35e28cde16 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Mon, 2 Sep 2013 20:19:26 +0000 Subject: [PATCH 159/444] ZOOKEEPER-1713. wrong time calculation in zkfuse.cc (german via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1519510 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/contrib/zkfuse/src/zkfuse.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 80cc2042e14..1d18404d246 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -83,6 +83,8 @@ BUGFIXES: ZOOKEEPER-1731. Unsynchronized access to ServerCnxnFactory.connectionBeans results in deadlock. (Dave Latham via camille) + + ZOOKEEPER-1713. wrong time calculation in zkfuse.cc (german via fpj) IMPROVEMENTS: diff --git a/src/contrib/zkfuse/src/zkfuse.cc b/src/contrib/zkfuse/src/zkfuse.cc index bcf662bd582..6a8216885fa 100644 --- a/src/contrib/zkfuse/src/zkfuse.cc +++ b/src/contrib/zkfuse/src/zkfuse.cc @@ -81,7 +81,7 @@ uint64_t secsToMillisecs(uint64_t secs) inline uint64_t nanosecsToMillisecs(uint64_t nanosecs) { - return nanosecs * 1000000; + return nanosecs / 1000000; } inline uint64_t timespecToMillisecs(const struct timespec & ts) From fb01942d1ffa59e64bbc874abb00b9494a7d42ad Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Mon, 2 Sep 2013 21:47:42 +0000 Subject: [PATCH 160/444] ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1519521 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../org/apache/zookeeper/ZooKeeperMain.java | 2 +- .../org/apache/zookeeper/ZooKeeperTest.java | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1d18404d246..5a8bbc81971 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -85,6 +85,8 @@ BUGFIXES: deadlock. (Dave Latham via camille) ZOOKEEPER-1713. wrong time calculation in zkfuse.cc (german via fpj) + + ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index 3a96b82b2d6..b1b4c27814d 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -810,7 +810,7 @@ protected boolean processZKCmd(MyCommandOptions co) b = args[2].getBytes(); zk.addAuthInfo(args[1], b); - } else { + } else if (!commandMap.containsKey(cmd)) { usage(); } return watch; diff --git a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java index 6d81d09050d..b438e30249a 100644 --- a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java +++ b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java @@ -19,7 +19,9 @@ import static org.junit.Assert.*; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -178,4 +180,25 @@ public void testDeleteWithInvalidVersionNo() throws Exception { } } + @Test + public void testCliCommandsNotEchoingUsage() throws Exception { + // setup redirect out/err streams to get System.in/err, use this judiciously! + final PrintStream systemErr = System.err; // get current err + final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + System.setErr(new PrintStream(errContent)); + final ZooKeeper zk = createClient(); + ZooKeeperMain zkMain = new ZooKeeperMain(zk); + String cmd1 = "printwatches"; + zkMain.executeLine(cmd1); + String cmd2 = "history"; + zkMain.executeLine(cmd2); + String cmd3 = "redo"; + zkMain.executeLine(cmd3); + // revert redirect of out/err streams - important step! + System.setErr(systemErr); + if (errContent.toString().contains("ZooKeeper -server host:port cmd args")) { + fail("CLI commands (history, redo, connect, printwatches) display usage info!"); + } + } + } From 32593f56c1c084a20f3a76a39b70555607ea8982 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Tue, 3 Sep 2013 11:20:03 +0000 Subject: [PATCH 161/444] ZOOKEEPER-1670: zookeeper should set a default value for SERVER_JVMFLAGS and CLIENT_JVMFLAGS so that memory usage is controlled (Arpit Gupta via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1519650 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ bin/zkEnv.sh | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 5a8bbc81971..009f5cbdcf3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -87,6 +87,8 @@ BUGFIXES: ZOOKEEPER-1713. wrong time calculation in zkfuse.cc (german via fpj) ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj) + + ZOOKEEPER-1670: zookeeper should set a default value for SERVER_JVMFLAGS and CLIENT_JVMFLAGS so that memory usage is controlled (Arpit Gupta via fpj) IMPROVEMENTS: diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh index e6e90dfe8c5..9be6984e1e4 100755 --- a/bin/zkEnv.sh +++ b/bin/zkEnv.sh @@ -113,3 +113,11 @@ then fi #echo "CLASSPATH=$CLASSPATH" + +# default heap for zookeeper server +ZK_SERVER_HEAP="${ZK_SERVER_HEAP:-1000}" +export SERVER_JVMFLAGS="-Xmx${ZK_SERVER_HEAP}m $SERVER_JVMFLAGS" + +# default heap for zookeeper client +ZK_CLIENT_HEAP="${ZK_CLIENT_HEAP:-256}" +export CLIENT_JVMFLAGS="-Xmx${ZK_CLIENT_HEAP}m $CLIENT_JVMFLAGS" From 33550d6ce719b2cedfd0fa8650c4311dd9077d97 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 5 Sep 2013 20:46:44 +0000 Subject: [PATCH 162/444] ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1520418 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../org/apache/zookeeper/common/PathTrie.java | 9 +++++ .../org/apache/zookeeper/server/DataTree.java | 1 + .../apache/zookeeper/test/DataTreeTest.java | 39 +++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 009f5cbdcf3..a5bd4da217c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -89,6 +89,8 @@ BUGFIXES: ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj) ZOOKEEPER-1670: zookeeper should set a default value for SERVER_JVMFLAGS and CLIENT_JVMFLAGS so that memory usage is controlled (Arpit Gupta via fpj) + + ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/common/PathTrie.java b/src/java/main/org/apache/zookeeper/common/PathTrie.java index 2857592ec67..73053e02cd1 100644 --- a/src/java/main/org/apache/zookeeper/common/PathTrie.java +++ b/src/java/main/org/apache/zookeeper/common/PathTrie.java @@ -281,4 +281,13 @@ public String findMaxPrefix(String path) { } return sb.toString(); } + + /** + * clear all nodes + */ + public void clear() { + for(String child : rootNode.getChildren()) { + rootNode.deleteChild(child); + } + } } diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index 59cfc1bc1dd..babd02491aa 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -1187,6 +1187,7 @@ public void serialize(OutputArchive oa, String tag) throws IOException { public void deserialize(InputArchive ia, String tag) throws IOException { deserializeList(longKeyMap, ia); nodes.clear(); + pTrie.clear(); String path = ia.readString("path"); while (!path.equals("/")) { DataNode node = new DataNode(); diff --git a/src/java/test/org/apache/zookeeper/test/DataTreeTest.java b/src/java/test/org/apache/zookeeper/test/DataTreeTest.java index 74c8a5e07cd..ef01c10b5bb 100644 --- a/src/java/test/org/apache/zookeeper/test/DataTreeTest.java +++ b/src/java/test/org/apache/zookeeper/test/DataTreeTest.java @@ -30,6 +30,14 @@ import org.junit.Before; import org.junit.Test; import org.apache.zookeeper.server.DataNode; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import org.apache.zookeeper.Quotas; +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.common.PathTrie; +import java.lang.reflect.*; public class DataTreeTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(DataTreeTest.class); @@ -80,4 +88,35 @@ public void testIncrementCversion() throws Exception { newCversion + ", " + newPzxid + ">", (newCversion == prevCversion + 1 && newPzxid == prevPzxid + 1)); } + + @Test + public void testPathTrieClearOnDeserialize() throws Exception { + + //Create a DataTree with quota nodes so PathTrie get updated + DataTree dserTree = new DataTree(); + + dserTree.createNode("/bug", new byte[20], null, -1, 1, 1, 1); + dserTree.createNode(Quotas.quotaZookeeper+"/bug", null, null, -1, 1, 1, 1); + dserTree.createNode(Quotas.quotaPath("/bug"), new byte[20], null, -1, 1, 1, 1); + dserTree.createNode(Quotas.statPath("/bug"), new byte[20], null, -1, 1, 1, 1); + + //deserialize a DataTree; this should clear the old /bug nodes and pathTrie + DataTree tree = new DataTree(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); + tree.serialize(oa, "test"); + baos.flush(); + + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + BinaryInputArchive ia = BinaryInputArchive.getArchive(bais); + dserTree.deserialize(ia, "test"); + + Field pfield = DataTree.class.getDeclaredField("pTrie"); + pfield.setAccessible(true); + PathTrie pTrie = (PathTrie)pfield.get(dserTree); + + //Check that the node path is removed from pTrie + Assert.assertEquals("/bug is still in pTrie", "", pTrie.findMaxPrefix("/bug")); + } } From 579c7a4736ee03183e6cba25d7d20dedb6f892dc Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Wed, 11 Sep 2013 20:42:24 +0000 Subject: [PATCH 163/444] ZOOKEEPER-1664. Kerberos auth doesn't work with native platform GSS integration. (Boaz Kelmer via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1522028 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +- .../zookeeper/client/ZooKeeperSaslClient.java | 61 ++++++++++++++----- .../zookeeper/server/ZooKeeperSaslServer.java | 55 ++++++++++++++--- 3 files changed, 96 insertions(+), 23 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a5bd4da217c..06bba213dbc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -91,7 +91,8 @@ BUGFIXES: ZOOKEEPER-1670: zookeeper should set a default value for SERVER_JVMFLAGS and CLIENT_JVMFLAGS so that memory usage is controlled (Arpit Gupta via fpj) ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) - + + ZOOKEEPER-1664. Kerberos auth doesn't work with native platform GSS integration. (Boaz Kelmer via camille) IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 6087e99b2d3..434fb8d52d0 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -18,23 +18,11 @@ package org.apache.zookeeper.client; -import org.apache.zookeeper.AsyncCallback; -import org.apache.zookeeper.ClientCnxn; -import org.apache.zookeeper.Login; -import org.apache.zookeeper.Watcher.Event.KeeperState; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.Environment; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.proto.GetSASLRequest; -import org.apache.zookeeper.proto.SetSASLResponse; -import org.apache.zookeeper.server.auth.KerberosName; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; + import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; @@ -50,6 +38,24 @@ import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; +import org.apache.zookeeper.AsyncCallback; +import org.apache.zookeeper.ClientCnxn; +import org.apache.zookeeper.Environment; +import org.apache.zookeeper.Login; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.proto.GetSASLRequest; +import org.apache.zookeeper.proto.SetSASLResponse; +import org.apache.zookeeper.server.auth.KerberosName; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.Oid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * This class manages SASL authentication for the client. It * allows ClientCnxn to authenticate using SASL with a Zookeeper server. @@ -213,6 +219,33 @@ synchronized private SaslClient createSaslClient(final String servicePrincipal, return saslClient; } else { // GSSAPI. + boolean usingNativeJgss = + Boolean.getBoolean("sun.security.jgss.native"); + if (usingNativeJgss) { + // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + // """ + // In addition, when performing operations as a particular + // Subject, e.g. Subject.doAs(...) or Subject.doAsPrivileged(...), + // the to-be-used GSSCredential should be added to Subject's + // private credential set. Otherwise, the GSS operations will + // fail since no credential is found. + // """ + try { + GSSManager manager = GSSManager.getInstance(); + Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); + GSSCredential cred = manager.createCredential(null, + GSSContext.DEFAULT_LIFETIME, + krb5Mechanism, + GSSCredential.INITIATE_ONLY); + subject.getPrivateCredentials().add(cred); + if (LOG.isDebugEnabled()) { + LOG.debug("Added private credential to subject: " + cred); + } + } catch (GSSException ex) { + LOG.warn("Cannot add private credential to subject; " + + "authentication at the server may fail", ex); + } + } final Object[] principals = subject.getPrincipals().toArray(); // determine client principal from subject. final Principal clientPrincipal = (Principal)principals[0]; @@ -237,7 +270,7 @@ public SaslClient run() throws SaslException { return saslClient; } catch (Exception e) { - LOG.error("Error creating SASL client:" + e); + LOG.error("Exception while trying to create SASL client", e); e.printStackTrace(); return null; } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java index e3221a3aacd..71870ce1c18 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java @@ -21,13 +21,21 @@ import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + import javax.security.auth.Subject; import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; + import org.apache.zookeeper.Login; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ZooKeeperSaslServer { public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.serverconfig"; @@ -68,9 +76,40 @@ private SaslServer createSaslServer(final Login login) { final String mech = "GSSAPI"; // TODO: should depend on zoo.cfg specified mechs, but if subject is non-null, it can be assumed to be GSSAPI. LOG.debug("serviceHostname is '"+ serviceHostname + "'"); - LOG.debug("servicePrincipalName is "+ servicePrincipalName + "'"); - LOG.debug("SASL mechanism(mech) is "+ mech +"'"); - + LOG.debug("servicePrincipalName is '"+ servicePrincipalName + "'"); + LOG.debug("SASL mechanism(mech) is '"+ mech +"'"); + + boolean usingNativeJgss = + Boolean.getBoolean("sun.security.jgss.native"); + if (usingNativeJgss) { + // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + // """ + // In addition, when performing operations as a particular + // Subject, e.g. Subject.doAs(...) or + // Subject.doAsPrivileged(...), the to-be-used + // GSSCredential should be added to Subject's + // private credential set. Otherwise, the GSS operations + // will fail since no credential is found. + // """ + try { + GSSManager manager = GSSManager.getInstance(); + Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); + GSSName gssName = manager.createName( + servicePrincipalName + "@" + serviceHostname, + GSSName.NT_HOSTBASED_SERVICE); + GSSCredential cred = manager.createCredential(gssName, + GSSContext.DEFAULT_LIFETIME, + krb5Mechanism, + GSSCredential.ACCEPT_ONLY); + subject.getPrivateCredentials().add(cred); + if (LOG.isDebugEnabled()) { + LOG.debug("Added private credential to subject: " + cred); + } + } catch (GSSException ex) { + LOG.warn("Cannot add private credential to subject; " + + "clients authentication may fail", ex); + } + } try { return Subject.doAs(subject,new PrivilegedExceptionAction() { public SaslServer run() { @@ -94,8 +133,8 @@ public SaslServer run() { e.printStackTrace(); } } - catch (Exception e) { - LOG.error("server principal name/hostname determination error: " + e); + catch (IndexOutOfBoundsException e) { + LOG.error("server principal name/hostname determination error: ", e); } } else { @@ -106,7 +145,7 @@ public SaslServer run() { return saslServer; } catch (SaslException e) { - LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation: " + e); + LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e); } } } From a69b56620a12ab4be2147dab6b45a92c85bca1f2 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 12 Sep 2013 17:26:40 +0000 Subject: [PATCH 164/444] ZOOKEEPER-1750. Race condition producing NPE in NIOServerCnxn.toString (Rakesh R via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1522673 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++ .../zookeeper/server/NIOServerCnxn.java | 11 ++- .../zookeeper/server/NIOServerCnxnTest.java | 71 +++++++++++++++++++ 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 06bba213dbc..85eb3d23c6d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -93,6 +93,10 @@ BUGFIXES: ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) ZOOKEEPER-1664. Kerberos auth doesn't work with native platform GSS integration. (Boaz Kelmer via camille) + + ZOOKEEPER-1750. Race condition producing NPE in NIOServerCnxn.toString + (Rakesh R via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index eade1d61ab5..b0cad0642d7 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -60,7 +60,7 @@ public class NIOServerCnxn extends ServerCnxn { NIOServerCnxnFactory factory; - SocketChannel sock; + final SocketChannel sock; private final SelectionKey sk; @@ -132,7 +132,7 @@ void sendBufferSync(ByteBuffer bb) { */ sock.configureBlocking(true); if (bb != ServerCnxnFactory.closeConn) { - if (sock != null) { + if (sock.isOpen()) { sock.write(bb); } packetSent(); @@ -206,7 +206,7 @@ private void readPayload() throws IOException, InterruptedException { void doIO(SelectionKey k) throws InterruptedException { try { - if (sock == null) { + if (sock.isOpen() == false) { LOG.warn("trying to do i/o on a null socket for session:0x" + Long.toHexString(sessionId)); @@ -990,7 +990,7 @@ public void close() { * Close resources associated with the sock of this cnxn. */ private void closeSock() { - if (sock == null) { + if (sock.isOpen() == false) { return; } @@ -1039,7 +1039,6 @@ private void closeSock() { LOG.debug("ignoring exception during socketchannel close", e); } } - sock = null; } private final static byte fourBytes[] = new byte[4]; @@ -1136,7 +1135,7 @@ public int getInterestOps() { @Override public InetSocketAddress getRemoteSocketAddress() { - if (sock == null) { + if (sock.isOpen() == false) { return null; } return (InetSocketAddress) sock.socket().getRemoteSocketAddress(); diff --git a/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java b/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java new file mode 100644 index 00000000000..5c94ed70ebe --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java @@ -0,0 +1,71 @@ +/** + * 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.server; + +import java.io.IOException; + +import junit.framework.Assert; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.test.ClientBase; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NIOServerCnxnTest extends ClientBase { + private static final Logger LOG = LoggerFactory + .getLogger(NIOServerCnxnTest.class); + /** + * Test operations on ServerCnxn after socket closure. + */ + @Test(timeout = 60000) + public void testOperationsAfterCnxnClose() throws IOException, + InterruptedException, KeeperException { + final ZooKeeper zk = createClient(); + + final String path = "/a"; + try { + // make sure zkclient works + zk.create(path, "test".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Assert.assertNotNull("Didn't create znode:" + path, + zk.exists(path, false)); + // Defaults ServerCnxnFactory would be instantiated with + // NIOServerCnxnFactory + Assert.assertTrue( + "Didn't instantiate ServerCnxnFactory with NIOServerCnxnFactory!", + serverFactory instanceof NIOServerCnxnFactory); + Iterable connections = serverFactory.getConnections(); + for (ServerCnxn serverCnxn : connections) { + serverCnxn.close(); + try { + serverCnxn.toString(); + } catch (Exception e) { + LOG.error("Exception while getting connection details!", e); + Assert.fail("Shouldn't throw exception while " + + "getting connection details!"); + } + } + } finally { + zk.close(); + } + } +} From 2ad419efc40456c3aa25b1cba0624c39d0b49c7f Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Tue, 17 Sep 2013 22:44:54 +0000 Subject: [PATCH 165/444] ZOOKEEPER-1754. Read-only server allows to create znode (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1524247 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../quorum/ReadOnlyRequestProcessor.java | 4 +- .../zookeeper/test/ReadOnlyModeTest.java | 51 +++++++++++++++++-- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 85eb3d23c6d..f4b1525b19d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -97,6 +97,8 @@ BUGFIXES: ZOOKEEPER-1750. Race condition producing NPE in NIOServerCnxn.toString (Rakesh R via michim) + ZOOKEEPER-1754. Read-only server allows to create znode (Rakesh R via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java index 5545ba5385a..14c09988fc5 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java @@ -28,7 +28,6 @@ import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooTrace; -import org.apache.zookeeper.server.RequestProcessor.RequestProcessorException; import org.apache.zookeeper.server.quorum.Leader.XidRolloverException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,7 +80,8 @@ public void run() { case OpCode.delete: case OpCode.setData: case OpCode.setACL: - + case OpCode.multi: + case OpCode.check: ReplyHeader hdr = new ReplyHeader(request.cxid, zks.getZKDatabase() .getDataTreeLastProcessedZxid(), Code.NOTREADONLY.intValue()); try { diff --git a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java index c705a7bba56..3b7a149ab53 100644 --- a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java +++ b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java @@ -34,6 +34,7 @@ import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.NotReadOnlyException; +import org.apache.zookeeper.Transaction; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; @@ -62,11 +63,53 @@ public void tearDown() throws Exception { qu.tearDown(); } + /** + * Test write operations using multi request. + */ + @Test(timeout = 90000) + public void testMultiTransaction() throws Exception { + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, + watcher, true); + watcher.waitForConnected(CONNECTION_TIMEOUT); // ensure zk got connected + + final String data = "Data to be read in RO mode"; + final String node1 = "/tnode1"; + final String node2 = "/tnode2"; + zk.create(node1, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + watcher.reset(); + qu.shutdown(2); + watcher.waitForConnected(CONNECTION_TIMEOUT); + Assert.assertEquals("Should be in r-o mode", States.CONNECTEDREADONLY, + zk.getState()); + + // read operation during r/o mode + String remoteData = new String(zk.getData(node1, false, null)); + Assert.assertEquals("Failed to read data in r-o mode", data, remoteData); + + try { + Transaction transaction = zk.transaction(); + transaction.setData(node1, "no way".getBytes(), -1); + transaction.create(node2, data.getBytes(), + ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + transaction.commit(); + Assert.fail("Write operation using multi-transaction" + + " api has succeeded during RO mode"); + } catch (NotReadOnlyException e) { + // ok + } + + Assert.assertNull("Should have created the znode:" + node2, + zk.exists(node2, false)); + } + /** * Basic test of read-only client functionality. Tries to read and write * during read-only mode, then regains a quorum and tries to write again. */ - @Test + @Test(timeout = 90000) public void testReadOnlyClient() throws Exception { CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, @@ -107,7 +150,7 @@ public void testReadOnlyClient() throws Exception { * Ensures that upon connection to a read-only server client receives * ConnectedReadOnly state notification. */ - @Test + @Test(timeout = 90000) public void testConnectionEvents() throws Exception { final List states = new ArrayList(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, @@ -153,7 +196,7 @@ public void process(WatchedEvent event) { * then connects to a majority server. Transition should be transparent for * the user. */ - @Test + @Test(timeout = 90000) public void testSessionEstablishment() throws Exception { qu.shutdown(2); @@ -183,7 +226,7 @@ public void testSessionEstablishment() throws Exception { * server. */ @SuppressWarnings("deprecation") - @Test + @Test(timeout = 90000) public void testSeekForRwServer() throws Exception { // setup the logger to capture all logs From 973665c48a176227a681998587f1bd14f0ea9578 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Wed, 18 Sep 2013 02:06:28 +0000 Subject: [PATCH 166/444] ZOOKEEPER-1751. ClientCnxn#run could miss the second ping or connection get dropped before a ping. (Jeffrey Zhong via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1524274 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/ClientCnxn.java | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f4b1525b19d..9297e9aaf03 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -99,6 +99,9 @@ BUGFIXES: ZOOKEEPER-1754. Read-only server allows to create znode (Rakesh R via fpj) + ZOOKEEPER-1751. ClientCnxn#run could miss the second ping or connection get + dropped before a ping. (Jeffrey Zhong via mahadev) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 27547b4b52b..43da699709a 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -976,6 +976,7 @@ public void run() { clientCnxnSocket.updateLastSendAndHeard(); int to; long lastPingRwServer = System.currentTimeMillis(); + final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds while (state.isAlive()) { try { if (!clientCnxnSocket.isConnected()) { @@ -1039,9 +1040,12 @@ public void run() { + Long.toHexString(sessionId)); } if (state.isConnected()) { - int timeToNextPing = readTimeout / 2 - - clientCnxnSocket.getIdleSend(); - if (timeToNextPing <= 0) { + //1000(1 second) is to prevent race condition missing to send the second ping + //also make sure not to send too many pings when readTimeout is small + int timeToNextPing = readTimeout / 2 - clientCnxnSocket.getIdleSend() - + ((clientCnxnSocket.getIdleSend() > 1000) ? 1000 : 0); + //send a ping request either time is due or no packet sent out within MAX_SEND_PING_INTERVAL + if (timeToNextPing <= 0 || clientCnxnSocket.getIdleSend() > MAX_SEND_PING_INTERVAL) { sendPing(); clientCnxnSocket.updateLastSend(); } else { From a8df46db76ee725411ff4774debf8a6352d12c6a Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 18 Sep 2013 10:24:00 +0000 Subject: [PATCH 167/444] ZOOKEEPER-1657. Increased CPU usage by unnecessary SASL checks (Philip K. Warren via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1524355 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../main/org/apache/zookeeper/ClientCnxn.java | 39 +++++++----- .../zookeeper/client/ZooKeeperSaslClient.java | 15 +++++ .../apache/zookeeper/test/SaslClientTest.java | 62 +++++++++++++++++++ 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/SaslClientTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 9297e9aaf03..fd7516750d3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -102,6 +102,8 @@ BUGFIXES: ZOOKEEPER-1751. ClientCnxn#run could miss the second ping or connection get dropped before a ping. (Jeffrey Zhong via mahadev) + ZOOKEEPER-1657. Increased CPU usage by unnecessary SASL checks (Philip K. Warren via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 43da699709a..c29b2c816b7 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -939,19 +939,21 @@ private void startConnect() throws IOException { setName(getName().replaceAll("\\(.*\\)", "(" + addr.getHostName() + ":" + addr.getPort() + ")")); - try { - zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); - } catch (LoginException e) { - // An authentication error occurred when the SASL client tried to initialize: - // for Kerberos this means that the client failed to authenticate with the KDC. - // This is different from an authentication error that occurs during communication - // with the Zookeeper server, which is handled below. - LOG.warn("SASL configuration failed: " + e + " Will continue connection to Zookeeper server without " - + "SASL authentication, if Zookeeper server allows it."); - eventThread.queueEvent(new WatchedEvent( - Watcher.Event.EventType.None, - Watcher.Event.KeeperState.AuthFailed, null)); - saslLoginFailed = true; + if (ZooKeeperSaslClient.isEnabled()) { + try { + zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); + } catch (LoginException e) { + // An authentication error occurred when the SASL client tried to initialize: + // for Kerberos this means that the client failed to authenticate with the KDC. + // This is different from an authentication error that occurs during communication + // with the Zookeeper server, which is handled below. + LOG.warn("SASL configuration failed: " + e + " Will continue connection to Zookeeper server without " + + "SASL authentication, if Zookeeper server allows it."); + eventThread.queueEvent(new WatchedEvent( + Watcher.Event.EventType.None, + Watcher.Event.KeeperState.AuthFailed, null)); + saslLoginFailed = true; + } } logStartConnect(addr); @@ -1230,18 +1232,23 @@ void testableCloseSocket() throws IOException { } public boolean clientTunneledAuthenticationInProgress() { - // 1. SASL login failed. + // 1. SASL client is disabled. + if (!ZooKeeperSaslClient.isEnabled()) { + return false; + } + + // 2. SASL login failed. if (saslLoginFailed == true) { return false; } - // 2. SendThread has not created the authenticating object yet, + // 3. SendThread has not created the authenticating object yet, // therefore authentication is (at the earliest stage of being) in progress. if (zooKeeperSaslClient == null) { return true; } - // 3. authenticating object exists, so ask it for its progress. + // 4. authenticating object exists, so ask it for its progress. return zooKeeperSaslClient.clientTunneledAuthenticationInProgress(); } diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 434fb8d52d0..d45071ea3ad 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -62,6 +62,21 @@ */ public class ZooKeeperSaslClient { public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.clientconfig"; + public static final String ENABLE_CLIENT_SASL_KEY = "zookeeper.sasl.client"; + public static final String ENABLE_CLIENT_SASL_DEFAULT = "true"; + + /** + * Returns true if the SASL client is enabled. By default, the client + * is enabled but can be disabled by setting the system property + * zookeeper.sasl.client to false. See + * ZOOKEEPER-1657 for more information. + * + * @return If the SASL client is enabled. + */ + public static boolean isEnabled() { + return Boolean.valueOf(System.getProperty(ENABLE_CLIENT_SASL_KEY, ENABLE_CLIENT_SASL_DEFAULT)); + } + private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class); private static Login login = null; private SaslClient saslClient; diff --git a/src/java/test/org/apache/zookeeper/test/SaslClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslClientTest.java new file mode 100644 index 00000000000..8213abca455 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SaslClientTest.java @@ -0,0 +1,62 @@ +/** + * 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.test; + +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + +public class SaslClientTest extends ZKTestCase { + + private String existingPropertyValue = null; + + @Before + public void setUp() { + existingPropertyValue = System.getProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY); + } + + @After + public void tearDown() { + // Restore the System property if it was set previously + if (existingPropertyValue != null) { + System.setProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY, existingPropertyValue); + } + } + + @Test + public void testSaslClientDisabled() { + System.clearProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY); + Assert.assertTrue("SASL client disabled", ZooKeeperSaslClient.isEnabled()); + + for (String value : Arrays.asList("true", "TRUE")) { + System.setProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY, value); + Assert.assertTrue("SASL client disabled", ZooKeeperSaslClient.isEnabled()); + } + + for (String value : Arrays.asList("false", "FALSE")) { + System.setProperty(ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY, value); + Assert.assertFalse("SASL client disabled", ZooKeeperSaslClient.isEnabled()); + } + } +} From d1eff7a27be156a5b4a37bd6aff4da446dd3813e Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 18 Sep 2013 12:33:45 +0000 Subject: [PATCH 168/444] ZOOKEEPER-1753. ClientCnxn is not properly releasing the resources, which are used to ping RwServer (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1524387 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/ClientCnxn.java | 23 +++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index fd7516750d3..ac340b07bf2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -104,6 +104,9 @@ BUGFIXES: ZOOKEEPER-1657. Increased CPU usage by unnecessary SASL checks (Philip K. Warren via fpj) + ZOOKEEPER-1753. ClientCnxn is not properly releasing the resources, + which are used to ping RwServer (Rakesh R via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index c29b2c816b7..eafc0c81dc6 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -1128,25 +1128,40 @@ private void pingRwServer() throws RWServerFoundException { LOG.info("Checking server " + addr + " for being r/w." + " Timeout " + pingRwTimeout); + Socket sock = null; + BufferedReader br = null; try { - Socket sock = new Socket(addr.getHostName(), addr.getPort()); + sock = new Socket(addr.getHostName(), addr.getPort()); sock.setSoLinger(false, -1); sock.setSoTimeout(1000); sock.setTcpNoDelay(true); sock.getOutputStream().write("isro".getBytes()); sock.getOutputStream().flush(); sock.shutdownOutput(); - BufferedReader br = new BufferedReader( + br = new BufferedReader( new InputStreamReader(sock.getInputStream())); result = br.readLine(); - sock.close(); - br.close(); } catch (ConnectException e) { // ignore, this just means server is not up } catch (IOException e) { // some unexpected error, warn about it LOG.warn("Exception while seeking for r/w server " + e.getMessage(), e); + } finally { + if (sock != null) { + try { + sock.close(); + } catch (IOException e) { + LOG.warn("Unexpected exception", e); + } + } + if (br != null) { + try { + br.close(); + } catch (IOException e) { + LOG.warn("Unexpected exception", e); + } + } } if ("rw".equals(result)) { From e3a488b11e4e0e8a124c7387dad98ecd286ee1eb Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 25 Sep 2013 21:44:11 +0000 Subject: [PATCH 169/444] ZOOKEEPER-1096. Leader communication should listen on specified IP, not wildcard address (Jared Cantwell, German Blanco via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1526313 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../content/xdocs/zookeeperAdmin.xml | 14 ++++++++++++ .../zookeeper/server/quorum/Leader.java | 18 +++++++++++---- .../server/quorum/QuorumCnxManager.java | 12 +++++++--- .../zookeeper/server/quorum/QuorumPeer.java | 22 ++++++++++++++++--- .../server/quorum/QuorumPeerConfig.java | 7 ++++++ .../server/quorum/QuorumPeerMain.java | 1 + .../zookeeper/test/LENonTerminateTest.java | 2 +- 8 files changed, 67 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ac340b07bf2..3948fbd1442 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -107,6 +107,8 @@ BUGFIXES: ZOOKEEPER-1753. ClientCnxn is not properly releasing the resources, which are used to ping RwServer (Rakesh R via fpj) + ZOOKEEPER-1096. Leader communication should listen on specified IP, not wildcard address (Jared Cantwell, German Blanco via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 48c1f08908e..abab8f69298 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1109,6 +1109,20 @@ server.3=zoo3:2888:3888 but opens up full access to the data tree to everyone. + + + quorumListenOnAllIPs + + + When set to true the ZooKeeper server will listen + for connections from its peers on all available IP addresses, + and not only the address configured in the server list of the + configuration file. It affects the connections handling the + ZAB protocol and the Fast Leader Election protocol. Default + value is false. + + + diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 04a2f005c2b..b26d7594537 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -39,6 +39,7 @@ import java.util.concurrent.ConcurrentMap; import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; @@ -182,12 +183,21 @@ boolean isLearnerSynced(LearnerHandler peer){ Leader(QuorumPeer self,LeaderZooKeeperServer zk) throws IOException { this.self = self; try { - ss = new ServerSocket(); + if (self.getQuorumListenOnAllIPs()) { + ss = new ServerSocket(self.getQuorumAddress().getPort()); + } else { + ss = new ServerSocket(); + } ss.setReuseAddress(true); - ss.bind(new InetSocketAddress(self.getQuorumAddress().getPort())); + if (!self.getQuorumListenOnAllIPs()) { + ss.bind(self.getQuorumAddress()); + } } catch (BindException e) { - LOG.error("Couldn't bind to port " - + self.getQuorumAddress().getPort(), e); + if (self.getQuorumListenOnAllIPs()) { + LOG.error("Couldn't bind to port " + self.getQuorumAddress().getPort(), e); + } else { + LOG.error("Couldn't bind to " + self.getQuorumAddress(), e); + } throw e; } this.zk=zk; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index f897ce2fa4e..1d47a234563 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -39,6 +39,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.zookeeper.server.ZooKeeperServer; + /** * This class implements a connection manager for leader election using TCP. It * maintains one connection for every pair of servers. The tricky part is to @@ -487,13 +489,17 @@ public class Listener extends Thread { @Override public void run() { int numRetries = 0; + InetSocketAddress addr; while((!shutdown) && (numRetries < 3)){ try { ss = new ServerSocket(); ss.setReuseAddress(true); - int port = self.quorumPeers.get(self.getId()).electionAddr - .getPort(); - InetSocketAddress addr = new InetSocketAddress(port); + if (self.getQuorumListenOnAllIPs()) { + int port = self.quorumPeers.get(self.getId()).electionAddr.getPort(); + addr = new InetSocketAddress(port); + } else { + addr = self.quorumPeers.get(self.getId()).electionAddr; + } LOG.info("My election bind port: " + addr.toString()); setName(self.quorumPeers.get(self.getId()).electionAddr .toString()); 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 22c4aecf8a2..f8813ac0026 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -248,6 +248,12 @@ public synchronized void setCurrentVote(Vote v){ */ protected volatile int tick; + /** + * Whether or not to listen on all IPs for the two quorum ports + * (broadcast and fast leader election). + */ + protected boolean quorumListenOnAllIPs = false; + /** * @deprecated As of release 3.4.0, this class has been deprecated, since * it is used with one of the udp-based versions of leader election, which @@ -376,13 +382,14 @@ public QuorumPeer(Map quorumPeers, File dataDir, long myid, int tickTime, int initLimit, int syncLimit, ServerCnxnFactory cnxnFactory) throws IOException { this(quorumPeers, dataDir, dataLogDir, electionType, myid, tickTime, - initLimit, syncLimit, cnxnFactory, + initLimit, syncLimit, false, cnxnFactory, new QuorumMaj(countParticipants(quorumPeers))); } public QuorumPeer(Map quorumPeers, File dataDir, File dataLogDir, int electionType, long myid, int tickTime, int initLimit, int syncLimit, + boolean quorumListenOnAllIPs, ServerCnxnFactory cnxnFactory, QuorumVerifier quorumConfig) throws IOException { this(); @@ -393,6 +400,7 @@ public QuorumPeer(Map quorumPeers, File dataDir, this.tickTime = tickTime; this.initLimit = initLimit; this.syncLimit = syncLimit; + this.quorumListenOnAllIPs = quorumListenOnAllIPs; this.logFactory = new FileTxnSnapLog(dataLogDir, dataDir); this.zkDb = new ZKDatabase(this.logFactory); if(quorumConfig == null) @@ -515,7 +523,7 @@ public QuorumPeer(Map quorumPeers, File snapDir, throws IOException { this(quorumPeers, snapDir, logDir, electionAlg, - myid,tickTime, initLimit,syncLimit, + myid,tickTime, initLimit,syncLimit, false, ServerCnxnFactory.createFactory(new InetSocketAddress(clientPort), -1), new QuorumMaj(countParticipants(quorumPeers))); } @@ -531,7 +539,7 @@ public QuorumPeer(Map quorumPeers, File snapDir, throws IOException { this(quorumPeers, snapDir, logDir, electionAlg, - myid,tickTime, initLimit,syncLimit, + myid,tickTime, initLimit,syncLimit, false, ServerCnxnFactory.createFactory(new InetSocketAddress(clientPort), -1), quorumConfig); } @@ -1016,6 +1024,14 @@ public void setElectionType(int electionType) { this.electionType = electionType; } + public boolean getQuorumListenOnAllIPs() { + return quorumListenOnAllIPs; + } + + public void setQuorumListenOnAllIPs(boolean quorumListenOnAllIPs) { + this.quorumListenOnAllIPs = quorumListenOnAllIPs; + } + public ServerCnxnFactory getCnxnFactory() { return cnxnFactory; } 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 be0a410ba7d..c565b25a04a 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java @@ -59,6 +59,7 @@ public class QuorumPeerConfig { protected int syncLimit; protected int electionAlg = 3; protected int electionPort = 2182; + protected boolean quorumListenOnAllIPs = false; protected final HashMap servers = new HashMap(); protected final HashMap observers = @@ -157,6 +158,8 @@ public void parseProperties(Properties zkProp) syncLimit = Integer.parseInt(value); } else if (key.equals("electionAlg")) { electionAlg = Integer.parseInt(value); + } else if (key.equals("quorumListenOnAllIPs")) { + quorumListenOnAllIPs = Boolean.parseBoolean(value); } else if (key.equals("peerType")) { if (value.toLowerCase().equals("observer")) { peerType = LearnerType.OBSERVER; @@ -408,4 +411,8 @@ public Map getServers() { public LearnerType getPeerType() { return peerType; } + + public Boolean getQuorumListenOnAllIPs() { + return quorumListenOnAllIPs; + } } 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 4ffb24b399e..c6cf79e413e 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -147,6 +147,7 @@ public void runFromConfig(QuorumPeerConfig config) throws IOException { quorumPeer.setCnxnFactory(cnxnFactory); quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory())); quorumPeer.setLearnerType(config.getPeerType()); + quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs()); quorumPeer.start(); quorumPeer.join(); diff --git a/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java b/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java index 36587143a6d..a1f7a5eccb7 100644 --- a/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java +++ b/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java @@ -216,7 +216,7 @@ public MockQuorumPeer(Map quorumPeers, File snapDir, throws IOException { super(quorumPeers, snapDir, logDir, electionAlg, - myid,tickTime, initLimit,syncLimit, + myid,tickTime, initLimit,syncLimit, false, ServerCnxnFactory.createFactory(clientPort, -1), new QuorumMaj(countParticipants(quorumPeers))); } From dc3267b0ec40abdd67833a86e7a6051e0b468a4d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Thu, 26 Sep 2013 00:03:59 +0000 Subject: [PATCH 170/444] ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. (Jeffrey Zhong via mahadev) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1526338 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/client/ZooKeeperSaslClient.java | 27 +++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3948fbd1442..2f542735deb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -109,6 +109,9 @@ BUGFIXES: ZOOKEEPER-1096. Leader communication should listen on specified IP, not wildcard address (Jared Cantwell, German Blanco via fpj) + ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. + (Jeffrey Zhong via mahadev). + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index d45071ea3ad..8c7d26e418f 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -80,6 +80,7 @@ public static boolean isEnabled() { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class); private static Login login = null; private SaslClient saslClient; + private boolean isSASLConfigured = true; private byte[] saslToken = new byte[0]; @@ -104,7 +105,7 @@ public String getLoginContext() { } public ZooKeeperSaslClient(final String serverPrincipal) - throws LoginException { + throws LoginException { /** * ZOOKEEPER-1373: allow system property to specify the JAAS * configuration section that the zookeeper client should use. @@ -113,12 +114,12 @@ public ZooKeeperSaslClient(final String serverPrincipal) String clientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client"); // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. AppConfigurationEntry entries[] = null; - SecurityException securityException = null; + RuntimeException runtimeException = null; try { entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection); - } catch (SecurityException e) { + } catch (RuntimeException e) { // handle below: might be harmless if the user doesn't intend to use JAAS authentication. - securityException = e; + runtimeException = e; } if (entries != null) { this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'"; @@ -131,11 +132,11 @@ public ZooKeeperSaslClient(final String serverPrincipal) if (explicitClientSection != null) { // If the user explicitly overrides the default Login Context, they probably expected SASL to // succeed. But if we got here, SASL failed. - if (securityException != null) { + if (runtimeException != null) { throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + " section of the supplied JAAS configuration: '" + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + - "SecurityException: " + securityException); + "RuntimeException: " + runtimeException); } else { throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " + "section '" + explicitClientSection + "' could not be found."); @@ -144,21 +145,22 @@ public ZooKeeperSaslClient(final String serverPrincipal) // The user did not override the default context. It might be that they just don't intend to use SASL, // so log at INFO, not WARN, since they don't expect any SASL-related information. String msg = "Will not attempt to authenticate using SASL "; - if (securityException != null) { - msg += "(" + securityException.getLocalizedMessage() + ")"; + if (runtimeException != null) { + msg += "(" + runtimeException.getLocalizedMessage() + ")"; } else { msg += "(unknown error)"; } this.configStatus = msg; + this.isSASLConfigured = false; } if (System.getProperty(Environment.JAAS_CONF_KEY) != null) { // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed. - if (securityException != null) { + if (runtimeException != null) { throw new LoginException("Zookeeper client cannot authenticate using the '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + "' section of the supplied JAAS configuration: '" + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + - "SecurityException: " + securityException); + "RuntimeException: " + runtimeException); } else { throw new LoginException("No JAAS configuration section named '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + @@ -168,7 +170,7 @@ public ZooKeeperSaslClient(final String serverPrincipal) } } } - + /** * @return informational message indicating the current configuration status. */ @@ -528,6 +530,9 @@ public void handle(Callback[] callbacks) throws } public boolean clientTunneledAuthenticationInProgress() { + if (!isSASLConfigured) { + return false; + } // TODO: Rather than checking a disjunction here, should be a single member // variable or method in this class to determine whether the client is // configured to use SASL. (see also ZOOKEEPER-1455). From a8fe3f3be3b77273950d9e10bc2c51aadd1902dc Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 26 Sep 2013 12:37:12 +0000 Subject: [PATCH 171/444] ZOOKEEPER-87. Follower does not shut itself down if its too far behind the leader. (German Blanco via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1526461 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/quorum/LearnerHandler.java | 77 +++++++++++++++-- .../zookeeper/server/quorum/Zab1_0Test.java | 85 ++++++++++++++++++- .../org/apache/zookeeper/test/QuorumTest.java | 2 +- .../org/apache/zookeeper/test/QuorumUtil.java | 8 +- 5 files changed, 165 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2f542735deb..c72d99a2c0b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -112,6 +112,8 @@ BUGFIXES: ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. (Jeffrey Zhong via mahadev). + ZOOKEEPER-87. Follower does not shut itself down if its too far behind the leader. (German Blanco via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 1ddf6a6a7b1..01e79dc1b00 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -91,6 +91,63 @@ int getVersion() { final LinkedBlockingQueue queuedPackets = new LinkedBlockingQueue(); + /** + * This class controls the time that the Leader has been + * waiting for acknowledgement of a proposal from this Learner. + * If the time is above syncLimit, the connection will be closed. + * It keeps track of only one proposal at a time, when the ACK for + * that proposal arrives, it switches to the last proposal received + * or clears the value if there is no pending proposal. + */ + private class SyncLimitCheck { + private boolean started = false; + private long currentZxid = 0; + private long currentTime = 0; + private long nextZxid = 0; + private long nextTime = 0; + + public synchronized void start() { + started = true; + } + + public synchronized void updateProposal(long zxid, long time) { + if (!started) { + return; + } + if (currentTime == 0) { + currentTime = time; + currentZxid = zxid; + } else { + nextTime = time; + nextZxid = zxid; + } + } + + public synchronized void updateAck(long zxid) { + if (currentZxid == zxid) { + currentTime = nextTime; + currentZxid = nextZxid; + nextTime = 0; + nextZxid = 0; + } else if (nextZxid == zxid) { + LOG.warn("ACK for " + zxid + " received before ACK for " + currentZxid + "!!!!"); + nextTime = 0; + nextZxid = 0; + } + } + + public synchronized boolean check(long time) { + if (currentTime == 0) { + return true; + } else { + long msDelay = (time - currentTime) / 1000000; + return (msDelay < (leader.self.tickTime * leader.self.syncLimit)); + } + } + }; + + private SyncLimitCheck syncLimitCheck = new SyncLimitCheck(); + private BinaryInputArchive ia; private BinaryOutputArchive oa; @@ -148,6 +205,9 @@ private void sendPackets() throws InterruptedException { if (p.getType() == Leader.PING) { traceMask = ZooTrace.SERVER_PING_TRACE_MASK; } + if (p.getType() == Leader.PROPOSAL) { + syncLimitCheck.updateProposal(p.getZxid(), System.nanoTime()); + } if (LOG.isTraceEnabled()) { ZooTrace.logQuorumPacket(LOG, traceMask, 'o', p); } @@ -461,6 +521,8 @@ public void run() { } LOG.info("Received NEWLEADER-ACK message from " + getSid()); leader.waitForNewLeaderAck(getSid(), qp.getZxid(), getLearnerType()); + + syncLimitCheck.start(); // now that the ack has been processed expect the syncLimit sock.setSoTimeout(leader.self.tickTime * leader.self.syncLimit); @@ -505,6 +567,7 @@ public void run() { LOG.debug("Received ACK from Observer " + this.sid); } } + syncLimitCheck.updateAck(qp.getZxid()); leader.processAck(this.sid, qp.getZxid(), sock.getLocalSocketAddress()); break; case Leader.PING: @@ -614,12 +677,16 @@ public long tickOfNextAckDeadline() { */ public void ping() { long id; - synchronized(leader) { - id = leader.lastProposed; + if (syncLimitCheck.check(System.nanoTime())) { + synchronized(leader) { + id = leader.lastProposed; + } + QuorumPacket ping = new QuorumPacket(Leader.PING, id, null, null); + queuePacket(ping); + } else { + LOG.warn("Closing connection to peer due to transaction timeout."); + shutdown(); } - QuorumPacket ping = new QuorumPacket(Leader.PING, id, - null, null); - queuePacket(ping); } void queuePacket(QuorumPacket p) { diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index bfe6da0268b..de832115a5c 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -27,6 +27,7 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.EOFException; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.net.ServerSocket; @@ -67,6 +68,8 @@ import org.slf4j.LoggerFactory; public class Zab1_0Test { + private static final int SYNC_LIMIT = 2; + private static final Logger LOG = LoggerFactory.getLogger(Zab1_0Test.class); private static final class LeadThread extends Thread { @@ -833,6 +836,86 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) }); } + @Test + public void testTxnTimeout() throws Exception { + testLeaderConversation(new LeaderConversation() { + public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) + throws IOException, InterruptedException, org.apache.zookeeper.server.quorum.Leader.XidRolloverException { + Assert.assertEquals(0, l.self.getAcceptedEpoch()); + Assert.assertEquals(0, l.self.getCurrentEpoch()); + + LearnerInfo li = new LearnerInfo(1, 0x10000); + byte liBytes[] = new byte[20]; + ByteBufferOutputStream.record2ByteBuffer(li, + ByteBuffer.wrap(liBytes)); + QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 0, + liBytes, null); + oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.LEADERINFO, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), + 0x10000); + Assert.assertEquals(1, l.self.getAcceptedEpoch()); + Assert.assertEquals(0, l.self.getCurrentEpoch()); + + qp = new QuorumPacket(Leader.ACKEPOCH, 0, new byte[4], null); + oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.DIFF, qp.getType()); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.NEWLEADER, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(1, l.self.getAcceptedEpoch()); + Assert.assertEquals(1, l.self.getCurrentEpoch()); + + qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); + oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.UPTODATE, qp.getType()); + + l.propose(createNodeRequest(l.zk.getZxid())); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.PROPOSAL, qp.getType()); + + LOG.info("Proposal sent."); + + for (int i = 0; i < (2 * SYNC_LIMIT) + 2; i++) { + try { + ia.readRecord(qp, null); + LOG.info("Ping received: " + i); + qp = new QuorumPacket(Leader.PING, qp.getZxid(), "".getBytes(), null); + oa.writeRecord(qp, null); + } catch (EOFException e) { + return; + } + } + + Assert.fail("Connection hasn't been closed by leader after transaction times out."); + } + + private Request createNodeRequest(long zxid) throws IOException { + TxnHeader hdr = new TxnHeader(1, 1, zxid, 1, ZooDefs.OpCode.create); + CreateTxn ct = new CreateTxn("/foo", "data".getBytes(), null, true, 0); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputArchive boa = BinaryOutputArchive.getArchive(baos); + boa.writeRecord(hdr, "header"); + boa.writeRecord(ct, "txn"); + baos.close(); + Request rq = new Request(null, 1, 1, ZooDefs.OpCode.create, ByteBuffer.wrap(baos.toByteArray()), null); + rq.zxid = zxid; + rq.hdr = hdr; + rq.txn = ct; + return rq; + } + }); + } + private void deserializeSnapshot(InputArchive ia) throws IOException { ZKDatabase zkdb = new ZKDatabase(null); @@ -977,7 +1060,7 @@ private ConversableFollower createFollower(File tmpDir, QuorumPeer peer) private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, FileNotFoundException { QuorumPeer peer = new QuorumPeer(); - peer.syncLimit = 2; + peer.syncLimit = SYNC_LIMIT; peer.initLimit = 2; peer.tickTime = 2000; peer.quorumPeers = new HashMap(); diff --git a/src/java/test/org/apache/zookeeper/test/QuorumTest.java b/src/java/test/org/apache/zookeeper/test/QuorumTest.java index 85f1759c9c0..3b31786a90f 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumTest.java @@ -354,7 +354,7 @@ public void testNoLogBeforeLeaderEstablishment () throws IOException, InterruptedException, KeeperException{ final Semaphore sem = new Semaphore(0); - QuorumUtil qu = new QuorumUtil(2); + QuorumUtil qu = new QuorumUtil(2, 10); qu.startQuorum(); diff --git a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java index c39cb13361f..54df0914d27 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java @@ -79,7 +79,7 @@ public class PeerStruct { * @param n * number of peers in the ensemble will be 2n+1 */ - public QuorumUtil(int n) throws RuntimeException { + public QuorumUtil(int n, int syncLimit) throws RuntimeException { try { ClientBase.setupTestEnv(); JMXEnv.setUp(); @@ -88,7 +88,7 @@ public QuorumUtil(int n) throws RuntimeException { ALL = 2 * N + 1; tickTime = 2000; initLimit = 3; - syncLimit = 3; + this.syncLimit = syncLimit; electionAlg = 3; hostPort = ""; @@ -116,6 +116,10 @@ public QuorumUtil(int n) throws RuntimeException { } } + public QuorumUtil(int n) throws RuntimeException { + this(n, 3); + } + public PeerStruct getPeer(int id) { return peers.get(id); } From be944d15e8b684cc5a300665f2fceb19dbdad654 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 26 Sep 2013 21:15:53 +0000 Subject: [PATCH 172/444] ZOOKEEPER-1603. StaticHostProviderTest testUpdateClientMigrateOrNot hangs (fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1526695 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/client/StaticHostProvider.java | 5 ++-- .../test/StaticHostProviderTest.java | 26 ++++++++++++++----- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c72d99a2c0b..41efebe49c9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -114,6 +114,9 @@ BUGFIXES: ZOOKEEPER-87. Follower does not shut itself down if its too far behind the leader. (German Blanco via fpj) + ZOOKEEPER-1603. StaticHostProviderTest testUpdateClientMigrateOrNot hangs (fpj) + + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java index df14c9d0aa1..959fe89f5f5 100644 --- a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java +++ b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java @@ -57,8 +57,9 @@ public final class StaticHostProvider implements HostProvider { public StaticHostProvider(Collection serverAddresses) throws UnknownHostException { for (InetSocketAddress address : serverAddresses) { - InetAddress resolvedAddresses[] = InetAddress.getAllByName(address - .getHostName()); + InetAddress ia = address.getAddress(); + InetAddress resolvedAddresses[] = InetAddress.getAllByName((ia!=null) ? ia.getHostAddress(): + address.getHostName()); for (InetAddress resolvedAddress : resolvedAddresses) { this.serverAddresses.add(new InetSocketAddress(resolvedAddress .getHostAddress(), address.getPort())); diff --git a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java index 4dec9767416..b6b3e9ee631 100644 --- a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java +++ b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java @@ -21,20 +21,27 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.StaticHostProvider; import org.junit.Test; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; public class StaticHostProviderTest extends ZKTestCase { + private static final Logger LOG = LoggerFactory.getLogger(StaticHostProviderTest.class); @Test public void testNextGoesRound() throws UnknownHostException { - HostProvider hostProvider = getHostProvider(2); + HostProvider hostProvider = getHostProvider((byte) 2); InetSocketAddress first = hostProvider.next(0); assertTrue(first instanceof InetSocketAddress); hostProvider.next(0); @@ -43,7 +50,7 @@ public void testNextGoesRound() throws UnknownHostException { @Test public void testNextGoesRoundAndSleeps() throws UnknownHostException { - int size = 2; + byte size = 2; HostProvider hostProvider = getHostProvider(size); while (size > 0) { hostProvider.next(0); @@ -57,7 +64,7 @@ public void testNextGoesRoundAndSleeps() throws UnknownHostException { @Test public void testNextDoesNotSleepForZero() throws UnknownHostException { - int size = 2; + byte size = 2; HostProvider hostProvider = getHostProvider(size); while (size > 0) { hostProvider.next(0); @@ -72,25 +79,30 @@ public void testNextDoesNotSleepForZero() throws UnknownHostException { @Test public void testTwoConsequitiveCallsToNextReturnDifferentElement() throws UnknownHostException { - HostProvider hostProvider = getHostProvider(2); + HostProvider hostProvider = getHostProvider((byte) 2); assertNotSame(hostProvider.next(0), hostProvider.next(0)); } @Test public void testOnConnectDoesNotReset() throws UnknownHostException { - HostProvider hostProvider = getHostProvider(2); + HostProvider hostProvider = getHostProvider((byte) 2); InetSocketAddress first = hostProvider.next(0); hostProvider.onConnected(); InetSocketAddress second = hostProvider.next(0); assertNotSame(first, second); } - private StaticHostProvider getHostProvider(int size) + private StaticHostProvider getHostProvider(byte size) throws UnknownHostException { ArrayList list = new ArrayList( size); while (size > 0) { - list.add(new InetSocketAddress("10.10.10." + size, 1234)); + try { + list.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, size}), 1234 + size)); + } catch (UnknownHostException e) { + LOG.error("Exception while resolving address", e); + fail("Failed to resolve address"); + } --size; } return new StaticHostProvider(list); From b955cf62edbd18caae22d5c4a98c43a5d792844d Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 27 Sep 2013 23:17:07 +0000 Subject: [PATCH 173/444] Reverting ZOOKEEPER-1696 patch that was committed. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1527120 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 --- .../zookeeper/client/ZooKeeperSaslClient.java | 27 ++++++++----------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 41efebe49c9..32c319c5233 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -109,9 +109,6 @@ BUGFIXES: ZOOKEEPER-1096. Leader communication should listen on specified IP, not wildcard address (Jared Cantwell, German Blanco via fpj) - ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. - (Jeffrey Zhong via mahadev). - ZOOKEEPER-87. Follower does not shut itself down if its too far behind the leader. (German Blanco via fpj) ZOOKEEPER-1603. StaticHostProviderTest testUpdateClientMigrateOrNot hangs (fpj) diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 8c7d26e418f..d45071ea3ad 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -80,7 +80,6 @@ public static boolean isEnabled() { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class); private static Login login = null; private SaslClient saslClient; - private boolean isSASLConfigured = true; private byte[] saslToken = new byte[0]; @@ -105,7 +104,7 @@ public String getLoginContext() { } public ZooKeeperSaslClient(final String serverPrincipal) - throws LoginException { + throws LoginException { /** * ZOOKEEPER-1373: allow system property to specify the JAAS * configuration section that the zookeeper client should use. @@ -114,12 +113,12 @@ public ZooKeeperSaslClient(final String serverPrincipal) String clientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client"); // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. AppConfigurationEntry entries[] = null; - RuntimeException runtimeException = null; + SecurityException securityException = null; try { entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection); - } catch (RuntimeException e) { + } catch (SecurityException e) { // handle below: might be harmless if the user doesn't intend to use JAAS authentication. - runtimeException = e; + securityException = e; } if (entries != null) { this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'"; @@ -132,11 +131,11 @@ public ZooKeeperSaslClient(final String serverPrincipal) if (explicitClientSection != null) { // If the user explicitly overrides the default Login Context, they probably expected SASL to // succeed. But if we got here, SASL failed. - if (runtimeException != null) { + if (securityException != null) { throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + " section of the supplied JAAS configuration: '" + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + - "RuntimeException: " + runtimeException); + "SecurityException: " + securityException); } else { throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " + "section '" + explicitClientSection + "' could not be found."); @@ -145,22 +144,21 @@ public ZooKeeperSaslClient(final String serverPrincipal) // The user did not override the default context. It might be that they just don't intend to use SASL, // so log at INFO, not WARN, since they don't expect any SASL-related information. String msg = "Will not attempt to authenticate using SASL "; - if (runtimeException != null) { - msg += "(" + runtimeException.getLocalizedMessage() + ")"; + if (securityException != null) { + msg += "(" + securityException.getLocalizedMessage() + ")"; } else { msg += "(unknown error)"; } this.configStatus = msg; - this.isSASLConfigured = false; } if (System.getProperty(Environment.JAAS_CONF_KEY) != null) { // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed. - if (runtimeException != null) { + if (securityException != null) { throw new LoginException("Zookeeper client cannot authenticate using the '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + "' section of the supplied JAAS configuration: '" + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + - "RuntimeException: " + runtimeException); + "SecurityException: " + securityException); } else { throw new LoginException("No JAAS configuration section named '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + @@ -170,7 +168,7 @@ public ZooKeeperSaslClient(final String serverPrincipal) } } } - + /** * @return informational message indicating the current configuration status. */ @@ -530,9 +528,6 @@ public void handle(Callback[] callbacks) throws } public boolean clientTunneledAuthenticationInProgress() { - if (!isSASLConfigured) { - return false; - } // TODO: Rather than checking a disjunction here, should be a single member // variable or method in this class to determine whether the client is // configured to use SASL. (see also ZOOKEEPER-1455). From 77377b5b683b536c9ceb3bfdec567150e0ef18d0 Mon Sep 17 00:00:00 2001 From: Mahadev Konar Date: Fri, 27 Sep 2013 23:25:51 +0000 Subject: [PATCH 174/444] ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. (Jeffrey Zhong via mahade git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1527130 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../zookeeper/client/ZooKeeperSaslClient.java | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 32c319c5233..e6772f30073 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -113,6 +113,8 @@ BUGFIXES: ZOOKEEPER-1603. StaticHostProviderTest testUpdateClientMigrateOrNot hangs (fpj) + ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. + (Jeffrey Zhong via mahadev) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index d45071ea3ad..aa1e66f6fec 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -80,6 +80,7 @@ public static boolean isEnabled() { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class); private static Login login = null; private SaslClient saslClient; + private boolean isSASLConfigured = true; private byte[] saslToken = new byte[0]; @@ -113,12 +114,17 @@ public ZooKeeperSaslClient(final String serverPrincipal) String clientSection = System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client"); // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. AppConfigurationEntry entries[] = null; - SecurityException securityException = null; + RuntimeException runtimeException = null; try { entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection); } catch (SecurityException e) { // handle below: might be harmless if the user doesn't intend to use JAAS authentication. - securityException = e; + runtimeException = e; + } catch (IllegalArgumentException e) { + // third party customized getAppConfigurationEntry could throw IllegalArgumentException when JAAS + // configuration isn't set. We can reevaluate whether to catch RuntimeException instead when more + // different types of RuntimeException found + runtimeException = e; } if (entries != null) { this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'"; @@ -131,11 +137,11 @@ public ZooKeeperSaslClient(final String serverPrincipal) if (explicitClientSection != null) { // If the user explicitly overrides the default Login Context, they probably expected SASL to // succeed. But if we got here, SASL failed. - if (securityException != null) { + if (runtimeException != null) { throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + " section of the supplied JAAS configuration: '" + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + - "SecurityException: " + securityException); + "RuntimeException: " + runtimeException); } else { throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " + "section '" + explicitClientSection + "' could not be found."); @@ -144,21 +150,22 @@ public ZooKeeperSaslClient(final String serverPrincipal) // The user did not override the default context. It might be that they just don't intend to use SASL, // so log at INFO, not WARN, since they don't expect any SASL-related information. String msg = "Will not attempt to authenticate using SASL "; - if (securityException != null) { - msg += "(" + securityException.getLocalizedMessage() + ")"; + if (runtimeException != null) { + msg += "(" + runtimeException + ")"; } else { msg += "(unknown error)"; } this.configStatus = msg; + this.isSASLConfigured = false; } if (System.getProperty(Environment.JAAS_CONF_KEY) != null) { // Again, the user explicitly set something SASL-related, so they probably expected SASL to succeed. - if (securityException != null) { + if (runtimeException != null) { throw new LoginException("Zookeeper client cannot authenticate using the '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + "' section of the supplied JAAS configuration: '" + System.getProperty(Environment.JAAS_CONF_KEY) + "' because of a " + - "SecurityException: " + securityException); + "RuntimeException: " + runtimeException); } else { throw new LoginException("No JAAS configuration section named '" + System.getProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY, "Client") + @@ -168,7 +175,7 @@ public ZooKeeperSaslClient(final String serverPrincipal) } } } - + /** * @return informational message indicating the current configuration status. */ @@ -528,6 +535,9 @@ public void handle(Callback[] callbacks) throws } public boolean clientTunneledAuthenticationInProgress() { + if (!isSASLConfigured) { + return false; + } // TODO: Rather than checking a disjunction here, should be a single member // variable or method in this class to determine whether the client is // configured to use SASL. (see also ZOOKEEPER-1455). From f4fb6a2a5a8912b0e5a8fec65f0079d28ce9dd0a Mon Sep 17 00:00:00 2001 From: Thawan Kooburat Date: Mon, 30 Sep 2013 20:43:53 +0000 Subject: [PATCH 175/444] ZOOKEEPER-1552. Enable sync request processor in Observer (thawan, fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1527771 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 1 + .../server/SyncRequestProcessor.java | 31 ++++++++++++---- .../quorum/ObserverZooKeeperServer.java | 30 +++++++++++++--- .../zookeeper/server/quorum/QuorumPeer.java | 35 +++++++++++++++++++ .../server/quorum/QuorumPeerConfig.java | 7 ++++ .../server/quorum/QuorumPeerMain.java | 1 + 6 files changed, 94 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e6772f30073..fb48bce8ee5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -135,6 +135,7 @@ IMPROVEMENTS: ZOOKEEPER-1615. minor typos in ZooKeeper Programmer's Guide web page (Evan Zacks via phunt) + ZOOKEEPER-1552. Enable sync request processor in Observer (thawan, fpj) Release 3.4.5 - 2012-09-30 diff --git a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java index b18650cb22a..8d709b07785 100644 --- a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java @@ -27,11 +27,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** * This RequestProcessor logs requests to disk. It batches the requests to do * the io efficiently. The request is not passed to the next RequestProcessor * until its log has been synced to disk. + * + * SyncRequestProcessor is used in 3 different cases + * 1. Leader - Sync request to disk and forward it to AckRequestProcessor which + * send ack back to itself. + * 2. Follower - Sync request to disk and forward request to + * SendAckRequestProcessor which send the packets to leader. + * SendAckRequestProcessor is flushable which allow us to force + * push packets to leader. + * 3. Observer - Sync committed request to disk (received as INFORM packet). + * It never send ack back to the leader, so the nextProcessor will + * be null. This change the semantic of txnlog on the observer + * since it only contains committed txns. */ public class SyncRequestProcessor extends Thread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(SyncRequestProcessor.class); @@ -135,9 +146,11 @@ public void run() { // iff this is a read, and there are no pending // flushes (writes), then just pass this to the next // processor - nextProcessor.processRequest(si); - if (nextProcessor instanceof Flushable) { - ((Flushable)nextProcessor).flush(); + if (nextProcessor != null) { + nextProcessor.processRequest(si); + if (nextProcessor instanceof Flushable) { + ((Flushable)nextProcessor).flush(); + } } continue; } @@ -164,9 +177,11 @@ private void flush(LinkedList toFlush) zks.getZKDatabase().commit(); while (!toFlush.isEmpty()) { Request i = toFlush.remove(); - nextProcessor.processRequest(i); + if (nextProcessor != null) { + nextProcessor.processRequest(i); + } } - if (nextProcessor instanceof Flushable) { + if (nextProcessor != null && nextProcessor instanceof Flushable) { ((Flushable)nextProcessor).flush(); } } @@ -181,7 +196,9 @@ public void shutdown() { } catch(InterruptedException e) { LOG.warn("Interrupted while wating for " + this + " to finish"); } - nextProcessor.shutdown(); + if (nextProcessor != null) { + nextProcessor.shutdown(); + } } public void processRequest(Request request) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java index 60b78d7e511..48600e47952 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java @@ -38,6 +38,13 @@ public class ObserverZooKeeperServer extends LearnerZooKeeperServer { private static final Logger LOG = LoggerFactory.getLogger(ObserverZooKeeperServer.class); + /** + * Enable since request processor for writing txnlog to disk and + * take periodic snapshot. Default is ON. + */ + + private boolean syncRequestProcessorEnabled = this.self.getSyncEnabled(); + /* * Request processors */ @@ -54,6 +61,7 @@ public class ObserverZooKeeperServer extends LearnerZooKeeperServer { DataTreeBuilder treeBuilder, ZKDatabase zkDb) throws IOException { super(logFactory, self.tickTime, self.minSessionTimeout, self.maxSessionTimeout, treeBuilder, zkDb, self); + LOG.info("syncEnabled =" + syncRequestProcessorEnabled); } public Observer getObserver() { @@ -74,6 +82,10 @@ public Learner getLearner() { * @param request */ public void commitRequest(Request request) { + if (syncRequestProcessorEnabled) { + // Write to txnlog and take periodic snapshot + syncProcessor.processRequest(request); + } commitProcessor.commit(request); } @@ -92,11 +104,21 @@ protected void setupRequestProcessors() { commitProcessor.start(); firstProcessor = new ObserverRequestProcessor(this, commitProcessor); ((ObserverRequestProcessor) firstProcessor).start(); - syncProcessor = new SyncRequestProcessor(this, - new SendAckRequestProcessor(getObserver())); - syncProcessor.start(); + + /* + * Observer should write to disk, so that the it won't request + * too old txn from the leader which may lead to getting an entire + * snapshot. + * + * However, this may degrade performance as it has to write to disk + * and do periodic snapshot which may double the memory requirements + */ + if (syncRequestProcessorEnabled) { + syncProcessor = new SyncRequestProcessor(this, null); + syncProcessor.start(); + } } - + /* * Process a sync request */ 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 f8813ac0026..bd0b8405b27 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -242,6 +242,12 @@ public synchronized void setCurrentVote(Vote v){ * an acknowledgment */ protected int syncLimit; + + /** + * Enables/Disables sync request processor. This option is enabled + * by default and is to be used with observers. + */ + protected boolean syncEnabled; /** * The current tick @@ -1009,6 +1015,35 @@ public int getSyncLimit() { public void setSyncLimit(int syncLimit) { this.syncLimit = syncLimit; } + + + /** + * The syncEnabled can also be set via a system property. + */ + public static final String SYNC_ENABLED = "zookeeper.observer.syncEnabled"; + + /** + * Return syncEnabled. + * + * @return + */ + public boolean getSyncEnabled() { + if (System.getProperty(SYNC_ENABLED) != null) { + LOG.info(SYNC_ENABLED + "=" + Boolean.getBoolean(SYNC_ENABLED)); + return Boolean.getBoolean(SYNC_ENABLED); + } else { + return syncEnabled; + } + } + + /** + * Set syncEnabled. + * + * @param syncEnabled + */ + public void setSyncEnabled(boolean syncEnabled) { + this.syncEnabled = syncEnabled; + } /** * Gets the election type 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 c565b25a04a..59e048d6646 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java @@ -72,6 +72,7 @@ public class QuorumPeerConfig { protected QuorumVerifier quorumVerifier; protected int snapRetainCount = 3; protected int purgeInterval = 0; + protected boolean syncEnabled = true; protected LearnerType peerType = LearnerType.PARTICIPANT; @@ -169,6 +170,8 @@ public void parseProperties(Properties zkProp) { throw new ConfigException("Unrecognised peertype: " + value); } + } else if (key.equals( "syncEnabled" )) { + syncEnabled = Boolean.parseBoolean(value); } else if (key.equals("autopurge.snapRetainCount")) { snapRetainCount = Integer.parseInt(value); } else if (key.equals("autopurge.purgeInterval")) { @@ -395,6 +398,10 @@ public int getSnapRetainCount() { public int getPurgeInterval() { return purgeInterval; } + + public boolean getSyncEnabled() { + return syncEnabled; + } public QuorumVerifier getQuorumVerifier() { return quorumVerifier; 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 c6cf79e413e..e9c80070b3d 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -147,6 +147,7 @@ public void runFromConfig(QuorumPeerConfig config) throws IOException { quorumPeer.setCnxnFactory(cnxnFactory); quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory())); quorumPeer.setLearnerType(config.getPeerType()); + quorumPeer.setSyncEnabled(config.getSyncEnabled()); quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs()); quorumPeer.start(); From 6c0655b1ae1a6ee182ef56d9849f73d7a3362f89 Mon Sep 17 00:00:00 2001 From: Thawan Kooburat Date: Mon, 30 Sep 2013 20:54:41 +0000 Subject: [PATCH 176/444] ZOOKEEPER-1758. Add documentation for zookeeper.observer.syncEnabled flag (thawan, fpj via thawan) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1527778 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../content/xdocs/zookeeperAdmin.xml | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index fb48bce8ee5..ec1381720a2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -137,6 +137,9 @@ IMPROVEMENTS: ZOOKEEPER-1552. Enable sync request processor in Observer (thawan, fpj) + ZOOKEEPER-1758. Add documentation for zookeeper.observer.syncEnabled flag + (thawan, fpj via thawan) + Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index abab8f69298..6ea648fad77 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -826,6 +826,21 @@ server.3=zoo3:2888:3888 to enable the auto purging. Defaults to 0. + + + syncEnabled + + + (Java system property: zookeeper.observer.syncEnabled) + + New in 3.4.6, 3.5.0: + The observers now log transaction and write snapshot to disk + by default like the participants. This reduces the recovery time + of the observers on restart. Set to "false" to disable this + feature. Default is "true" + + From 84faeefab2694792e07d24e07b00f51ac8c3bddf Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Tue, 1 Oct 2013 21:30:42 +0000 Subject: [PATCH 177/444] ZOOKEEPER-1670. zookeeper should set a default value for SERVER_JVMFLAGS and CLIENT_JVMFLAGS so that memory usage is controlled (reverted) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1528221 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 -- bin/zkEnv.sh | 10 +--------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ec1381720a2..a59e66631fc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -88,8 +88,6 @@ BUGFIXES: ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj) - ZOOKEEPER-1670: zookeeper should set a default value for SERVER_JVMFLAGS and CLIENT_JVMFLAGS so that memory usage is controlled (Arpit Gupta via fpj) - ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) ZOOKEEPER-1664. Kerberos auth doesn't work with native platform GSS integration. (Boaz Kelmer via camille) diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh index 9be6984e1e4..43cbc2235a5 100755 --- a/bin/zkEnv.sh +++ b/bin/zkEnv.sh @@ -112,12 +112,4 @@ then CLASSPATH=`cygpath -wp "$CLASSPATH"` fi -#echo "CLASSPATH=$CLASSPATH" - -# default heap for zookeeper server -ZK_SERVER_HEAP="${ZK_SERVER_HEAP:-1000}" -export SERVER_JVMFLAGS="-Xmx${ZK_SERVER_HEAP}m $SERVER_JVMFLAGS" - -# default heap for zookeeper client -ZK_CLIENT_HEAP="${ZK_CLIENT_HEAP:-256}" -export CLIENT_JVMFLAGS="-Xmx${ZK_CLIENT_HEAP}m $CLIENT_JVMFLAGS" +#echo "CLASSPATH=$CLASSPATH" \ No newline at end of file From 33f17b51e8cba14dadf7522833d0088ac15b2dcb Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 1 Oct 2013 23:45:39 +0000 Subject: [PATCH 178/444] =?UTF-8?q?ZOOKEEPER-1770.=20NullPointerException?= =?UTF-8?q?=20in=20SnapshotFormatter=20(Germ=C3=A1n=20Blanco=20via=20phunt?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1528272 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/server/SnapshotFormatter.java | 6 +++++- .../data/invalidsnap/version-2/snapshot.273 | Bin 0 -> 56521 bytes .../zookeeper/test/InvalidSnapshotTest.java | 11 +++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/java/test/data/invalidsnap/version-2/snapshot.273 diff --git a/CHANGES.txt b/CHANGES.txt index a59e66631fc..e4f9985353f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -114,6 +114,9 @@ BUGFIXES: ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. (Jeffrey Zhong via mahadev) + ZOOKEEPER-1770. NullPointerException in SnapshotFormatter + (Germán Blanco via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java index 865dfbdbda0..0ecab73d051 100644 --- a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java +++ b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java @@ -87,7 +87,11 @@ private void printZnode(DataTree dataTree, String name) { synchronized(n) { // keep findbugs happy System.out.println(name); printStat(n.stat); - System.out.println(" dataLength = " + n.data.length); + if (n.data != null) { + System.out.println(" dataLength = " + n.data.length); + } else { + System.out.println(" no data"); + } children = n.getChildren(); } if (children != null) { diff --git a/src/java/test/data/invalidsnap/version-2/snapshot.273 b/src/java/test/data/invalidsnap/version-2/snapshot.273 new file mode 100644 index 0000000000000000000000000000000000000000..3146f5656633f739c241b2f921e9e54213a19134 GIT binary patch literal 56521 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 Date: Thu, 3 Oct 2013 21:45:05 +0000 Subject: [PATCH 179/444] ZOOKEEPER-732. Improper translation of error into Python exception (Andrei Savu, Lei Zhang, fpj via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1529012 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/contrib/zkpython/src/c/zookeeper.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e4f9985353f..cd0fbbb3e69 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -117,6 +117,8 @@ BUGFIXES: ZOOKEEPER-1770. NullPointerException in SnapshotFormatter (Germán Blanco via phunt) + ZOOKEEPER-732. Improper translation of error into Python exception (Andrei Savu, Lei Zhang, fpj via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/contrib/zkpython/src/c/zookeeper.c b/src/contrib/zkpython/src/c/zookeeper.c index 59747635a81..bfd81fa747e 100644 --- a/src/contrib/zkpython/src/c/zookeeper.c +++ b/src/contrib/zkpython/src/c/zookeeper.c @@ -52,6 +52,8 @@ PyObject *err_to_exception(int errcode) { switch (errcode) { case ZSYSTEMERROR: return SystemErrorException; + case ZINVALIDSTATE: + return InvalidStateException; case ZRUNTIMEINCONSISTENCY: return RuntimeInconsistencyException; case ZDATAINCONSISTENCY: @@ -66,8 +68,6 @@ PyObject *err_to_exception(int errcode) { return OperationTimeoutException; case ZBADARGUMENTS: return BadArgumentsException; - case ZINVALIDSTATE: - return InvalidStateException; case ZAPIERROR: return ApiErrorException; case ZNONODE: @@ -92,6 +92,10 @@ PyObject *err_to_exception(int errcode) { return InvalidCallbackException; case ZSESSIONMOVED: return SessionMovedException; + case ZCLOSING: + return ClosingException; + case ZNOTHING: + return NothingException; case ZOK: default: return NULL; From cf5781ddf0922842a871988872acdf8627505e40 Mon Sep 17 00:00:00 2001 From: Thawan Kooburat Date: Mon, 7 Oct 2013 19:16:04 +0000 Subject: [PATCH 180/444] ZOOKEEPER-1551. Observers ignore txns that come after snapshot and UPTODATE (thawan, fpj via thawan) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1530035 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/quorum/Leader.java | 15 +- .../zookeeper/server/quorum/Learner.java | 49 ++- .../zookeeper/server/quorum/QuorumPeer.java | 2 +- .../zookeeper/server/quorum/Zab1_0Test.java | 284 ++++++++++++++++-- 5 files changed, 320 insertions(+), 33 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index cd0fbbb3e69..ac8cc99f4aa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -119,6 +119,9 @@ BUGFIXES: ZOOKEEPER-732. Improper translation of error into Python exception (Andrei Savu, Lei Zhang, fpj via fpj) + ZOOKEEPER-1551. Observers ignore txns that come after snapshot and UPTODATE + (thawan, fpj via thawan) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index b26d7594537..06e36b01a3a 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -829,13 +829,16 @@ synchronized public long startForwarding(LearnerHandler handler, .getZxid(), null, null); handler.queuePacket(qp); } - Listzxids = new ArrayList(outstandingProposals.keySet()); - Collections.sort(zxids); - for (Long zxid: zxids) { - if (zxid <= lastSeenZxid) { - continue; + // Only participant need to get outstanding proposals + if (handler.getLearnerType() == LearnerType.PARTICIPANT) { + Listzxids = new ArrayList(outstandingProposals.keySet()); + Collections.sort(zxids); + for (Long zxid: zxids) { + if (zxid <= lastSeenZxid) { + continue; + } + handler.queuePacket(outstandingProposals.get(zxid).packet); } - handler.queuePacket(outstandingProposals.get(zxid).packet); } } if (handler.getLearnerType() == LearnerType.PARTICIPANT) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index e8d548ba153..5d9b169a208 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -392,9 +392,28 @@ else if (qp.getType() == Leader.SNAP) { } break; case Leader.INFORM: - TxnHeader hdr = new TxnHeader(); - Record txn = SerializeUtils.deserializeTxn(qp.getData(), hdr); - zk.processTxn(hdr, txn); + /* + * Only observer get this type of packet. We treat this + * as receiving PROPOSAL and COMMMIT. + */ + PacketInFlight packet = new PacketInFlight(); + packet.hdr = new TxnHeader(); + packet.rec = SerializeUtils.deserializeTxn(qp.getData(), packet.hdr); + // Log warning message if txn comes out-of-order + if (packet.hdr.getZxid() != lastQueued + 1) { + LOG.warn("Got zxid 0x" + + Long.toHexString(packet.hdr.getZxid()) + + " expected 0x" + + Long.toHexString(lastQueued + 1)); + } + lastQueued = packet.hdr.getZxid(); + if (!snapshotTaken) { + // Apply to db directly if we haven't taken the snapshot + zk.processTxn(packet.hdr, packet.rec); + } else { + packetsNotCommitted.add(packet); + packetsCommitted.add(qp.getZxid()); + } break; case Leader.UPTODATE: if (!snapshotTaken) { // true for the pre v1.0 case @@ -425,6 +444,30 @@ else if (qp.getType() == Leader.SNAP) { for(Long zxid: packetsCommitted) { fzk.commit(zxid); } + } else if (zk instanceof ObserverZooKeeperServer) { + // Similar to follower, we need to log requests between the snapshot + // and UPTODATE + ObserverZooKeeperServer ozk = (ObserverZooKeeperServer) zk; + for (PacketInFlight p : packetsNotCommitted) { + Long zxid = packetsCommitted.peekFirst(); + if (p.hdr.getZxid() != zxid) { + // log warning message if there is no matching commit + // old leader send outstanding proposal to observer + LOG.warn("Committing " + Long.toHexString(zxid) + + ", but next proposal is " + + Long.toHexString(p.hdr.getZxid())); + continue; + } + packetsCommitted.remove(); + Request request = new Request(null, p.hdr.getClientId(), + p.hdr.getCxid(), p.hdr.getType(), null, null); + request.txn = p.rec; + request.hdr = p.hdr; + ozk.commitRequest(request); + } + } else { + // New server type need to handle in-flight packets + throw new UnsupportedOperationException("Unknown server type"); } } 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 bd0b8405b27..882ea54cadd 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -247,7 +247,7 @@ public synchronized void setCurrentVote(Vote v){ * Enables/Disables sync request processor. This option is enabled * by default and is to be used with observers. */ - protected boolean syncEnabled; + protected boolean syncEnabled = true; /** * The current tick diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index de832115a5c..682e00b60d5 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -52,6 +52,7 @@ import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.server.ZooKeeperServer.DataTreeBuilder; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; @@ -332,6 +333,10 @@ static public interface FollowerConversation { void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception; } + static public interface ObserverConversation { + void converseWithObserver(InputArchive ia, OutputArchive oa, Observer o) throws Exception; + } + public void testLeaderConversation(LeaderConversation conversation) throws Exception { Socket pair[] = getSocketPair(); Socket leaderSocket = pair[0]; @@ -496,6 +501,57 @@ public void run() { } } + public void testObserverConversation(ObserverConversation conversation) throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + Thread observerThread = null; + ConversableObserver observer = null; + QuorumPeer peer = null; + try { + peer = createQuorumPeer(tmpDir); + peer.setSyncEnabled(true); + observer = createObserver(tmpDir, peer); + peer.observer = observer; + + ServerSocket ss = new ServerSocket(); + ss.bind(null); + observer.setLeaderSocketAddress((InetSocketAddress)ss.getLocalSocketAddress()); + final Observer observerForThread = observer; + + observerThread = new Thread() { + public void run() { + try { + observerForThread.observeLeader(); + } catch(Exception e) { + e.printStackTrace(); + } + } + }; + observerThread.start(); + Socket leaderSocket = ss.accept(); + + InputArchive ia = BinaryInputArchive.getArchive(leaderSocket + .getInputStream()); + OutputArchive oa = BinaryOutputArchive.getArchive(leaderSocket + .getOutputStream()); + + conversation.converseWithObserver(ia, oa, observer); + } finally { + if (observer != null) { + observer.shutdown(); + } + if (observerThread != null) { + observerThread.interrupt(); + observerThread.join(); + } + if (peer != null) { + peer.shutdown(); + } + recursiveDelete(tmpDir); + } + } + @Test public void testUnnecessarySnap() throws Exception { testPopulatedLeaderConversation(new PopulatedLeaderConversation() { @@ -535,7 +591,31 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, } }, 2); } - + + // We want to track the change with a callback rather than depending on timing + class TrackerWatcher implements Watcher { + boolean changed; + synchronized void waitForChange() throws InterruptedException { + while(!changed) { + wait(); + } + } + + @Override + public void process(WatchedEvent event) { + if (event.getType() == EventType.NodeDataChanged) { + synchronized(this) { + changed = true; + notifyAll(); + } + } + } + synchronized public boolean changed() { + return changed; + } + }; + + @Test public void testNormalFollowerRun() throws Exception { testFollowerConversation(new FollowerConversation() { @@ -613,28 +693,6 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, proposeSetData(qp, proposalZxid, "data2", 2); oa.writeRecord(qp, null); - // We want to track the change with a callback rather than depending on timing - class TrackerWatcher implements Watcher { - boolean changed; - synchronized void waitForChange() throws InterruptedException { - while(!changed) { - wait(); - } - } - @Override - public void process(WatchedEvent event) { - if (event.getType() == EventType.NodeDataChanged) { - synchronized(this) { - changed = true; - notifyAll(); - } - } - } - synchronized public boolean changed() { - return changed; - } - - }; TrackerWatcher watcher = new TrackerWatcher(); // The change should not have happened yet, since we haven't committed @@ -924,6 +982,157 @@ private void deserializeSnapshot(InputArchive ia) assertEquals("BenWasHere", signature); } + @Test + public void testNormalObserverRun() throws Exception { + testObserverConversation(new ObserverConversation() { + @Override + public void converseWithObserver(InputArchive ia, OutputArchive oa, + Observer o) throws Exception { + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + File logDir = o.zk.getTxnLogFactory().getDataDir().getParentFile(); + File snapDir = o.zk.getTxnLogFactory().getSnapDir().getParentFile(); + try { + Assert.assertEquals(0, o.self.getAcceptedEpoch()); + Assert.assertEquals(0, o.self.getCurrentEpoch()); + + // Setup a database with a single /foo node + ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); + final long foo1Zxid = ZxidUtils.makeZxid(1, 1); + final long foo2Zxid = ZxidUtils.makeZxid(1, 2); + zkDb.processTxn(new TxnHeader(13, 1313, foo1Zxid, 33, + ZooDefs.OpCode.create), new CreateTxn("/foo1", + "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + false, 1)); + zkDb.processTxn(new TxnHeader(13, 1313, foo2Zxid, 33, + ZooDefs.OpCode.create), new CreateTxn("/foo2", + "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, + false, 1)); + Stat stat = new Stat(); + Assert.assertEquals("data1", + new String(zkDb.getData("/foo1", stat, null))); + Assert.assertEquals("data1", + new String(zkDb.getData("/foo2", stat, null))); + + QuorumPacket qp = new QuorumPacket(); + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.OBSERVERINFO, qp.getType()); + Assert.assertEquals(qp.getZxid(), 0); + LearnerInfo learnInfo = new LearnerInfo(); + ByteBufferInputStream.byteBuffer2Record( + ByteBuffer.wrap(qp.getData()), learnInfo); + Assert.assertEquals(learnInfo.getProtocolVersion(), 0x10000); + Assert.assertEquals(learnInfo.getServerid(), 0); + + // We are simulating an established leader, so the epoch is 1 + qp.setType(Leader.LEADERINFO); + qp.setZxid(ZxidUtils.makeZxid(1, 0)); + byte protoBytes[] = new byte[4]; + ByteBuffer.wrap(protoBytes).putInt(0x10000); + qp.setData(protoBytes); + oa.writeRecord(qp, null); + + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACKEPOCH, qp.getType()); + Assert.assertEquals(0, qp.getZxid()); + Assert.assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer + .wrap(qp.getData()).getInt()); + Assert.assertEquals(1, o.self.getAcceptedEpoch()); + Assert.assertEquals(0, o.self.getCurrentEpoch()); + + // Send the snapshot we created earlier + qp.setType(Leader.SNAP); + qp.setData(new byte[0]); + qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); + oa.writeRecord(qp, null); + zkDb.serializeSnapshot(oa); + oa.writeString("BenWasHere", null); + qp.setType(Leader.NEWLEADER); + qp.setZxid(ZxidUtils.makeZxid(1, 0)); + oa.writeRecord(qp, null); + + // Get the ack of the new leader + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + Assert.assertEquals(1, o.self.getAcceptedEpoch()); + Assert.assertEquals(1, o.self.getCurrentEpoch()); + + Assert.assertEquals(foo2Zxid, o.zk.getLastProcessedZxid()); + + // Make sure the data was recorded in the filesystem ok + ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog( + logDir, snapDir)); + long lastZxid = zkDb2.loadDataBase(); + Assert.assertEquals("data1", + new String(zkDb2.getData("/foo1", stat, null))); + Assert.assertEquals(foo2Zxid, lastZxid); + + // Register watch + TrackerWatcher watcher = new TrackerWatcher(); + Assert.assertEquals("data1", new String(o.zk + .getZKDatabase().getData("/foo2", stat, watcher))); + + // Propose /foo1 update + long proposalZxid = ZxidUtils.makeZxid(1, 1000); + proposeSetData(qp, "/foo1", proposalZxid, "data2", 2); + oa.writeRecord(qp, null); + + // Commit /foo1 update + qp.setType(Leader.COMMIT); + qp.setZxid(proposalZxid); + oa.writeRecord(qp, null); + + // Inform /foo2 update + long informZxid = ZxidUtils.makeZxid(1, 1001); + proposeSetData(qp, "/foo2", informZxid, "data2", 2); + qp.setType(Leader.INFORM); + oa.writeRecord(qp, null); + + qp.setType(Leader.UPTODATE); + qp.setZxid(0); + oa.writeRecord(qp, null); + + // Read the uptodate ack + readPacketSkippingPing(ia, qp); + Assert.assertEquals(Leader.ACK, qp.getType()); + Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); + + // Data should get updated + watcher.waitForChange(); + Assert.assertEquals("data2", new String(o.zk + .getZKDatabase().getData("/foo1", stat, null))); + Assert.assertEquals("data2", new String(o.zk + .getZKDatabase().getData("/foo2", stat, null))); + + zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); + lastZxid = zkDb2.loadDataBase(); + Assert.assertEquals("data2", new String(zkDb2.getData("/foo1", stat, null))); + Assert.assertEquals("data2", new String(zkDb2.getData("/foo2", stat, null))); + Assert.assertEquals(informZxid, lastZxid); + } finally { + recursiveDelete(tmpDir); + } + + } + + private void proposeSetData(QuorumPacket qp, String path, + long zxid, String data, int version) throws IOException { + qp.setType(Leader.PROPOSAL); + qp.setZxid(zxid); + TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, + ZooDefs.OpCode.setData); + SetDataTxn sdt = new SetDataTxn(path, data.getBytes(), version); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputArchive boa = BinaryOutputArchive.getArchive(baos); + boa.writeRecord(hdr, null); + boa.writeRecord(sdt, null); + qp.setData(baos.toByteArray()); + } + }); + } + @Test public void testLeaderBehind() throws Exception { testLeaderConversation(new LeaderConversation() { @@ -1057,6 +1266,35 @@ private ConversableFollower createFollower(File tmpDir, QuorumPeer peer) return new ConversableFollower(peer, zk); } + static class ConversableObserver extends Observer { + + ConversableObserver(QuorumPeer self, ObserverZooKeeperServer zk) { + super(self, zk); + } + + InetSocketAddress leaderAddr; + public void setLeaderSocketAddress(InetSocketAddress addr) { + leaderAddr = addr; + } + + @Override + protected InetSocketAddress findLeader() { + return leaderAddr; + } + } + + private ConversableObserver createObserver(File tmpDir, QuorumPeer peer) + throws IOException { + FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); + peer.setTxnFactory(logFactory); + DataTreeBuilder treeBuilder = new ZooKeeperServer.BasicDataTreeBuilder(); + ZKDatabase zkDb = new ZKDatabase(logFactory); + ObserverZooKeeperServer zk = new ObserverZooKeeperServer(logFactory, peer, treeBuilder, zkDb); + peer.setZKDatabase(zkDb); + return new ConversableObserver(peer, zk); + } + + private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, FileNotFoundException { QuorumPeer peer = new QuorumPeer(); From ad7113ed56f58f3d2c308addf2c5785114cb9899 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 7 Oct 2013 23:37:49 +0000 Subject: [PATCH 181/444] ZOOKEEPER-1781. ZooKeeper Server fails if snapCount is set to 1 (Takashi Ohnishi via phunt, breed) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1530111 13f79535-47bb-0310-9956-ffa450edef68 --- docs/bookkeeperConfig.pdf | Bin 13824 -> 13824 bytes docs/bookkeeperOverview.pdf | Bin 147596 -> 147596 bytes docs/bookkeeperProgrammer.pdf | Bin 24991 -> 24991 bytes docs/bookkeeperStarted.pdf | Bin 17130 -> 17130 bytes docs/bookkeeperStream.pdf | Bin 13215 -> 13215 bytes docs/index.pdf | Bin 13525 -> 13525 bytes docs/javaExample.pdf | Bin 33852 -> 33852 bytes docs/linkmap.pdf | Bin 12472 -> 12472 bytes docs/recipes.html | 2 +- docs/recipes.pdf | Bin 31075 -> 31072 bytes docs/releasenotes.pdf | Bin 118640 -> 118640 bytes docs/zookeeperAdmin.html | 28 ++++ docs/zookeeperAdmin.pdf | Bin 72942 -> 74144 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6660 -> 6660 bytes docs/zookeeperInternals.pdf | Bin 48872 -> 48872 bytes docs/zookeeperJMX.pdf | Bin 16498 -> 16498 bytes docs/zookeeperObservers.pdf | Bin 12884 -> 12884 bytes docs/zookeeperOver.pdf | Bin 302521 -> 302521 bytes docs/zookeeperProgrammers.pdf | Bin 133888 -> 133888 bytes docs/zookeeperQuotas.pdf | Bin 11269 -> 11269 bytes docs/zookeeperStarted.pdf | Bin 27581 -> 27581 bytes docs/zookeeperTutorial.pdf | Bin 30557 -> 30557 bytes .../zookeeper/server/ZooKeeperServer.java | 9 +- .../server/InvalidSnapCountTest.java | 126 ++++++++++++++++++ 24 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/InvalidSnapCountTest.java diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index 0e8a9cb34e6e1e02b92c73664684aa83ebfb4def..d840c3f15c946c1d6a6d0665ae817916e35f6c3c 100644 GIT binary patch delta 188 zcmZq3X~>x{iPzA;z}(Qx*xb-W*T8(^{A)adh6X@Zh@qL4vAGpkbaNYTKO?5_WHo*r zOwrBB{B~;b#^&aZ7N&+4rbf<=js~VKhQ=1o#-=7lCZ=WvCYFxQb_zBGmBez{*>M$@ ZBo>ua6s4wd85)@zSaPYVy863u0RSvrEhhi~ delta 188 zcmZq3X~>x{iPyl;$i&DPh)s13EH=)+#v^E8sB2^rVq|P(WMTyt-Q338&xk2JS&d%@ zQ*?7OznxmVp|gpTk)e^Pk*m3zfvd5JtC^dnlZAnWp_`?vg|UH?oq`QPC9zy~c3j0J YiA5z9MX70AhDPQFmRzc;uKsRZ06e@chX4Qo diff --git a/docs/bookkeeperOverview.pdf b/docs/bookkeeperOverview.pdf index 461ba19fcdc21bc215b2c8ea5f2b8f7762ecc18c..faaf20e8eda640dc95027a3fe4565e50d81ffb21 100644 GIT binary patch delta 175 zcmeBa*Q+P5D zzYeBovnPMMCqLtMPkyGy2F}h-mQId9<4p}+9nCC_%^V#qoE=@A+?<@<3{6~&ob42B M2q~GK@4zGr0Q@5=vH$=8 delta 175 zcmeBa#MRi;(A?D6(aG4=&C=Y^#KhFq+{w{S!G@rcSS~v| buHur!qLPZD)HE(5V`Eb@E>%@me>W}wUePb1 delta 190 zcmbP#m~sAL#tD;n4GfJ;jEsTULf61z&`LR$$T1ZM^-An8K6Q z_;oNvHz)IJ7&|+f7+bhnIvE=|nmRj~8=5*=IyoD-IvF{cIGGq3SQywT*bq`O`J9ss E0OqME@Bjb+ delta 166 zcmaFW%J{04al#~C14APdBV!{IV?$j7i;eTI@dz3i>Kd7Z7#Uj`nOK2EH@ETjGhzx) zR^!*f6y2Q6uVL)$WNPH*?Cfl6X=rL<>gHl>;A&xJWa@0-WM%bn+yEz#-nwq&hHz{0FSFKSO5S3 delta 188 zcmbQAK0kdzH?M)Ak%^J9k%_T^u7SnI8RvNf4GeXSOhSx|t&B{pz@nQgc>5VKg(pk! z>tKp*4&{HX9PjFAVQOJ);AHG-Y~*BNU~Fh)ZfR*~Y363;=Hh5*YHX)qLr_U9mz^C~ aaY$)(Za;s&BD~t63B2eHgz#DGqSL-Ff_JPupy*m@=g;O05zN` AwEzGB delta 164 zcmcbbc{OvwI$i@qBNHQIBNJl-T?31ayFc*=8W`#tnS>Y_TN#;Hfkij(|rE3oM1BHn&ROyS8w z{5qJToBjFwN}Qc79i1&q937nuO)Q*T4UCPAEiK&~U0qC^O&pC4oSp3yYzQfttk5C@ E0MV~03jhEB delta 166 zcmdnf!L+A?X+j6DfuWI!kueZk>Ka&VoO*^w(7;gF$Rxzb*viPn3M{(0h_{~+Q+Tov zzYeD8W`F*^5@$SS!@X5eCEXy|6)Xy{~Yr(i=!$>cc(G61L# BD5(Gd delta 164 zcmdmyxFd1G1YQF}BNHQIBNJl-T?31abFc6S8W`#tnS>Y_TN#;Hfkihr@%A%f3Qtzz z*TEFs9MAtt!P(K$(!j;Y(9+e?(a6-q%*fQ#)!E6?)y2TV%*4XQ$k9&0hLDoUa|~nv DlM*PE diff --git a/docs/recipes.html b/docs/recipes.html index 57669472a16..b2fbb14d8f0 100644 --- a/docs/recipes.html +++ b/docs/recipes.html @@ -454,7 +454,7 @@

    Double Barriers

  • if p is the lowest process - node in L, wait on highest process node in P

    + node in L, wait on highest process node in L

  • diff --git a/docs/recipes.pdf b/docs/recipes.pdf index 294b720df3ac91f2a1049812a525b50e7a8b1093..177b8aaeaf3f256bde6c0e1bffdde5b1867bb73f 100644 GIT binary patch delta 4205 zcmah}c{r5q9!4TdmKd@t+xRTaKJOS5H6>XiOK7s6M4z2(FGa`}V^7x4*wuWtB+9-- zwrmlltRrj4mg9Wq)VV%$uIoI1J=gvI?&o(u_wRn*&#UwwSLq+c9*4tVSU3iWg#!vO zEG2z_of{52*crkxN=U5IUuaqI@fdnqm?X(5Pm7jWa>lZPnCKE6JHeTtNh_}r=l0|6 z3B|kH4B{>%^L9^rs~AZ|{t15uv75YW`^Jy8N|A!XrQMfp}b2L<2B8?dvxS7Jdjwez(aYu zUfAz$QtXYKI_~j;paX0K}uHyU1hP{kseCnlJ5Jg^}_I~&Y9!-40?$z@bbGh z8ASoJXDxoat!l={Va=|m`8?K%G45%0DWs~oG+$8RHrlZ3&bGV5gTf7wq}%M=fe?%Y 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 OXe_Ig6hZeQ>pua6zml^6 delta 4049 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 diff --git a/docs/releasenotes.pdf b/docs/releasenotes.pdf index 51fbd53c8c3c8c4451ce8e751568f629b28ff911..12658b21ec3bd81861f6b1548fd25250b2a2c63b 100644 GIT binary patch delta 195 zcmew`kNv|u_6hyGh6V=ahGxd*hNij(<{Rf+<`FbB0J1_1&8&>gt-zw28+iK}F@+~9 z@atfTHplXBkL73Fyg$L&+116w#MIH;&D_}7$jRB+#m&vk%+T4w)yUP%)Y-*O!G@rc gSS~v|uHur!qLPZD)HE(bLjw~7BQ8}{SARDy0KcCx!~g&Q delta 195 zcmew`kNv|u_6hyG28Ko^M#eyFrfXoaan5BPK?6fwBa;v#V=E&QE3oM12Ht)~OyS82 z{5qJT&9VI3WBD03?@ussGH`NnHL!3ob~18yGB7eQH!^cFwlH$EbTlAdvanced Configuration be triggered. Set to a positive integer (1 and above) to enable the auto purging. Defaults to 0.

    + + +
    +syncEnabled +
    +
    +

    (Java system property: zookeeper.observer.syncEnabled)

    +

    +New in 3.4.6, 3.5.0: + The observers now log transaction and write snapshot to disk + by default like the participants. This reduces the recovery time + of the observers on restart. Set to "false" to disable this + feature. Default is "true"

    +
    @@ -1458,6 +1472,20 @@

    Unsafe Options

    Skips ACL checks. This results in a boost in throughput, but opens up full access to the data tree to everyone.

    + + +
    +quorumListenOnAllIPs +
    +
    +

    When set to true the ZooKeeper server will listen + for connections from its peers on all available IP addresses, + and not only the address configured in the server list of the + configuration file. It affects the connections handling the + ZAB protocol and the Fast Leader Election protocol. Default + value is false.

    +
    + diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf index 6f22b955d43667b64997193a94383bebb3db9b38..fad67f99fed95ad146a81536dc7eb092e82b39bf 100644 GIT binary patch delta 23889 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 C=_i)} diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf index bb3cf556dc7cefe40bf7f6805afb107eef01b2ed..72f27b1d81af7c22f9c51465937eefb080db1d89 100644 GIT binary patch delta 166 zcmaFym+8e{rU|XQh6V=ahGxd*hL*Yp<{KxSgt-zw2b9nn1F@-1d z@atfTZuaDNSnq7&YHsFgVQOJvY~pI*XkqDO4#bwG#;!(AMix$vPId}5gp^D^vrh&9 DfpIFc delta 166 zcmaFym+8e{rU|XQ28Ko^M#eyFscT@danea1K?6fwBa;v#V=E&QE3oM19NvCLOyS8q z{5qJTn?3m*);k*;x*0l}Iy$;JnL4?dxtN-{85+768km`yTevtGo0-`u*bq`O`OH2U E0DRCYn*aa+ diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf index b82e94e9bcfa7bcf029825c5be10784932b2e1b6..d616512e5bb6afec2bce97ac4a1055a8826163de 100644 GIT binary patch delta 188 zcmey=!1$?waY8+>p@D(9p_#F{p{1^Y`Np2ZJc5P>KvsyMnU%4*6?{4UCm6*E!|vAT}_fuWI!kueZk>Ka&V>^aOMXke&oWD;UzY-MC(1s2_$z}wG=DLjc^ z2UBpfE&p|cI0IuRCnHBA3kw4m6LV7|6LV(^b2lebMk_bI|UmWenw2;$vpfz zn4+6K`PV2p8#%fdJDHdmJ2^SKI2)K48krcmx)~X}8JjzsnweQz*eTc$QZm`XNCp6e CP$$p; delta 164 zcmcbTawTO#E3bi}k%^J9k%_UPu7SnINhf&(4GeXSOhSx|t&B{pz@nRTc>5VKg(vgy z>tKp*_T*oq#^z>@b_zCxluWiTk^um3 C_9un_ diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf index 5df6e6ccd177ce946d3fbd7da2d9b5662e926810..1df95fb1de4b0536ba3c9f42b5bb12e35a974c19 100644 GIT binary patch delta 250 zcmdnFTWIHQp$XNzh6V=ahGxd*hDN#u<{LZq^9ULm09hf1W>&`LR$$T15xo73n8K5P z@abTRHkXR%ml>D+fDgdbQ^4p6^u<33>1R66cqG5a}5;?jS)Ok1#?RT&q%?@ z5WzE1FgM;lub*W%ud}&zsgsklsf&rFk)@NNn~{N&p{bpM4Iw4d J=gei11pvQrI^_TW diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf index d2b7c5b9677d2e4f212a0807bcc84ba8717f18a7..8780b094e7c66ee454c710dc07a96bf2c1081f04 100644 GIT binary patch delta 195 zcmZpe#?dg1V?rOVp@D(9p_#F{k%6v(`Nr9ocmxeWtPn#pD`Rsju;}JG-hM_*;mLCR zI+&u((fr$^`59e*CzzW%895m^nH!oLS(uu-nVY$oyE?nLxL6oEn_Czd8rvz@5L6P& fWoO4#T#{H+Qc;we#${+^X<}}`rK;-c@5TiHyNxg2 delta 195 zcmZpe#?dg1V?rOVfuWI!k+G49v4O6E#m3o}cmxd$b&X6yjEt>}Osv47o9lS{88L+? z%kk@AiZ(~{Z;$3@bp4%RU}@oIVBlzE>SAeTVB+lP;^yS&YHsLgU}0!zWbSHWr(i=+ hNi3J09anKlVo^y&QED2Op^>GDxdE4|s;j>n7XYi3FSq~z diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf index 00ab18969f5bcf959f05cdb534aaff9b893dca52..edb7e7ae22cffc6e1abfb768fd98c182675c8df1 100644 GIT binary patch delta 164 zcmZpTXpNZA$!lm}U~Xt;Y;I_wYhb={+F2e!Ljxcy#L&#j*xU*%y19h6pAl1dvIxHp zrs(EC{!kfb6GJyQ12acsH$yjPBQqyU6GK;L6K4ZcS5pfYOA}K!I|Um;N+!S5mH_}5 CyC>fO delta 164 zcmZpTXpNZA$!lO}WMX6t#1^^+78|FX|B&CJZn+0@d-&D7M@z`(`T+0xL|(#+Y#(a_A$)y2e4!G@5M$qTb( E0CdtSwg3PC delta 166 zcmdmcopJAV#t9R74GfJ;jEsTULf61z Date: Tue, 8 Oct 2013 05:27:13 +0000 Subject: [PATCH 182/444] ZOOKEEPER-1774. QuorumPeerMainTest fails consistently with complains about host" assertion failure (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1530155 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index ac8cc99f4aa..3be03156a17 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -122,6 +122,9 @@ BUGFIXES: ZOOKEEPER-1551. Observers ignore txns that come after snapshot and UPTODATE (thawan, fpj via thawan) + ZOOKEEPER-1774. QuorumPeerMainTest fails consistently with + "complains about host" assertion failure (phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM 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 0acf8f26521..24cd060dac6 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -406,7 +406,7 @@ public void testBadPeerAddressInQuorum() throws Exception { boolean isup = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, - 5000); + 30000); Assert.assertFalse("Server never came up", isup); From 67148f0f0c5deb34c57712ceb2f0ca9bf21596b9 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 8 Oct 2013 05:38:47 +0000 Subject: [PATCH 183/444] ZOOKEEPER-1771. ZooInspector authentication (Benjamin Jaton via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1530160 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../config/defaultConnectionSettings.cfg | 3 +- .../manager/ZooInspectorManagerImpl.java | 32 ++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3be03156a17..4a6cc97856c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -149,6 +149,8 @@ IMPROVEMENTS: ZOOKEEPER-1758. Add documentation for zookeeper.observer.syncEnabled flag (thawan, fpj via thawan) + ZOOKEEPER-1771. ZooInspector authentication (Benjamin Jaton via phunt) + Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/src/contrib/zooinspector/config/defaultConnectionSettings.cfg b/src/contrib/zooinspector/config/defaultConnectionSettings.cfg index 806789e10b3..36a34ff3088 100644 --- a/src/contrib/zooinspector/config/defaultConnectionSettings.cfg +++ b/src/contrib/zooinspector/config/defaultConnectionSettings.cfg @@ -14,7 +14,8 @@ # limitations under the License. # #Default connection for ZooInspector -#Sun Feb 28 14:46:55 GMT 2010 hosts=localhost\:2181 encryptionManager=org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager timeout=5000 +authScheme= +authData= diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java index e30e317c06d..6dd3e8a21d4 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java @@ -82,6 +82,14 @@ public class ZooInspectorManagerImpl implements ZooInspectorManager { * file */ public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager"; + /** + * The key used for the authentication scheme in the connection properties file + */ + public static final String AUTH_SCHEME_KEY = "authScheme"; + /** + * The key used for the authentication data in the connection properties file + */ + public static final String AUTH_DATA_KEY = "authData"; private static final File defaultNodeViewersFile = new File( "./config/defaultNodeVeiwers.cfg"); @@ -98,6 +106,8 @@ public class ZooInspectorManagerImpl implements ZooInspectorManager { private String defaultEncryptionManager; private String defaultTimeout; private String defaultHosts; + private String defaultAuthScheme; + private String defaultAuthValue; /** * @throws IOException @@ -124,6 +134,11 @@ public boolean connect(Properties connectionProps) { .getProperty(SESSION_TIMEOUT); String encryptionManager = connectionProps .getProperty(DATA_ENCRYPTION_MANAGER); + String authScheme = connectionProps + .getProperty(AUTH_SCHEME_KEY); + String authData = connectionProps + .getProperty(AUTH_DATA_KEY); + if (connectString == null || sessionTimeout == null) { throw new IllegalArgumentException( "Both connect string and session timeout are required."); @@ -153,6 +168,9 @@ public void process(WatchedEvent event) { } } }); + if (authData != null && authData.length() > 0){ + this.zooKeeper.addAuthInfo(authScheme, authData.getBytes()); + } ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10); connected = ((ZooKeeperRetry) this.zooKeeper).testConnection(); return connected; @@ -571,10 +589,16 @@ public Pair>, Map> getConnectionPropert .asList(new String[] { defaultTimeout })); template.put(DATA_ENCRYPTION_MANAGER, Arrays .asList(new String[] { defaultEncryptionManager })); + template.put(AUTH_SCHEME_KEY, Arrays + .asList(new String[] { defaultAuthScheme })); + template.put(AUTH_DATA_KEY, Arrays + .asList(new String[] { defaultAuthValue })); Map labels = new LinkedHashMap(); labels.put(CONNECT_STRING, "Connect String"); labels.put(SESSION_TIMEOUT, "Session Timeout"); labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager"); + labels.put(AUTH_SCHEME_KEY, "Authentication Scheme"); + labels.put(AUTH_DATA_KEY, "Authentication Data"); return new Pair>, Map>( template, labels); } @@ -707,7 +731,7 @@ public List loadNodeViewersFile(File selectedFile) try { while (buff.ready()) { String line = buff.readLine(); - if (line != null && line.length() > 0) { + if (line != null && line.length() > 0 && !line.startsWith("#")) { result.add(line); } } @@ -738,10 +762,16 @@ private void loadDefaultConnectionFile() throws IOException { : props.getProperty(SESSION_TIMEOUT); defaultHosts = props.getProperty(CONNECT_STRING) == null ? "localhost:2181" : props.getProperty(CONNECT_STRING); + defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ? "" + : props.getProperty(AUTH_SCHEME_KEY); + defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ? "" + : props.getProperty(AUTH_DATA_KEY); } else { defaultEncryptionManager = "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager"; defaultTimeout = "5000"; defaultHosts = "localhost:2181"; + defaultAuthScheme = ""; + defaultAuthValue = ""; } } From 631872e79957461e70eac0f4e7d033c769fc8a6c Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 8 Oct 2013 06:45:28 +0000 Subject: [PATCH 184/444] ZOOKEEPER-877. zkpython does not work with python3.1 (Daniel Enman via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1530167 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + src/contrib/zkpython/src/c/zookeeper.c | 58 +++++++++++++++++-- src/contrib/zkpython/src/python/zk.py | 22 +++---- .../zkpython/src/test/connection_test.py | 8 ++- src/contrib/zkpython/src/test/get_set_test.py | 7 ++- src/contrib/zkpython/src/test/zktestbase.py | 2 +- 6 files changed, 79 insertions(+), 21 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4a6cc97856c..ef7466ac352 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -125,6 +125,9 @@ BUGFIXES: ZOOKEEPER-1774. QuorumPeerMainTest fails consistently with "complains about host" assertion failure (phunt) + ZOOKEEPER-877. zkpython does not work with python3.1 + (Daniel Enman via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/contrib/zkpython/src/c/zookeeper.c b/src/contrib/zkpython/src/c/zookeeper.c index bfd81fa747e..7d8c899f106 100644 --- a/src/contrib/zkpython/src/c/zookeeper.c +++ b/src/contrib/zkpython/src/c/zookeeper.c @@ -275,7 +275,11 @@ PyObject *build_string_vector(const struct String_vector *sv) if (ret) { int i; for (i=0;icount;++i) { +#if PY_MAJOR_VERSION >= 3 + PyObject *s = PyUnicode_FromString(sv->data[i]); +#else PyObject *s = PyString_FromString(sv->data[i]); +#endif if (!s) { if (ret != Py_None) { Py_DECREF(ret); @@ -381,9 +385,15 @@ int parse_acls(struct ACL_vector *acls, PyObject *pyacls) a = PyList_GetItem(pyacls, i); // a is now a dictionary PyObject *perms = PyDict_GetItemString( a, "perms" ); +#if PY_MAJOR_VERSION >= 3 + acls->data[i].perms = (int32_t)(PyLong_AsLong(perms)); + acls->data[i].id.id = strdup( PyUnicode_AsUnicode( PyDict_GetItemString( a, "id" ) ) ); + acls->data[i].id.scheme = strdup( PyUnicode_AsUnicode( PyDict_GetItemString( a, "scheme" ) ) ); +#else acls->data[i].perms = (int32_t)(PyInt_AsLong(perms)); acls->data[i].id.id = strdup( PyString_AsString( PyDict_GetItemString( a, "id" ) ) ); acls->data[i].id.scheme = strdup( PyString_AsString( PyDict_GetItemString( a, "scheme" ) ) ); +#endif } return 1; } @@ -1432,7 +1442,14 @@ PyObject *pyzoo_set_log_stream(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "Must supply a Python object to set_log_stream"); return NULL; } - if (!PyFile_Check(pystream)) { + +#if PY_MAJOR_VERSION >= 3 + extern PyTypeObject PyIOBase_Type; + if (!PyObject_IsInstance(pystream, (PyObject *)&PyIOBase_Type)) { +#else + if(!PyFile_Check(pystream)) { +#endif + PyErr_SetString(PyExc_ValueError, "Must supply a file object to set_log_stream"); return NULL; } @@ -1443,7 +1460,14 @@ PyObject *pyzoo_set_log_stream(PyObject *self, PyObject *args) log_stream = pystream; Py_INCREF(log_stream); - zoo_set_log_stream(PyFile_AsFile(log_stream)); + +#if PY_MAJOR_VERSION >= 3 + int fd = PyObject_AsFileDescriptor(log_stream); + FILE *fp = fdopen(fd, "w"); +#else + FILE *fp = PyFile_AsFile(log_stream); +#endif + zoo_set_log_stream(fp); Py_INCREF(Py_None); return Py_None; @@ -1505,6 +1529,20 @@ static PyMethodDef ZooKeeperMethods[] = { {NULL, NULL} }; +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef zookeeper_moddef = { + PyModuleDef_HEAD_INIT, + "zookeeper", + NULL, + 0, + ZooKeeperMethods, + 0, + 0, + 0, + 0 +}; +#endif + #define ADD_INTCONSTANT(x) PyModule_AddIntConstant(module, #x, ZOO_##x) #define ADD_INTCONSTANTZ(x) PyModule_AddIntConstant(module, #x, Z##x) @@ -1512,10 +1550,18 @@ static PyMethodDef ZooKeeperMethods[] = { Py_INCREF(x); \ PyModule_AddObject(module, #x, x); - +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC PyInit_zookeeper(void) { +#else PyMODINIT_FUNC initzookeeper(void) { +#endif PyEval_InitThreads(); - PyObject *module = Py_InitModule("zookeeper", ZooKeeperMethods ); + +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&zookeeper_moddef); +#else + PyObject *module = Py_InitModule("zookeeper", ZooKeeperMethods); +#endif if (init_zhandles(32) == 0) { return; // TODO: Is there any way to raise an exception here? } @@ -1611,4 +1657,8 @@ PyMODINIT_FUNC initzookeeper(void) { ADD_EXCEPTION(ClosingException); ADD_EXCEPTION(NothingException); ADD_EXCEPTION(SessionMovedException); + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif } diff --git a/src/contrib/zkpython/src/python/zk.py b/src/contrib/zkpython/src/python/zk.py index abafaa07081..9c0f37482ed 100755 --- a/src/contrib/zkpython/src/python/zk.py +++ b/src/contrib/zkpython/src/python/zk.py @@ -24,22 +24,22 @@ def my_connection_watcher(handle,type,state,path): global connected, conn_cv - print "Connected, handle is ", handle + print("Connected, handle is ", handle) conn_cv.acquire() connected = True conn_cv.notifyAll() conn_cv.release() conn_cv.acquire() -print "Connecting to localhost:2181 -- " +print("Connecting to localhost:2181 -- ") handle = zookeeper.init("localhost:2181", my_connection_watcher, 10000, 0) while not connected: conn_cv.wait() conn_cv.release() def my_getc_watch( handle, type, state, path ): - print "Watch fired -- " - print type, state, path + print("Watch fired -- ") + print(type, state, path) ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"}; @@ -47,12 +47,12 @@ def my_getc_watch( handle, type, state, path ): zookeeper.create(handle, "/zk-python", "data", [ZOO_OPEN_ACL_UNSAFE], 0) zookeeper.get_children(handle, "/zk-python", my_getc_watch) for i in xrange(5): - print "Creating sequence node ", i, " ", zookeeper.create(handle, "/zk-python/sequencenode", "data", [ZOO_OPEN_ACL_UNSAFE], zookeeper.SEQUENCE ) + print("Creating sequence node ", i, " ", zookeeper.create(handle, "/zk-python/sequencenode", "data", [ZOO_OPEN_ACL_UNSAFE], zookeeper.SEQUENCE )) except: pass def pp_zk(handle,root, indent = 0): - """Pretty print a zookeeper tree, starting at root""" + """Pretty print(a zookeeper tree, starting at root""") def make_path(child): if root == "/": return "/" + child @@ -62,15 +62,15 @@ def make_path(child): for i in xrange(indent): out += "\t" out += "|---"+root + " :: " + zookeeper.get(handle, root, None)[0] - print out + print(out) for child in children: pp_zk(handle,make_path(child),indent+1) -print "ZNode tree -- " +print("ZNode tree -- ") pp_zk(handle,"/") -print "Getting ACL / Stat for /zk-python --" +print("Getting ACL / Stat for /zk-python --") (stat, acl) = zookeeper.get_acl(handle, "/zk-python") -print "Stat:: ", stat -print "Acl:: ", acl +print("Stat:: ", stat) +print("Acl:: ", acl) diff --git a/src/contrib/zkpython/src/test/connection_test.py b/src/contrib/zkpython/src/test/connection_test.py index b852d1a6fce..3913fe3b7d9 100755 --- a/src/contrib/zkpython/src/test/connection_test.py +++ b/src/contrib/zkpython/src/test/connection_test.py @@ -16,7 +16,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest, threading, re +import unittest, threading, re, sys +if sys.version_info < (3,): + range = xrange import zookeeper, zktestbase ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} @@ -71,7 +73,7 @@ def connection_watcher(handle, type, state, path): cv.release() cv.acquire() - handles = [ zookeeper.init(self.host) for i in xrange(10) ] + handles = [ zookeeper.init(self.host) for i in range(10) ] ret = zookeeper.init(self.host, connection_watcher) cv.wait(15.0) cv.release() @@ -93,7 +95,7 @@ def testmanyhandles(self): """ # We'd like to do more, but currently the C client doesn't # work with > 83 handles (fails to create a pipe) on MacOS 10.5.8 - handles = [ zookeeper.init(self.host) for i in xrange(63) ] + handles = [ zookeeper.init(self.host) for i in range(9) ] cv = threading.Condition() self.connected = False diff --git a/src/contrib/zkpython/src/test/get_set_test.py b/src/contrib/zkpython/src/test/get_set_test.py index 64d76f4aad4..953d9e4ae4f 100755 --- a/src/contrib/zkpython/src/test/get_set_test.py +++ b/src/contrib/zkpython/src/test/get_set_test.py @@ -16,7 +16,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import zookeeper, zktestbase, unittest, threading +import zookeeper, zktestbase, unittest, threading, sys +if sys.version_info < (3,): + range = xrange + ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} class GetSetTest(zktestbase.TestBase): @@ -84,7 +87,7 @@ def test_sync_get_large_datanode(self): 1Mb with default parameters (depends on ZooKeeper server). """ - data = ''.join(["A" for x in xrange(1024*1023)]) + data = ''.join(["A" for x in range(1024*1023)]) self.ensureDeleted("/zk-python-test-large-datanode") zookeeper.create(self.handle, "/zk-python-test-large-datanode", data, [{"perms":0x1f, "scheme":"world", "id" :"anyone"}]) diff --git a/src/contrib/zkpython/src/test/zktestbase.py b/src/contrib/zkpython/src/test/zktestbase.py index cf0f0557914..8229418e3e7 100755 --- a/src/contrib/zkpython/src/test/zktestbase.py +++ b/src/contrib/zkpython/src/test/zktestbase.py @@ -34,7 +34,7 @@ def __init__(self,methodName='runTest'): f = open(logfile,"w") zookeeper.set_log_stream(f) except IOError: - print "Couldn't open " + logfile + " for writing" + print("Couldn't open " + logfile + " for writing") def setUp(self): From 7451ab2a72c6ac1a32619d20fb626e3f43e171d9 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 9 Oct 2013 22:14:08 +0000 Subject: [PATCH 185/444] ZOOKEEPER-1627. Add org.apache.zookeeper.common to exported packages in OSGi MANIFEST (Arnoud Glimmerveen via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1530810 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ef7466ac352..3771be2de4e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -154,6 +154,9 @@ IMPROVEMENTS: ZOOKEEPER-1771. ZooInspector authentication (Benjamin Jaton via phunt) + ZOOKEEPER-1627. Add org.apache.zookeeper.common to exported packages + in OSGi MANIFEST (Arnoud Glimmerveen via phunt) + Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/build.xml b/build.xml index 57d3171d4ad..e608f9c0b26 100644 --- a/build.xml +++ b/build.xml @@ -538,7 +538,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + @@ -579,7 +579,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 8b4cc5290fe83094d2f5798a129c5306ebb0abcb Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Thu, 10 Oct 2013 18:06:58 +0000 Subject: [PATCH 186/444] ZOOKEEPER-1624. PrepRequestProcessor abort multi-operation incorrectly. (thawan via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1531063 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + src/c/tests/TestMulti.cc | 50 +++++++++++++++++++ .../server/PrepRequestProcessor.java | 26 ++++++++-- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3771be2de4e..11b7ccaf8a1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -127,6 +127,8 @@ BUGFIXES: ZOOKEEPER-877. zkpython does not work with python3.1 (Daniel Enman via phunt) + + ZOOKEEPER-1624. PrepRequestProcessor abort multi-operation incorrectly. (thawan via camille) IMPROVEMENTS: diff --git a/src/c/tests/TestMulti.cc b/src/c/tests/TestMulti.cc index 6bf61b2013a..a54e047b3ba 100644 --- a/src/c/tests/TestMulti.cc +++ b/src/c/tests/TestMulti.cc @@ -177,6 +177,7 @@ class Zookeeper_multi : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testMultiFail); CPPUNIT_TEST(testCheck); CPPUNIT_TEST(testWatch); + CPPUNIT_TEST(testSequentialNodeCreateInAsyncMulti); #endif CPPUNIT_TEST_SUITE_END(); @@ -244,6 +245,10 @@ class Zookeeper_multi : public CPPUNIT_NS::TestFixture count++; } + static void multi_completion_fn_no_assert(int rc, const void *data) { + count++; + } + static void waitForMultiCompletion(int seconds) { time_t expires = time(0) + seconds; while(count == 0 && time(0) < expires) { @@ -252,6 +257,10 @@ class Zookeeper_multi : public CPPUNIT_NS::TestFixture count--; } + static void resetCounter() { + count = 0; + } + /** * Test basic multi-op create functionality */ @@ -646,6 +655,47 @@ class Zookeeper_multi : public CPPUNIT_NS::TestFixture // wait for multi completion in doMultiInWatch waitForMultiCompletion(5); } + + /** + * ZOOKEEPER-1624: PendingChanges of create sequential node request didn't + * get rollbacked correctly when multi-op failed. This caused + * create sequential node request in subsequent multi-op to failed because + * sequential node name generation is incorrect. + * + * The check is to make sure that each request in multi-op failed with + * the correct reason. + */ + void testSequentialNodeCreateInAsyncMulti() { + int rc; + watchctx_t ctx; + zhandle_t *zk = createClient(&ctx); + + int iteration = 4; + int nops = 2; + + zoo_op_result_t results[iteration][nops]; + zoo_op_t ops[nops]; + zoo_create_op_init(&ops[0], "/node-", "", 0, &ZOO_OPEN_ACL_UNSAFE, ZOO_SEQUENCE, NULL, 0); + zoo_create_op_init(&ops[1], "/dup", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, NULL, 0); + for (int i = 0; i < iteration ; ++i) { + rc = zoo_amulti(zk, nops, ops, results[i], multi_completion_fn_no_assert, 0); + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + } + + waitForMultiCompletion(10); + + CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0][0].err); + CPPUNIT_ASSERT_EQUAL((int)ZOK, results[1][0].err); + CPPUNIT_ASSERT_EQUAL((int)ZOK, results[2][0].err); + CPPUNIT_ASSERT_EQUAL((int)ZOK, results[3][0].err); + + CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0][1].err); + CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, results[1][1].err); + CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, results[2][1].err); + CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, results[3][1].err); + + resetCounter(); + } }; volatile int Zookeeper_multi::count; diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 59e45cc66d6..f636d2a87cb 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -198,10 +198,28 @@ HashMap getPendingChanges(MultiTransactionRecord multiRequ String path = op.getPath(); try { - ChangeRecord cr = getRecordForPath(path); - if (cr != null) { - pendingChangeRecords.put(path, cr); - } + ChangeRecord cr = getRecordForPath(path); + if (cr != null) { + pendingChangeRecords.put(path, cr); + } + /* + * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord + * of the parent node of a request. So that if this is a + * sequential node creation request, rollbackPendingChanges() + * can restore previous parent's ChangeRecord correctly. + * + * Otherwise, sequential node name generation will be incorrect + * for a subsequent request. + */ + int lastSlash = path.lastIndexOf('/'); + if (lastSlash == -1 || path.indexOf('\0') != -1) { + continue; + } + String parentPath = path.substring(0, lastSlash); + ChangeRecord parentCr = getRecordForPath(parentPath); + if (parentCr != null) { + pendingChangeRecords.put(parentPath, parentCr); + } } catch (KeeperException.NoNodeException e) { // ignore this one } From f9e2f5105e411e01b29f79e8ee0aebcae769cfec Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 11 Oct 2013 19:19:18 +0000 Subject: [PATCH 187/444] ZOOKEEPER-1610. Some classes are using == or != to compare Long/String objects instead of .equals() (Edward Ribeiro via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1531397 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/client/ZooKeeperSaslClient.java | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 11b7ccaf8a1..776d0b6845a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -130,6 +130,9 @@ BUGFIXES: ZOOKEEPER-1624. PrepRequestProcessor abort multi-operation incorrectly. (thawan via camille) + ZOOKEEPER-1610. Some classes are using == or != to compare + Long/String objects instead of .equals() (Edward Ribeiro via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index aa1e66f6fec..dbc10801949 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -330,10 +330,10 @@ public void respondToServer(byte[] serverToken, ClientCnxn cnxn) { if (saslClient.isComplete()) { // GSSAPI: server sends a final packet after authentication succeeds // or fails. - if ((serverToken == null) && (saslClient.getMechanismName() == "GSSAPI")) + if ((serverToken == null) && (saslClient.getMechanismName().equals("GSSAPI"))) gotLastPacket = true; // non-GSSAPI: no final packet from server. - if (saslClient.getMechanismName() != "GSSAPI") { + if (!saslClient.getMechanismName().equals("GSSAPI")) { gotLastPacket = true; } // SASL authentication is completed, successfully or not: From fe3824a993d02c61e82e3baf5b315776a28eb375 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 17 Oct 2013 17:08:27 +0000 Subject: [PATCH 188/444] ZOOKEEPER-1646. mt c client tests fail on Ubuntu Raring (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1533162 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/tests/LibCMocks.cc | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 776d0b6845a..9cb3ebfb485 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -133,6 +133,8 @@ BUGFIXES: ZOOKEEPER-1610. Some classes are using == or != to compare Long/String objects instead of .equals() (Edward Ribeiro via phunt) + ZOOKEEPER-1646. mt c client tests fail on Ubuntu Raring (phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/tests/LibCMocks.cc b/src/c/tests/LibCMocks.cc index 57de1e36dc2..44ab0a9ad38 100644 --- a/src/c/tests/LibCMocks.cc +++ b/src/c/tests/LibCMocks.cc @@ -310,6 +310,17 @@ int poll(struct pollfd *fds, POLL_NFDS_TYPE nfds, int timeout){ } +/* + * Recent gcc with -O2 and glibc FORTIFY feature may cause our poll + * mock to be ignored. + */ +#if __USE_FORTIFY_LEVEL > 0 +int __poll_chk (struct pollfd *__fds, nfds_t __nfds, int __timeout, + __SIZE_TYPE__ __fdslen) { + return poll(__fds, __nfds, __timeout); +} +#endif + // ***************************************************************************** // gettimeofday int gettimeofday(struct timeval *tp, GETTIMEOFDAY_ARG2_TYPE tzp){ From d91f65c07692875932c15e563f5596727bda0fca Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 19 Oct 2013 10:05:30 +0000 Subject: [PATCH 189/444] ZOOKEEPER-1558. Leader should not snapshot uncommitted state (fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1533725 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/SyncRequestProcessor.java | 23 ++++- .../zookeeper/server/ZooKeeperServer.java | 4 +- .../zookeeper/server/quorum/Leader.java | 2 +- .../zookeeper/server/quorum/Zab1_0Test.java | 85 +++++++++++++++++++ 5 files changed, 110 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9cb3ebfb485..b1a697c9473 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -135,6 +135,8 @@ BUGFIXES: ZOOKEEPER-1646. mt c client tests fail on Ubuntu Raring (phunt) + ZOOKEEPER-1558. Leader should not snapshot uncommitted state (fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java index 8d709b07785..9129b00b766 100644 --- a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java @@ -65,6 +65,12 @@ public class SyncRequestProcessor extends Thread implements RequestProcessor { * The number of log entries to log before starting a snapshot */ private static int snapCount = ZooKeeperServer.getSnapCount(); + + /** + * The number of log entries before rolling the log, number + * is chosen randomly + */ + private static int randRoll; private final Request requestOfDeath = Request.requestOfDeath; @@ -76,7 +82,7 @@ public SyncRequestProcessor(ZooKeeperServer zks, this.nextProcessor = nextProcessor; running = true; } - + /** * used by tests to check for changing * snapcounts @@ -84,6 +90,7 @@ public SyncRequestProcessor(ZooKeeperServer zks, */ public static void setSnapCount(int count) { snapCount = count; + randRoll = count; } /** @@ -93,6 +100,18 @@ public static void setSnapCount(int count) { public static int getSnapCount() { return snapCount; } + + /** + * Sets the value of randRoll. This method + * is here to avoid a findbugs warning for + * setting a static variable in an instance + * method. + * + * @param roll + */ + private static void setRandRoll(int roll) { + randRoll = roll; + } @Override public void run() { @@ -101,7 +120,7 @@ public void run() { // we do this in an attempt to ensure that not all of the servers // in the ensemble take a snapshot at the same time - int randRoll = r.nextInt(snapCount/2); + setRandRoll(r.nextInt(snapCount/2)); while (true) { Request si = null; if (toFlush.isEmpty()) { diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index e1742f5d6de..a049a31f367 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -284,12 +284,10 @@ public void loadData() throws IOException, InterruptedException { // XXX: Is lastProcessedZxid really the best thing to use? killSession(session, zkDb.getDataTreeLastProcessedZxid()); } - - // Make a clean snapshot - takeSnapshot(); } public void takeSnapshot(){ + try { txnLogFactory.save(zkDb.getDataTree(), zkDb.getSessionWithTimeOuts()); } catch (IOException e) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 06e36b01a3a..b3b1f17c099 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -434,7 +434,7 @@ void lead() throws IOException, InterruptedException { long zxid = Long.parseLong(initialZxid); zk.setZxid((zk.getZxid() & 0xffffffff00000000L) | zxid); } - + if (!System.getProperty("zookeeper.leaderServes", "yes").equals("no")) { self.cnxnFactory.setZooKeeperServer(zk); } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 682e00b60d5..9975d568e10 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -33,23 +33,30 @@ import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.ByteBufferOutputStream; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServer.DataTreeBuilder; @@ -1201,6 +1208,84 @@ public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) }); } + /** + * verify that a peer with dirty snapshot joining an established cluster + * does not go into an inconsistent state. + * + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1558} + */ + @Test + public void testDirtySnapshot() + throws IOException, + InterruptedException, + KeeperException, + NoSuchFieldException, + IllegalAccessException { + Socket pair[] = getSocketPair(); + Socket leaderSocket = pair[0]; + Socket followerSocket = pair[1]; + File tmpDir = File.createTempFile("test", "dir"); + tmpDir.delete(); + tmpDir.mkdir(); + LeadThread leadThread = null; + Leader leader = null; + try { + // Setup a database with two znodes + FileTxnSnapLog snapLog = new FileTxnSnapLog(tmpDir, tmpDir); + ZKDatabase zkDb = new ZKDatabase(snapLog); + + long zxid = ZxidUtils.makeZxid(0, 1); + String path = "/foo"; + zkDb.processTxn(new TxnHeader(13,1000,zxid,30,ZooDefs.OpCode.create), + new CreateTxn(path, "fpjwasalsohere".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1)); + Stat stat = new Stat(); + Assert.assertEquals("fpjwasalsohere", new String(zkDb.getData(path, stat, null))); + + // Close files + snapLog.close(); + + QuorumPeer peer = createQuorumPeer(tmpDir); + + leader = createLeader(tmpDir, peer); + peer.leader = leader; + + // Set the last accepted epoch and current epochs to be 1 + peer.setAcceptedEpoch(0); + peer.setCurrentEpoch(0); + + leadThread = new LeadThread(leader); + leadThread.start(); + + while(leader.cnxAcceptor == null || !leader.cnxAcceptor.isAlive()) { + Thread.sleep(20); + } + + leader.shutdown("Shutting down the leader"); + + // Check if there is a valid snapshot (we better not have it) + File snapDir = new File (tmpDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); + List files = Util.sortDataDir(snapDir.listFiles(),"snapshot", false); + + for (File f : files) { + try { + Assert.assertFalse("Found a valid snapshot", Util.isValidSnapshot(f)); + } catch (IOException e) { + LOG.info("invalid snapshot " + f, e); + } + } + + } finally { + if (leader != null) { + leader.shutdown("end of test"); + } + if (leadThread != null) { + leadThread.interrupt(); + leadThread.join(); + } + recursiveDelete(tmpDir); + } + } + private void recursiveDelete(File file) { if (file.isFile()) { file.delete(); From 42dea0dcfe6dd67d2bae6d3dc4646891a0dca3df Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Mon, 21 Oct 2013 21:35:12 +0000 Subject: [PATCH 190/444] ZOOKEEPER-1732. ZooKeeper server unable to join established ensemble (German Blanco via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1534388 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/quorum/FastLeaderElection.java | 30 +++++++-- .../zookeeper/server/quorum/Leader.java | 9 +++ .../zookeeper/server/quorum/Learner.java | 9 +++ .../zookeeper/server/quorum/QuorumPeer.java | 17 +++++ .../org/apache/zookeeper/test/FLETest.java | 63 +++++++++++++++++++ 6 files changed, 124 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b1a697c9473..4cad0ef165e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -137,6 +137,8 @@ BUGFIXES: ZOOKEEPER-1558. Leader should not snapshot uncommitted state (fpj) + ZOOKEEPER-1732. ZooKeeper server unable to join established ensemble (German Blanco via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index ad60bf9d77b..11c0dc7bdab 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -66,6 +66,14 @@ public class FastLeaderElection implements Election { */ final static int maxNotificationInterval = 60000; + + /** + * This value is passed to the methods that check the quorum + * majority of an established ensemble for those values that + * should not be taken into account in the comparison + * (electionEpoch and zxid). + */ + final static int IGNOREVALUE = -1; /** * Connection manager. Fast leader election uses TCP for @@ -324,7 +332,7 @@ public void run() { ToSend.mType.notification, current.getId(), current.getZxid(), - logicalclock, + current.getElectionEpoch(), self.getPeerState(), response.sid, current.getPeerEpoch()); @@ -867,15 +875,25 @@ && checkLeader(outofelection, n.leader, n.electionEpoch)) { } } - /** + /* * Before joining an established ensemble, verify that * a majority are following the same leader. + * Only peer epoch is used to check that the votes come + * from the same ensemble. This is because there is at + * least one corner case in which the ensemble can be + * created with inconsistent zxid and election epoch + * info. However, given that only one ensemble can be + * running at a single point in time and that each + * epoch is used only once, using only the epoch to + * compare the votes is sufficient. + * + * @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732 */ - outofelection.put(n.sid, new Vote(n.leader, n.zxid, - n.electionEpoch, n.peerEpoch, n.state)); + outofelection.put(n.sid, new Vote(n.leader, + IGNOREVALUE, IGNOREVALUE, n.peerEpoch, n.state)); if (termPredicate(outofelection, new Vote(n.leader, - n.zxid, n.electionEpoch, n.peerEpoch, n.state)) - && checkLeader(outofelection, n.leader, n.electionEpoch)) { + IGNOREVALUE, IGNOREVALUE, n.peerEpoch, n.state)) + && checkLeader(outofelection, n.leader, IGNOREVALUE)) { synchronized(this){ logicalclock = n.electionEpoch; self.setPeerState((n.leader == self.getId()) ? diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index b3b1f17c099..dd11a91dc7f 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -945,6 +945,15 @@ private synchronized void startZkServer() { + " ]; starting up and setting last processed zxid: 0x{}", Long.toHexString(zk.getZxid())); zk.startup(); + /* + * Update the election vote here to ensure that all members of the + * ensemble report the same vote to new servers that start up and + * send leader election notifications to the ensemble. + * + * @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732 + */ + self.updateElectionVote(getEpoch()); + zk.getZKDatabase().setlastProcessedZxid(zk.getZxid()); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index 5d9b169a208..46bf31598ec 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -435,6 +435,15 @@ else if (qp.getType() == Leader.SNAP) { writePacket(ack, true); sock.setSoTimeout(self.tickTime * self.syncLimit); zk.startup(); + /* + * Update the election vote here to ensure that all members of the + * ensemble report the same vote to new servers that start up and + * send leader election notifications to the ensemble. + * + * @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732 + */ + self.updateElectionVote(newEpoch); + // We need to log the stuff that came in between the snapshot and the uptodate if (zk instanceof FollowerZooKeeperServer) { FollowerZooKeeperServer fzk = (FollowerZooKeeperServer)zk; 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 882ea54cadd..d6e8bba907b 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -1194,4 +1194,21 @@ public void setAcceptedEpoch(long e) throws IOException { acceptedEpoch = e; writeLongToFile(ACCEPTED_EPOCH_FILENAME, e); } + + /** + * Updates leader election info to avoid inconsistencies when + * a new server tries to join the ensemble. + * See ZOOKEEPER-1732 for more info. + */ + protected void updateElectionVote(long newEpoch) { + Vote currentVote = getCurrentVote(); + if (currentVote != null) { + setCurrentVote(new Vote(currentVote.getId(), + currentVote.getZxid(), + currentVote.getElectionEpoch(), + newEpoch, + currentVote.getState())); + } + } + } diff --git a/src/java/test/org/apache/zookeeper/test/FLETest.java b/src/java/test/org/apache/zookeeper/test/FLETest.java index f1e050d60d7..b9a35d0222a 100644 --- a/src/java/test/org/apache/zookeeper/test/FLETest.java +++ b/src/java/test/org/apache/zookeeper/test/FLETest.java @@ -403,4 +403,67 @@ public void testJoin() throws Exception { } } } + + /* + * For ZOOKEEPER-1732 verify that it is possible to join an ensemble with + * inconsistent election round information. + */ + @Test + public void testJoinInconsistentEnsemble() throws Exception { + int sid; + QuorumPeer peer; + int waitTime = 10 * 1000; + ArrayList peerList = new ArrayList(); + for(sid = 0; sid < 3; sid++) { + peers.put(Long.valueOf(sid), + new QuorumServer(sid, + new InetSocketAddress(PortAssignment.unique()), + new InetSocketAddress(PortAssignment.unique()))); + tmpdir[sid] = ClientBase.createTmpDir(); + port[sid] = PortAssignment.unique(); + } + // start 2 peers and verify if they form the cluster + for (sid = 0; sid < 2; sid++) { + peer = new QuorumPeer(peers, tmpdir[sid], tmpdir[sid], + port[sid], 3, sid, 2000, 2, 2); + LOG.info("Starting peer " + peer.getId()); + peer.start(); + peerList.add(sid, peer); + } + peer = peerList.get(0); + VerifyState v1 = new VerifyState(peerList.get(0)); + v1.start(); + v1.join(waitTime); + Assert.assertFalse("Unable to form cluster in " + + waitTime + " ms", + !v1.isSuccess()); + // Change the election round for one of the members of the ensemble + long leaderSid = peer.getCurrentVote().getId(); + long zxid = peer.getCurrentVote().getZxid(); + long electionEpoch = peer.getCurrentVote().getElectionEpoch(); + ServerState state = peer.getCurrentVote().getState(); + long peerEpoch = peer.getCurrentVote().getPeerEpoch(); + Vote newVote = new Vote(leaderSid, zxid+100, electionEpoch+100, peerEpoch, state); + peer.setCurrentVote(newVote); + // Start 3rd peer and check if it joins the quorum + peer = new QuorumPeer(peers, tmpdir[2], tmpdir[2], + port[2], 3, 2, 2000, 2, 2); + LOG.info("Starting peer " + peer.getId()); + peer.start(); + peerList.add(sid, peer); + v1 = new VerifyState(peer); + v1.start(); + v1.join(waitTime); + if (v1.isAlive()) { + Assert.fail("Peer " + peer.getId() + " failed to join the cluster " + + "within " + waitTime + " ms"); + } + // cleanup + for (int id = 0; id < 3; id++) { + peer = peerList.get(id); + if (peer != null) { + peer.shutdown(); + } + } + } } From 7192b8dac65d27f813f6fcbb5bf8a0e4f57e98e9 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Tue, 22 Oct 2013 10:50:38 +0000 Subject: [PATCH 191/444] ZOOKEEPER-1667. Watch event isn't handled correctly when a client reestablish to a server (jacky007, fpj via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1534598 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 6 +- .../content/xdocs/zookeeperProgrammers.xml | 33 ++- .../org/apache/zookeeper/server/DataTree.java | 44 +--- .../test/WatchEventWhenAutoReset.java | 212 ++++++++++++++++++ 4 files changed, 260 insertions(+), 35 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/WatchEventWhenAutoReset.java diff --git a/CHANGES.txt b/CHANGES.txt index 4cad0ef165e..05a8387ad80 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -137,7 +137,11 @@ BUGFIXES: ZOOKEEPER-1558. Leader should not snapshot uncommitted state (fpj) - ZOOKEEPER-1732. ZooKeeper server unable to join established ensemble (German Blanco via fpj) + ZOOKEEPER-1732. ZooKeeper server unable to join established + ensemble (German Blanco via fpj) + + ZOOKEEPER-1667. Watch event isn't handled correctly when + a client reestablish to a server (jacky007, fpj via fpj) IMPROVEMENTS: diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml index 07792fa4ef4..96f14a13a30 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml @@ -182,7 +182,7 @@ which uses a ZooKeeper service. - Znodes are the main enitity that a programmer access. They have + A znode is the main abstraction a programmer needs to be aware of. Znodes have several characteristics that are worth mentioning here.
    @@ -613,6 +613,37 @@ may be missed: a watch for the existence of a znode not yet created will be missed if the znode is created and deleted while disconnected. +
    + Semantics of Watches + + We can set watches with the three calls that read the state of + ZooKeeper: exists, getData, and getChildren. The following list details + the events that a watch can trigger and the calls that enable them: + + + + + Created event: + Enabled with a call to exists. + + + + Deleted event: + Enabled with a call to exists, getData, and getChildren. + + + + Changed event: + Enabled with a call to exists and getData. + + + + Child event: + Enabled with a call to getChildren. + + +
    +
    What ZooKeeper Guarantees about Watches diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index babd02491aa..df3963a30a5 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -1270,8 +1270,6 @@ public void clear() { root = null; nodes.clear(); ephemerals.clear(); - // dataWatches = null; - // childWatches = null; } public void setWatches(long relativeZxid, List dataWatches, @@ -1279,53 +1277,33 @@ public void setWatches(long relativeZxid, List dataWatches, Watcher watcher) { for (String path : dataWatches) { DataNode node = getNode(path); - WatchedEvent e = null; if (node == null) { - e = new WatchedEvent(EventType.NodeDeleted, - KeeperState.SyncConnected, path); - } else if (node.stat.getCzxid() > relativeZxid) { - e = new WatchedEvent(EventType.NodeCreated, - KeeperState.SyncConnected, path); + watcher.process(new WatchedEvent(EventType.NodeDeleted, + KeeperState.SyncConnected, path)); } else if (node.stat.getMzxid() > relativeZxid) { - e = new WatchedEvent(EventType.NodeDataChanged, - KeeperState.SyncConnected, path); - } - if (e != null) { - watcher.process(e); + watcher.process(new WatchedEvent(EventType.NodeDataChanged, + KeeperState.SyncConnected, path)); } else { this.dataWatches.addWatch(path, watcher); } } for (String path : existWatches) { DataNode node = getNode(path); - WatchedEvent e = null; - if (node == null) { - // This is the case when the watch was registered - } else if (node.stat.getMzxid() > relativeZxid) { - e = new WatchedEvent(EventType.NodeDataChanged, - KeeperState.SyncConnected, path); - } else { - e = new WatchedEvent(EventType.NodeCreated, - KeeperState.SyncConnected, path); - } - if (e != null) { - watcher.process(e); + if (node != null) { + watcher.process(new WatchedEvent(EventType.NodeCreated, + KeeperState.SyncConnected, path)); } else { this.dataWatches.addWatch(path, watcher); } } for (String path : childWatches) { DataNode node = getNode(path); - WatchedEvent e = null; if (node == null) { - e = new WatchedEvent(EventType.NodeDeleted, - KeeperState.SyncConnected, path); + watcher.process(new WatchedEvent(EventType.NodeDeleted, + KeeperState.SyncConnected, path)); } else if (node.stat.getPzxid() > relativeZxid) { - e = new WatchedEvent(EventType.NodeChildrenChanged, - KeeperState.SyncConnected, path); - } - if (e != null) { - watcher.process(e); + watcher.process(new WatchedEvent(EventType.NodeChildrenChanged, + KeeperState.SyncConnected, path)); } else { this.childWatches.addWatch(path, watcher); } diff --git a/src/java/test/org/apache/zookeeper/test/WatchEventWhenAutoReset.java b/src/java/test/org/apache/zookeeper/test/WatchEventWhenAutoReset.java new file mode 100644 index 00000000000..eed02c5845b --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/WatchEventWhenAutoReset.java @@ -0,0 +1,212 @@ +/** + * 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.test; + +import java.io.IOException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.EventType; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import junit.framework.TestCase; + +public class WatchEventWhenAutoReset extends TestCase { + protected static final Logger LOG = LoggerFactory + .getLogger(WatchEventWhenAutoReset.class); + + // waiting time for expected condition + private static final int TIMEOUT = 30000; + + static public class EventsWatcher extends CountdownWatcher { + private LinkedBlockingQueue dataEvents = new LinkedBlockingQueue(); + + @Override + public void process(WatchedEvent event) { + super.process(event); + try { + if (event.getType() != Event.EventType.None) { + dataEvents.put(event); + } + } catch (InterruptedException e) { + LOG.warn("ignoring interrupt during EventsWatcher process"); + } + } + + public void assertEvent(long timeout, EventType eventType) { + try { + WatchedEvent event = dataEvents.poll(timeout, + TimeUnit.MILLISECONDS); + Assert.assertNotNull("do not receive a " + eventType, event); + Assert.assertEquals(eventType, event.getType()); + } catch (InterruptedException e) { + LOG.warn("ignoring interrupt during EventsWatcher assertEvent"); + } + } + } + + private ZooKeeper createClient(QuorumUtil qu, int id, EventsWatcher watcher) + throws IOException { + String hostPort = "127.0.0.1:" + qu.getPeer(id).clientPort; + ZooKeeper zk = new ZooKeeper(hostPort, TIMEOUT, watcher); + try { + watcher.waitForConnected(TIMEOUT); + } catch (InterruptedException e) { + // ignoring the interrupt + } catch (TimeoutException e) { + fail("can not connect to " + hostPort); + } + return zk; + } + + private ZooKeeper createClient(QuorumUtil qu, int id) throws IOException { + return createClient(qu, id, new EventsWatcher()); + } + + @Test + public void testNodeDataChanged() throws Exception { + QuorumUtil qu = new QuorumUtil(1); + qu.startAll(); + + EventsWatcher watcher = new EventsWatcher(); + ZooKeeper zk1 = createClient(qu, 1, watcher); + ZooKeeper zk2 = createClient(qu, 2); + + String path = "/test-changed"; + + zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk1.getData(path, watcher, null); + qu.shutdown(1); + zk2.delete(path, -1); + zk2.create(path, new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + qu.start(1); + watcher.waitForConnected(TIMEOUT); + watcher.assertEvent(TIMEOUT, EventType.NodeDataChanged); + + zk1.exists(path, watcher); + qu.shutdown(1); + zk2.delete(path, -1); + zk2.create(path, new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + qu.start(1); + watcher.waitForConnected(TIMEOUT * 1000L); + watcher.assertEvent(TIMEOUT, EventType.NodeDataChanged); + + qu.shutdownAll(); + } + + @Test + public void testNodeCreated() throws Exception { + QuorumUtil qu = new QuorumUtil(1); + qu.startAll(); + + EventsWatcher watcher = new EventsWatcher(); + ZooKeeper zk1 = createClient(qu, 1, watcher); + ZooKeeper zk2 = createClient(qu, 2); + + String path = "/test1-created"; + + zk1.exists(path, watcher); + qu.shutdown(1); + zk2.create(path, new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + qu.start(1); + watcher.waitForConnected(TIMEOUT * 1000L); + watcher.assertEvent(TIMEOUT, EventType.NodeCreated); + + qu.shutdownAll(); + } + + @Test + public void testNodeDeleted() throws Exception { + QuorumUtil qu = new QuorumUtil(1); + qu.startAll(); + + EventsWatcher watcher = new EventsWatcher(); + ZooKeeper zk1 = createClient(qu, 1, watcher); + ZooKeeper zk2 = createClient(qu, 2); + + String path = "/test-deleted"; + + zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk1.getData(path, watcher, null); + qu.shutdown(1); + zk2.delete(path, -1); + qu.start(1); + watcher.waitForConnected(TIMEOUT * 1000L); + watcher.assertEvent(TIMEOUT, EventType.NodeDeleted); + + zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk1.exists(path, watcher); + qu.shutdown(1); + zk2.delete(path, -1); + qu.start(1); + watcher.waitForConnected(TIMEOUT * 1000L); + watcher.assertEvent(TIMEOUT, EventType.NodeDeleted); + + zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk1.getChildren(path, watcher); + qu.shutdown(1); + zk2.delete(path, -1); + qu.start(1); + watcher.waitForConnected(TIMEOUT * 1000L); + watcher.assertEvent(TIMEOUT, EventType.NodeDeleted); + + qu.shutdownAll(); + } + + @Test + public void testNodeChildrenChanged() throws Exception { + QuorumUtil qu = new QuorumUtil(1); + qu.startAll(); + + EventsWatcher watcher = new EventsWatcher(); + ZooKeeper zk1 = createClient(qu, 1, watcher); + ZooKeeper zk2 = createClient(qu, 2); + + String path = "/test-children-changed"; + + zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk1.getChildren(path, watcher); + qu.shutdown(1); + zk2.create(path + "/children-1", new byte[2], + ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + qu.start(1); + watcher.waitForConnected(TIMEOUT * 1000L); + watcher.assertEvent(TIMEOUT, EventType.NodeChildrenChanged); + + qu.shutdownAll(); + } +} + From b410a1f3a0323f7fe9046f6c11f4913eb1a3d947 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 22 Oct 2013 23:16:34 +0000 Subject: [PATCH 192/444] ZOOKEEPER-1799. SaslAuthFailDesignatedClientTest.testAuth fails frequently on SUSE (Jeffrey Zhong via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1534843 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../test/SaslAuthFailDesignatedClientTest.java | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 05a8387ad80..39d60e03ced 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -143,6 +143,9 @@ BUGFIXES: ZOOKEEPER-1667. Watch event isn't handled correctly when a client reestablish to a server (jacky007, fpj via fpj) + ZOOKEEPER-1799. SaslAuthFailDesignatedClientTest.testAuth fails + frequently on SUSE (Jeffrey Zhong via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java index b6e166aae8e..5291141cf8f 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java @@ -21,10 +21,12 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.Watcher.Event.KeeperState; @@ -84,7 +86,14 @@ public synchronized void process(WatchedEvent event) { @Test public void testAuth() throws Exception { - ZooKeeper zk = createClient(); + // Cannot use createClient here because server may close session before + // JMXEnv.ensureAll is called which will fail the test case + CountdownWatcher watcher = new CountdownWatcher(); + TestableZooKeeper zk = new TestableZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); + if (!watcher.clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) + { + Assert.fail("Unable to connect to server"); + } try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Assert.fail("Should have gotten exception."); From e0d3b8fc689a6cbc6fbd111a2707b1af08fd0b94 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 22 Oct 2013 23:25:07 +0000 Subject: [PATCH 193/444] ZOOKEEPER-1180 New entry for files ignored by svn (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1534845 13f79535-47bb-0310-9956-ffa450edef68 From 8aa5794e74f469088d3c77035f17c120c53026ab Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 24 Oct 2013 01:10:07 +0000 Subject: [PATCH 194/444] ZOOKEEPER-1557. jenkins jdk7 test failure in testBadSaslAuthNotifiesWatch (Eugene Koontz via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1535252 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../test/SaslAuthFailNotifyTest.java | 98 +++++++++++++++++++ .../zookeeper/test/SaslAuthFailTest.java | 37 ------- 3 files changed, 101 insertions(+), 37 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/SaslAuthFailNotifyTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 39d60e03ced..1530a519b06 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -146,6 +146,9 @@ BUGFIXES: ZOOKEEPER-1799. SaslAuthFailDesignatedClientTest.testAuth fails frequently on SUSE (Jeffrey Zhong via phunt) + ZOOKEEPER-1557. jenkins jdk7 test failure in + testBadSaslAuthNotifiesWatch (Eugene Koontz via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailNotifyTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailNotifyTest.java new file mode 100644 index 00000000000..2b00d862dfe --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailNotifyTest.java @@ -0,0 +1,98 @@ +/** + * 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.test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.TestableZooKeeper; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs.Ids; +import org.junit.Test; +import org.junit.Assert; + +public class SaslAuthFailNotifyTest extends ClientBase { + static { + System.setProperty("zookeeper.authProvider.1","org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); + System.setProperty("zookeeper.allowSaslFailedClients","true"); + + try { + File tmpDir = createTmpDir(); + File saslConfFile = new File(tmpDir, "jaas.conf"); + FileWriter fwriter = new FileWriter(saslConfFile); + + fwriter.write("" + + "Server {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_super=\"test\";\n" + + "};\n" + + "Client {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"super\"\n" + + " password=\"test1\";\n" + // NOTE: wrong password ('test' != 'test1') : this is to test SASL authentication failure. + "};" + "\n"); + fwriter.close(); + System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); + } + catch (IOException e) { + // could not create tmp directory to hold JAAS conf file. + } + } + + private AtomicInteger authFailed = new AtomicInteger(0); + + @Override + protected TestableZooKeeper createClient(String hp) + throws IOException, InterruptedException + { + MyWatcher watcher = new MyWatcher(); + return createClient(watcher, hp); + } + + private class MyWatcher extends CountdownWatcher { + @Override + public synchronized void process(WatchedEvent event) { + if (event.getState() == KeeperState.AuthFailed) { + synchronized(authFailed) { + authFailed.incrementAndGet(); + authFailed.notify(); + } + } + else { + super.process(event); + } + } + } + + @Test + public void testBadSaslAuthNotifiesWatch() throws Exception { + ZooKeeper zk = createClient(); + // wait for authFailed event from client's EventThread. + synchronized(authFailed) { + authFailed.wait(); + } + Assert.assertEquals(authFailed.get(),1); + zk.close(); + } +} diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java index 1589b1f513d..33a505ea2a7 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java +++ b/src/java/test/org/apache/zookeeper/test/SaslAuthFailTest.java @@ -59,43 +59,6 @@ public class SaslAuthFailTest extends ClientBase { // could not create tmp directory to hold JAAS conf file. } } - - private AtomicInteger authFailed = new AtomicInteger(0); - - @Override - protected TestableZooKeeper createClient(String hp) - throws IOException, InterruptedException - { - MyWatcher watcher = new MyWatcher(); - return createClient(watcher, hp); - } - - private class MyWatcher extends CountdownWatcher { - @Override - public synchronized void process(WatchedEvent event) { - if (event.getState() == KeeperState.AuthFailed) { - synchronized(authFailed) { - authFailed.incrementAndGet(); - authFailed.notify(); - } - } - else { - super.process(event); - } - } - } - - @Test - public void testBadSaslAuthNotifiesWatch() throws Exception { - ZooKeeper zk = createClient(); - // wait for authFailed event from client's EventThread. - synchronized(authFailed) { - authFailed.wait(); - } - Assert.assertEquals(authFailed.get(),1); - zk.close(); - } - @Test public void testAuthFail() throws Exception { From a752eee7837d241d79151fa6053571df7a00761e Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 24 Oct 2013 05:11:59 +0000 Subject: [PATCH 195/444] ZOOKEEPER-1744. clientPortAddress breaks "zkServer.sh status" (Nick Ohanian via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1535279 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ bin/zkServer.sh | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1530a519b06..e4cede18a68 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -149,6 +149,9 @@ BUGFIXES: ZOOKEEPER-1557. jenkins jdk7 test failure in testBadSaslAuthNotifiesWatch (Eugene Koontz via phunt) + ZOOKEEPER-1744. clientPortAddress breaks "zkServer.sh status" + (Nick Ohanian via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/bin/zkServer.sh b/bin/zkServer.sh index c1880b1aecd..0490982760a 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -161,9 +161,15 @@ restart) ;; status) # -q is necessary on some versions of linux where nc returns too quickly, and no stat result is output + clientPortAddress=`grep "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` + if ! [ $clientPortAddress ] + then + clientPortAddress="localhost" + fi + clientPort=`grep "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain localhost \ - $(grep "^[[:space:]]*clientPort" "$ZOOCFG" | sed -e 's/.*=//') srvr 2> /dev/null \ + -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \ + $clientPortAddress $clientPort srvr 2> /dev/null \ | grep Mode` if [ "x$STAT" = "x" ] then From 3dae9c4770f77e28fa6bba07e58133f1530f381a Mon Sep 17 00:00:00 2001 From: Thawan Kooburat Date: Thu, 24 Oct 2013 18:52:31 +0000 Subject: [PATCH 196/444] ZOOKEEPER-1798. Fix race condition in testNormalObserverRun (thawan, fpj via thawan) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1535497 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/server/SyncRequestProcessor.java | 7 +++++++ .../org/apache/zookeeper/server/quorum/Zab1_0Test.java | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index e4cede18a68..5f65850f438 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -152,6 +152,9 @@ BUGFIXES: ZOOKEEPER-1744. clientPortAddress breaks "zkServer.sh status" (Nick Ohanian via phunt) + ZOOKEEPER-1798. Fix race condition in testNormalObserverRun + (thawan, fpj via thawan) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java index 9129b00b766..92999614766 100644 --- a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java @@ -212,8 +212,15 @@ public void shutdown() { if(running){ this.join(); } + if (!toFlush.isEmpty()) { + flush(toFlush); + } } catch(InterruptedException e) { LOG.warn("Interrupted while wating for " + this + " to finish"); + } catch (IOException e) { + LOG.warn("Got IO exception during shutdown"); + } catch (RequestProcessorException e) { + LOG.warn("Got request processor exception during shutdown"); } if (nextProcessor != null) { nextProcessor.shutdown(); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 9975d568e10..41fcce97e52 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -1113,6 +1113,10 @@ public void converseWithObserver(InputArchive ia, OutputArchive oa, Assert.assertEquals("data2", new String(o.zk .getZKDatabase().getData("/foo2", stat, null))); + // Shutdown sequence guarantee that all pending requests + // in sync request processor get flush to disk + o.zk.shutdown(); + zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); lastZxid = zkDb2.loadDataBase(); Assert.assertEquals("data2", new String(zkDb2.getData("/foo1", stat, null))); From 2bdf4678c70ff7639761c459708922cb2309b078 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 12 Nov 2013 06:30:06 +0000 Subject: [PATCH 197/444] ZOOKEEPER-1812. ZooInspector reconnection always fails if first connection fails (Benjamin Jaton via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1540960 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../inspector/manager/ZooInspectorManagerImpl.java | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5f65850f438..6503a303e4d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -155,6 +155,9 @@ BUGFIXES: ZOOKEEPER-1798. Fix race condition in testNormalObserverRun (thawan, fpj via thawan) + ZOOKEEPER-1812. ZooInspector reconnection always fails if first + connection fails (Benjamin Jaton via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java index 6dd3e8a21d4..1937d260f5e 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java @@ -173,12 +173,14 @@ public void process(WatchedEvent event) { } ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10); connected = ((ZooKeeperRetry) this.zooKeeper).testConnection(); - return connected; } } catch (Exception e) { + connected = false; e.printStackTrace(); } - connected = false; + if (!connected){ + disconnect(); + } return connected; } From ffd23df5f0f5038bee5c3b05f54f12a80f8098b1 Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Wed, 13 Nov 2013 02:22:39 +0000 Subject: [PATCH 198/444] ZOOKEEPER-1666. Avoid Reverse DNS lookup if the hostname in connection string is literal IP address. (George Cao via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1541359 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 +++ .../zookeeper/client/StaticHostProvider.java | 20 +++++++++-- .../test/StaticHostProviderTest.java | 34 ++++++++++++++++++- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6503a303e4d..57da442f4aa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -186,6 +186,10 @@ IMPROVEMENTS: ZOOKEEPER-1627. Add org.apache.zookeeper.common to exported packages in OSGi MANIFEST (Arnoud Glimmerveen via phunt) + + ZOOKEEPER-1666. Avoid Reverse DNS lookup if the hostname in + connection string is literal IP address. (George Cao via camille) + Release 3.4.5 - 2012-09-30 diff --git a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java index 959fe89f5f5..e207963fd66 100644 --- a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java +++ b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java @@ -61,11 +61,25 @@ public StaticHostProvider(Collection serverAddresses) InetAddress resolvedAddresses[] = InetAddress.getAllByName((ia!=null) ? ia.getHostAddress(): address.getHostName()); for (InetAddress resolvedAddress : resolvedAddresses) { - this.serverAddresses.add(new InetSocketAddress(resolvedAddress - .getHostAddress(), address.getPort())); + // If hostName is null but the address is not, we can tell that + // the hostName is an literal IP address. Then we can set the host string as the hostname + // safely to avoid reverse DNS lookup. + // As far as i know, the only way to check if the hostName is null is use toString(). + // Both the two implementations of InetAddress are final class, so we can trust the return value of + // the toString() method. + if (resolvedAddress.toString().startsWith("/") + && resolvedAddress.getAddress() != null) { + this.serverAddresses.add( + new InetSocketAddress(InetAddress.getByAddress( + address.getHostName(), + resolvedAddress.getAddress()), + address.getPort())); + } else { + this.serverAddresses.add(new InetSocketAddress(resolvedAddress.getHostAddress(), address.getPort())); + } } } - + if (this.serverAddresses.isEmpty()) { throw new IllegalArgumentException( "A HostProvider may not be empty!"); diff --git a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java index b6b3e9ee631..75d3c5901d2 100644 --- a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java +++ b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java @@ -35,10 +35,12 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; public class StaticHostProviderTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(StaticHostProviderTest.class); - + @Test public void testNextGoesRound() throws UnknownHostException { HostProvider hostProvider = getHostProvider((byte) 2); @@ -92,6 +94,36 @@ public void testOnConnectDoesNotReset() throws UnknownHostException { assertNotSame(first, second); } + @Test + public void testLiteralIPNoReverseNS() throws Exception { + byte size = 30; + HostProvider hostProvider = getHostProviderUnresolved(size); + for (int i = 0; i < size; i++) { + InetSocketAddress next = hostProvider.next(0); + assertTrue(next instanceof InetSocketAddress); + assertTrue(!next.isUnresolved()); + assertTrue("Did not match "+ next.toString(), !next.toString().startsWith("/")); + // Do NOT trigger the reverse name service lookup. + String hostname = next.getHostName(); + // In this case, the hostname equals literal IP address. + hostname.equals(next.getAddress().getHostAddress()); + } + } + + private StaticHostProvider getHostProviderUnresolved(byte size) + throws UnknownHostException { + return new StaticHostProvider(getUnresolvedServerAddresses(size)); + } + + private Collection getUnresolvedServerAddresses(byte size) { + ArrayList list = new ArrayList(size); + while (size > 0) { + list.add(InetSocketAddress.createUnresolved("10.10.10." + size, 1234 + size)); + --size; + } + return list; + } + private StaticHostProvider getHostProvider(byte size) throws UnknownHostException { ArrayList list = new ArrayList( From bf1814b9943588ac25b46aa158d994f5900b9202 Mon Sep 17 00:00:00 2001 From: Thawan Kooburat Date: Thu, 14 Nov 2013 04:43:46 +0000 Subject: [PATCH 199/444] ZOOKEEPER-1798. Fix race condition in testNormalObserverRun (Part 2) (thawan, fpj via thawan) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1541814 13f79535-47bb-0310-9956-ffa450edef68 --- .../zookeeper/server/quorum/ObserverZooKeeperServer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java index 48600e47952..4359b8dd2d0 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java @@ -136,4 +136,12 @@ synchronized public void sync(){ public String getState() { return "observer"; }; + + @Override + public void shutdown() { + super.shutdown(); + if (syncRequestProcessorEnabled && syncProcessor != null) { + syncProcessor.shutdown(); + } + } } From 6503f12e144bc6b0652f7adf558bdc28c984e6c0 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 15 Nov 2013 18:12:07 +0000 Subject: [PATCH 200/444] ZOOKEEPER-1786. ZooKeeper data model documentation is incorrect (Niraj Tolia via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1542356 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 7 +++++-- .../documentation/content/xdocs/zookeeperProgrammers.xml | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 57da442f4aa..8fbffb906c0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -172,7 +172,8 @@ IMPROVEMENTS: ZOOKEEPER-1584. Adding mvn-install target for deploying the zookeeper artifacts to .m2 repository (Ashish Singh via phunt) - ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the Leader to the Follower. (thawan, fpj via fpj) + ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the + Leader to the Follower. (thawan, fpj via fpj) ZOOKEEPER-1615. minor typos in ZooKeeper Programmer's Guide web page (Evan Zacks via phunt) @@ -189,7 +190,9 @@ IMPROVEMENTS: ZOOKEEPER-1666. Avoid Reverse DNS lookup if the hostname in connection string is literal IP address. (George Cao via camille) - + + ZOOKEEPER-1786. ZooKeeper data model documentation is incorrect + (Niraj Tolia via fpj) Release 3.4.5 - 2012-09-30 diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml index 96f14a13a30..13642538fcd 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml @@ -139,8 +139,7 @@ The following characters are not allowed: \ud800 -uF8FFF, - \uFFF0-uFFFF, \uXFFFE - \uXFFFF (where X is a digit 1 - E), \uF0000 - - \uFFFFF. + \uFFF0 - uFFFF. From a452d791cff92d65937142c3475a9894264376f6 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 16 Nov 2013 10:06:19 +0000 Subject: [PATCH 201/444] ZOOKEEPER-1808. Add version to FLE notifications for 3.4 branch (fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1542489 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/quorum/FastLeaderElection.java | 85 +++-- .../server/quorum/QuorumCnxManager.java | 3 +- .../apache/zookeeper/server/quorum/Vote.java | 49 ++- .../server/quorum/CnxManagerTest.java | 15 +- .../quorum}/FLEBackwardElectionRoundTest.java | 13 +- .../server/quorum/FLECompatibilityTest.java | 350 ++++++++++++++++++ .../quorum}/FLELostMessageTest.java | 7 +- .../{test => server/quorum}/FLETestUtils.java | 18 +- .../zookeeper/test/LENonTerminateTest.java | 1 + 10 files changed, 473 insertions(+), 70 deletions(-) rename src/java/test/org/apache/zookeeper/{test => server/quorum}/FLEBackwardElectionRoundTest.java (90%) create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java rename src/java/test/org/apache/zookeeper/{test => server/quorum}/FLELostMessageTest.java (96%) rename src/java/test/org/apache/zookeeper/{test => server/quorum}/FLETestUtils.java (85%) diff --git a/CHANGES.txt b/CHANGES.txt index 8fbffb906c0..9c31ab97ccd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -155,6 +155,8 @@ BUGFIXES: ZOOKEEPER-1798. Fix race condition in testNormalObserverRun (thawan, fpj via thawan) + ZOOKEEPER-1808. Add version to FLE notifications for 3.4 branch (fpj) + ZOOKEEPER-1812. ZooInspector reconnection always fails if first connection fails (Benjamin Jaton via phunt) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index 11c0dc7bdab..d3dc665c8d4 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -92,6 +92,13 @@ public class FastLeaderElection implements Election { */ static public class Notification { + /* + * Format version, introduced in 3.4.6 + */ + + public final static int CURRENTVERSION = 0x1; + int version; + /* * Proposed leader */ @@ -121,6 +128,39 @@ static public class Notification { * epoch of the proposed leader */ long peerEpoch; + + @Override + public String toString() { + return new String(Long.toHexString(version) + " (message format version), " + + leader + " (n.leader), 0x" + + Long.toHexString(zxid) + " (n.zxid), 0x" + + Long.toHexString(electionEpoch) + " (n.round), " + state + + " (n.state), " + sid + " (n.sid), 0x" + + Long.toHexString(peerEpoch) + " (n.peerEpoch) "); + } + } + + static ByteBuffer buildMsg(int state, + long leader, + long zxid, + long electionEpoch, + long epoch) { + byte requestBytes[] = new byte[40]; + ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); + + /* + * Building notification packet to send + */ + + requestBuffer.clear(); + requestBuffer.putInt(state); + requestBuffer.putLong(leader); + requestBuffer.putLong(zxid); + requestBuffer.putLong(electionEpoch); + requestBuffer.putLong(epoch); + requestBuffer.putInt(Notification.CURRENTVERSION); + + return requestBuffer; } /** @@ -188,7 +228,7 @@ static enum mType {crequest, challenge, notification, ack} * spawns a new thread. */ - private class Messenger { + protected class Messenger { /** * Receives messages from instance of QuorumCnxManager on @@ -250,6 +290,9 @@ public void run() { boolean backCompatibility = (response.buffer.capacity() == 28); response.buffer.clear(); + // Instantiate Notification and set its attributes + Notification n = new Notification(); + // State of peer that sent this message QuorumPeer.ServerState ackstate = QuorumPeer.ServerState.LOOKING; switch (response.buffer.getInt()) { @@ -265,10 +308,10 @@ public void run() { case 3: ackstate = QuorumPeer.ServerState.OBSERVING; break; + default: + continue; } - - // Instantiate Notification and set its attributes - Notification n = new Notification(); + n.leader = response.buffer.getLong(); n.zxid = response.buffer.getLong(); n.electionEpoch = response.buffer.getLong(); @@ -283,6 +326,12 @@ public void run() { n.peerEpoch = ZxidUtils.getEpochFromZxid(n.zxid); } + /* + * Version added in 3.4.6 + */ + + n.version = (response.buffer.remaining() >= 4) ? response.buffer.getInt() : 0x0; + /* * Print notification info */ @@ -383,23 +432,13 @@ public void run() { * * @param m message to send */ - private void process(ToSend m) { - byte requestBytes[] = new byte[36]; - ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); - - /* - * Building notification packet to send - */ - - requestBuffer.clear(); - requestBuffer.putInt(m.state.ordinal()); - requestBuffer.putLong(m.leader); - requestBuffer.putLong(m.zxid); - requestBuffer.putLong(m.electionEpoch); - requestBuffer.putLong(m.peerEpoch); - + void process(ToSend m) { + ByteBuffer requestBuffer = buildMsg(m.state.ordinal(), + m.leader, + m.zxid, + m.electionEpoch, + m.peerEpoch); manager.toSend(m.sid, requestBuffer); - } } @@ -547,11 +586,7 @@ private void sendNotifications() { private void printNotification(Notification n){ - LOG.info("Notification: " + n.leader + " (n.leader), 0x" - + Long.toHexString(n.zxid) + " (n.zxid), 0x" - + Long.toHexString(n.electionEpoch) + " (n.round), " + n.state - + " (n.state), " + n.sid + " (n.sid), 0x" - + Long.toHexString(n.peerEpoch) + " (n.peerEPoch), " + LOG.info("Notification: " + n.toString() + self.getPeerState() + " (my state)"); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 1d47a234563..eacea312b27 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -72,7 +72,7 @@ public class QuorumCnxManager { // stale notifications to peers static final int SEND_CAPACITY = 1; - static final int PACKETMAXSIZE = 1024 * 1024; + static final int PACKETMAXSIZE = 1024 * 512; /* * Maximum number of attempts to connect to a peer */ @@ -129,6 +129,7 @@ public class QuorumCnxManager { private AtomicInteger threadCnt = new AtomicInteger(0); static public class Message { + Message(ByteBuffer buffer, long sid) { this.buffer = buffer; this.sid = sid; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Vote.java b/src/java/main/org/apache/zookeeper/server/quorum/Vote.java index b374f1bd00f..82639e09418 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Vote.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Vote.java @@ -23,7 +23,9 @@ public class Vote { - public Vote(long id, long zxid) { + public Vote(long id, + long zxid) { + this.version = 0x0; this.id = id; this.zxid = zxid; this.electionEpoch = -1; @@ -31,7 +33,10 @@ public Vote(long id, long zxid) { this.state = ServerState.LOOKING; } - public Vote(long id, long zxid, long peerEpoch) { + public Vote(long id, + long zxid, + long peerEpoch) { + this.version = 0x0; this.id = id; this.zxid = zxid; this.electionEpoch = -1; @@ -39,7 +44,11 @@ public Vote(long id, long zxid, long peerEpoch) { this.state = ServerState.LOOKING; } - public Vote(long id, long zxid, long electionEpoch, long peerEpoch) { + public Vote(long id, + long zxid, + long electionEpoch, + long peerEpoch) { + this.version = 0x0; this.id = id; this.zxid = zxid; this.electionEpoch = electionEpoch; @@ -47,7 +56,13 @@ public Vote(long id, long zxid, long electionEpoch, long peerEpoch) { this.state = ServerState.LOOKING; } - public Vote(long id, long zxid, long electionEpoch, long peerEpoch, ServerState state) { + public Vote(int version, + long id, + long zxid, + long electionEpoch, + long peerEpoch, + ServerState state) { + this.version = version; this.id = id; this.zxid = zxid; this.electionEpoch = electionEpoch; @@ -55,6 +70,21 @@ public Vote(long id, long zxid, long electionEpoch, long peerEpoch, ServerState this.peerEpoch = peerEpoch; } + public Vote(long id, + long zxid, + long electionEpoch, + long peerEpoch, + ServerState state) { + this.id = id; + this.zxid = zxid; + this.electionEpoch = electionEpoch; + this.state = state; + this.peerEpoch = peerEpoch; + this.version = 0x0; + } + + final private int version; + final private long id; final private long zxid; @@ -63,6 +93,10 @@ public Vote(long id, long zxid, long electionEpoch, long peerEpoch, ServerState final private long peerEpoch; + public int getVersion() { + return version; + } + public long getId() { return id; } @@ -91,10 +125,13 @@ public boolean equals(Object o) { return false; } Vote other = (Vote) o; - return (id == other.id && zxid == other.zxid && electionEpoch == other.electionEpoch && peerEpoch == other.peerEpoch); + return (id == other.id + && zxid == other.zxid + && electionEpoch == other.electionEpoch + && peerEpoch == other.peerEpoch); } - + @Override public int hashCode() { return (int) (id & zxid); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java index 866f8d0e2d8..e8fdf6ba527 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java @@ -74,20 +74,7 @@ public void setUp() throws Exception { } ByteBuffer createMsg(int state, long leader, long zxid, long epoch){ - byte requestBytes[] = new byte[28]; - ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); - - /* - * Building notification packet to send - */ - - requestBuffer.clear(); - requestBuffer.putInt(state); - requestBuffer.putLong(leader); - requestBuffer.putLong(zxid); - requestBuffer.putLong(epoch); - - return requestBuffer; + return FastLeaderElection.buildMsg(state, leader, zxid, 0, epoch); } class CnxManagerThread extends Thread { diff --git a/src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java similarity index 90% rename from src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java rename to src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java index 7443c24d15d..3eeb0d32d7a 100644 --- a/src/java/test/org/apache/zookeeper/test/FLEBackwardElectionRoundTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.zookeeper.test; +package org.apache.zookeeper.server.quorum; import java.io.File; import java.io.IOException; @@ -32,12 +32,12 @@ import org.apache.zookeeper.server.quorum.Vote; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.apache.zookeeper.test.ClientBase; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.apache.zookeeper.test.FLETestUtils.LEThread; public class FLEBackwardElectionRoundTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLELostMessageTest.class); @@ -118,7 +118,8 @@ public void testBackwardElectionRound() throws Exception { QuorumCnxManager.Listener listener = cnxManagers[0].listener; listener.start(); - cnxManagers[0].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + ByteBuffer msg = FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1); + cnxManagers[0].toSend(0l, msg); /* * Start mock server 2 @@ -128,7 +129,7 @@ public void testBackwardElectionRound() throws Exception { listener = cnxManagers[1].listener; listener.start(); - cnxManagers[1].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + cnxManagers[1].toSend(0l, msg); /* * Run another instance of leader election. @@ -140,8 +141,8 @@ public void testBackwardElectionRound() throws Exception { /* * Send the same messages, this time should not make 0 the leader. */ - cnxManagers[0].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); - cnxManagers[1].toSend(0l, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1)); + cnxManagers[0].toSend(0l, msg); + cnxManagers[1].toSend(0l, msg); thread.join(5000); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java new file mode 100644 index 00000000000..cf5c0e08ee7 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java @@ -0,0 +1,350 @@ +/** + * 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.server.quorum; + +import java.io.File; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.server.quorum.FastLeaderElection.Notification; +import org.apache.zookeeper.server.quorum.FastLeaderElection.ToSend; +import org.apache.zookeeper.server.quorum.FastLeaderElection.Messenger.WorkerReceiver; +import org.apache.zookeeper.server.quorum.QuorumCnxManager.Message; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.apache.zookeeper.server.util.ZxidUtils; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.FLETest; +import org.apache.zookeeper.test.QuorumBase; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class FLECompatibilityTest extends ZKTestCase { + protected static final Logger LOG = LoggerFactory.getLogger(FLECompatibilityTest.class); + + int count; + HashMap peers; + File tmpdir[]; + int port[]; + + @Before + public void setUp() throws Exception { + count = 3; + peers = new HashMap(count); + tmpdir = new File[count]; + port = new int[count]; + } + + @After + public void tearDown() throws Exception { + + } + + class MockFLEMessengerBackward { + QuorumCnxManager manager; + QuorumPeer self; + long logicalclock = 1L; + LinkedBlockingQueue sendqueue = new LinkedBlockingQueue(); + LinkedBlockingQueue internalqueue = new LinkedBlockingQueue(); + LinkedBlockingQueue recvqueue = new LinkedBlockingQueue(); + WorkerReceiver wr; + + MockFLEMessengerBackward(QuorumPeer self, QuorumCnxManager manager){ + this.manager = manager; + this.self = self; + + this.wr = new WorkerReceiver(manager); + + Thread t = new Thread(this.wr, + "WorkerReceiver[myid=" + self.getId() + "]"); + t.setDaemon(true); + t.start(); + } + + void halt() { + wr.stop = true; + } + + /* + * This class has been copied from before adding versions to notifications. + * + * {@see https://issues.apache.org/jira/browse/ZOOKEEPER-1808} + */ + class WorkerReceiver implements Runnable { + volatile boolean stop; + QuorumCnxManager manager; + final long proposedLeader = 2; + final long proposedZxid = 0x1; + final long proposedEpoch = 1; + + WorkerReceiver(QuorumCnxManager manager) { + this.stop = false; + this.manager = manager; + } + + /* + * The vote we return here is fixed for test purposes. + */ + Vote getVote(){ + return new Vote(proposedLeader, proposedZxid, proposedEpoch); + } + + public void run() { + + Message response; + while (!stop) { + // Sleeps on receive + try{ + response = manager.pollRecvQueue(3000, TimeUnit.MILLISECONDS); + if(response == null) continue; + + /* + * If it is from an observer, respond right away. + * Note that the following predicate assumes that + * if a server is not a follower, then it must be + * an observer. If we ever have any other type of + * learner in the future, we'll have to change the + * way we check for observers. + */ + if(!self.getVotingView().containsKey(response.sid)){ + Vote current = self.getCurrentVote(); + ToSend notmsg = new ToSend(ToSend.mType.notification, + current.getId(), + current.getZxid(), + logicalclock, + self.getPeerState(), + response.sid, + current.getPeerEpoch()); + + internalqueue.offer(notmsg); + } else { + // Receive new message + if (LOG.isDebugEnabled()) { + LOG.debug("Receive new notification message. My id = " + + self.getId()); + } + + /* + * We check for 28 bytes for backward compatibility + */ + if (response.buffer.capacity() < 28) { + LOG.error("Got a short response: " + + response.buffer.capacity()); + continue; + } + boolean backCompatibility = (response.buffer.capacity() == 28); + response.buffer.clear(); + + // State of peer that sent this message + QuorumPeer.ServerState ackstate = QuorumPeer.ServerState.LOOKING; + switch (response.buffer.getInt()) { + case 0: + ackstate = QuorumPeer.ServerState.LOOKING; + break; + case 1: + ackstate = QuorumPeer.ServerState.FOLLOWING; + break; + case 2: + ackstate = QuorumPeer.ServerState.LEADING; + break; + case 3: + ackstate = QuorumPeer.ServerState.OBSERVING; + break; + } + + // Instantiate Notification and set its attributes + Notification n = new Notification(); + n.leader = response.buffer.getLong(); + n.zxid = response.buffer.getLong(); + n.electionEpoch = response.buffer.getLong(); + n.state = ackstate; + n.sid = response.sid; + if(!backCompatibility){ + n.peerEpoch = response.buffer.getLong(); + } else { + if(LOG.isInfoEnabled()){ + LOG.info("Backward compatibility mode, server id=" + n.sid); + } + n.peerEpoch = ZxidUtils.getEpochFromZxid(n.zxid); + } + + /* + * If this server is looking, then send proposed leader + */ + + if(self.getPeerState() == QuorumPeer.ServerState.LOOKING){ + recvqueue.offer(n); + + /* + * Send a notification back if the peer that sent this + * message is also looking and its logical clock is + * lagging behind. + */ + if((ackstate == QuorumPeer.ServerState.LOOKING) + && (n.electionEpoch < logicalclock)){ + Vote v = getVote(); + ToSend notmsg = new ToSend(ToSend.mType.notification, + v.getId(), + v.getZxid(), + logicalclock, + self.getPeerState(), + response.sid, + v.getPeerEpoch()); + internalqueue.offer(notmsg); + } + } else { + /* + * If this server is not looking, but the one that sent the ack + * is looking, then send back what it believes to be the leader. + */ + Vote current = self.getCurrentVote(); + if(ackstate == QuorumPeer.ServerState.LOOKING){ + if(LOG.isDebugEnabled()){ + LOG.debug("Sending new notification. My id = " + + self.getId() + " recipient=" + + response.sid + " zxid=0x" + + Long.toHexString(current.getZxid()) + + " leader=" + current.getId()); + } + ToSend notmsg = new ToSend( + ToSend.mType.notification, + current.getId(), + current.getZxid(), + current.getElectionEpoch(), + self.getPeerState(), + response.sid, + current.getPeerEpoch()); + internalqueue.offer(notmsg); + } + } + } + } catch (InterruptedException e) { + System.out.println("Interrupted Exception while waiting for new message" + + e.toString()); + } + } + LOG.info("WorkerReceiver is down"); + } + } + } + + class MockFLEMessengerForward extends FastLeaderElection { + + MockFLEMessengerForward(QuorumPeer self, QuorumCnxManager manager){ + super( self, manager ); + } + + void halt() { + super.shutdown(); + } + } + + void populate() + throws Exception { + for (int i = 0; i < count; i++) { + peers.put(Long.valueOf(i), + new QuorumServer(i, + new InetSocketAddress(PortAssignment.unique()), + new InetSocketAddress(PortAssignment.unique()))); + tmpdir[i] = ClientBase.createTmpDir(); + port[i] = PortAssignment.unique(); + } + } + + @Test(timeout=20000) + public void testBackwardCompatibility() + throws Exception { + populate(); + + QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); + peer.setPeerState(ServerState.LOOKING); + QuorumCnxManager mng = new QuorumCnxManager(peer); + + /* + * Check that it generates an internal notification correctly + */ + MockFLEMessengerBackward fle = new MockFLEMessengerBackward(peer, mng); + ByteBuffer buffer = FastLeaderElection.buildMsg(ServerState.LOOKING.ordinal(), 2, 0x1, 1, 1); + fle.manager.recvQueue.add(new Message(buffer, 2)); + Notification n = fle.recvqueue.take(); + Assert.assertTrue("Wrong state", n.state == ServerState.LOOKING); + Assert.assertTrue("Wrong leader", n.leader == 2); + Assert.assertTrue("Wrong zxid", n.zxid == 0x1); + Assert.assertTrue("Wrong epoch", n.electionEpoch == 1); + Assert.assertTrue("Wrong epoch", n.peerEpoch == 1); + + /* + * Check that it sends a notification back to the sender + */ + peer.setPeerState(ServerState.FOLLOWING); + peer.setCurrentVote( new Vote(2, 0x1, 1, 1, ServerState.LOOKING) ); + buffer = FastLeaderElection.buildMsg(ServerState.LOOKING.ordinal(), 1, 0x1, 1, 1); + fle.manager.recvQueue.add(new Message(buffer, 1)); + ToSend m = fle.internalqueue.take(); + Assert.assertTrue("Wrong state", m.state == ServerState.FOLLOWING); + Assert.assertTrue("Wrong sid", m.sid == 1); + Assert.assertTrue("Wrong leader", m.leader == 2); + Assert.assertTrue("Wrong epoch", m.electionEpoch == 1); + Assert.assertTrue("Wrong epoch", m.peerEpoch == 1); + } + + @Test(timeout=20000) + public void testForwardCompatibility() + throws Exception { + populate(); + + QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); + peer.setPeerState(ServerState.LOOKING); + QuorumCnxManager mng = new QuorumCnxManager(peer); + + /* + * Check that it generates an internal notification correctly + */ + MockFLEMessengerForward fle = new MockFLEMessengerForward(peer, mng); + ByteBuffer notBuffer = FastLeaderElection.buildMsg(ServerState.LOOKING.ordinal(), 2, 0x1, 1, 1); + ByteBuffer buffer = ByteBuffer.allocate( notBuffer.capacity() + 8 ); + notBuffer.flip(); + buffer.put(notBuffer); + buffer.putLong( Long.MAX_VALUE ); + buffer.flip(); + + fle.manager.recvQueue.add(new Message(buffer, 2)); + Notification n = fle.recvqueue.take(); + Assert.assertTrue("Wrong state", n.state == ServerState.LOOKING); + Assert.assertTrue("Wrong leader", n.leader == 2); + Assert.assertTrue("Wrong zxid", n.zxid == 0x1); + Assert.assertTrue("Wrong epoch", n.electionEpoch == 1); + Assert.assertTrue("Wrong epoch", n.peerEpoch == 1); + Assert.assertTrue("Wrong version", n.version == FastLeaderElection.Notification.CURRENTVERSION); + } +} diff --git a/src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java similarity index 96% rename from src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java rename to src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java index eb64b4bcdad..52c40c4e973 100644 --- a/src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.zookeeper.test; +package org.apache.zookeeper.server.quorum; import java.io.File; import java.io.IOException; @@ -32,6 +32,7 @@ import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.apache.zookeeper.test.ClientBase; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -106,8 +107,8 @@ void mockServer() throws InterruptedException, IOException { listener.start(); - cnxManager.toSend(1l, FLETestUtils.createMsg(ServerState.LOOKING.ordinal(), 0, 0, 1)); + cnxManager.toSend(1l, FLETestUtils.createMsg(ServerState.LOOKING.ordinal(), 0, 0, 0)); cnxManager.recvQueue.take(); - cnxManager.toSend(1L, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 1, 0, 1)); + cnxManager.toSend(1L, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 1, 0, 0)); } } diff --git a/src/java/test/org/apache/zookeeper/test/FLETestUtils.java b/src/java/test/org/apache/zookeeper/server/quorum/FLETestUtils.java similarity index 85% rename from src/java/test/org/apache/zookeeper/test/FLETestUtils.java rename to src/java/test/org/apache/zookeeper/server/quorum/FLETestUtils.java index 81f5cc5ba74..1f0bca3d977 100644 --- a/src/java/test/org/apache/zookeeper/test/FLETestUtils.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLETestUtils.java @@ -15,10 +15,11 @@ * limitations under the License. */ -package org.apache.zookeeper.test; +package org.apache.zookeeper.server.quorum; import java.nio.ByteBuffer; +import org.apache.zookeeper.server.quorum.FastLeaderElection; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.Vote; @@ -79,20 +80,7 @@ public void run(){ */ static ByteBuffer createMsg(int state, long leader, long zxid, long epoch){ - byte requestBytes[] = new byte[28]; - ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); - - /* - * Building notification packet to send - */ - - requestBuffer.clear(); - requestBuffer.putInt(state); - requestBuffer.putLong(leader); - requestBuffer.putLong(zxid); - requestBuffer.putLong(epoch); - - return requestBuffer; + return FastLeaderElection.buildMsg(state, leader, zxid, 1, epoch); } } \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java b/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java index a1f7a5eccb7..dbf8ba9f0a0 100644 --- a/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java +++ b/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java @@ -38,6 +38,7 @@ import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.quorum.Election; +import org.apache.zookeeper.server.quorum.FLELostMessageTest; import org.apache.zookeeper.server.quorum.LeaderElection; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.Vote; From b770aeb0c8bcb28a9b653b1dc0edac623c75b509 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sun, 17 Nov 2013 11:42:22 +0000 Subject: [PATCH 202/444] ZOOKEEPER-1597. Windows build failing (michim via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1542710 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + src/c/Cli.vcproj | 262 ++---------- src/c/zookeeper.sln | 70 +--- src/c/zookeeper.vcproj | 902 ++++++++++++++--------------------------- 4 files changed, 351 insertions(+), 885 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9c31ab97ccd..9db91b23167 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -160,6 +160,8 @@ BUGFIXES: ZOOKEEPER-1812. ZooInspector reconnection always fails if first connection fails (Benjamin Jaton via phunt) + ZOOKEEPER-1597. Windows build failing (michim via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/Cli.vcproj b/src/c/Cli.vcproj index ba93ffc6432..39ed8a429d6 100644 --- a/src/c/Cli.vcproj +++ b/src/c/Cli.vcproj @@ -1,10 +1,12 @@ - @@ -113,13 +117,16 @@ /> - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/c/zookeeper.sln b/src/c/zookeeper.sln index 4b82253d761..42f41c95264 100644 --- a/src/c/zookeeper.sln +++ b/src/c/zookeeper.sln @@ -1,57 +1,25 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zookeeper", "zookeeper.vcproj", "{5754FB2B-5EA5-4988-851D-908CA533A626}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cli", "Cli.vcproj", "{050228F9-070F-4806-A2B5-E6B95D8EC4AF}" - ProjectSection(ProjectDependencies) = postProject - {5754FB2B-5EA5-4988-851D-908CA533A626} = {5754FB2B-5EA5-4988-851D-908CA533A626} - EndProjectSection EndProject Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.ActiveCfg = Debug|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.Build.0 = Debug|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.ActiveCfg = Release|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.Build.0 = Release|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.Build.0 = Debug|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.ActiveCfg = Release|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.ActiveCfg = Debug|Win32 + {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.Build.0 = Debug|Win32 + {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.ActiveCfg = Release|Win32 + {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.Build.0 = Release|Win32 + {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.ActiveCfg = Debug|Win32 + {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.Build.0 = Debug|Win32 + {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.ActiveCfg = Release|Win32 + {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection EndGlobal - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zookeeper", "zookeeper.vcproj", "{5754FB2B-5EA5-4988-851D-908CA533A626}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cli", "Cli.vcproj", "{050228F9-070F-4806-A2B5-E6B95D8EC4AF}" - ProjectSection(ProjectDependencies) = postProject - {5754FB2B-5EA5-4988-851D-908CA533A626} = {5754FB2B-5EA5-4988-851D-908CA533A626} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.ActiveCfg = Debug|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.Build.0 = Debug|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.ActiveCfg = Release|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.Build.0 = Release|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.Build.0 = Debug|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.ActiveCfg = Release|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/c/zookeeper.vcproj b/src/c/zookeeper.vcproj index fcd58c2457e..dc3ab43e3e6 100644 --- a/src/c/zookeeper.vcproj +++ b/src/c/zookeeper.vcproj @@ -1,606 +1,300 @@ - +??? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ProjectType="Visual C++" + Version="9.00" + Name="zookeeper" + ProjectGUID="{5754FB2B-5EA5-4988-851D-908CA533A626}" + RootNamespace="zookeeper" + Keyword="Win32Proj" + TargetFrameworkVersion="0" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 7f069c0e084a74f6de661267b828ff3fde30bd11 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 23 Nov 2013 18:21:38 +0000 Subject: [PATCH 203/444] ZOOKEEPER-1817. Fix don't care for b3.4 (fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1544858 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/quorum/FastLeaderElection.java | 125 ++++++--- .../zookeeper/server/quorum/QuorumPeer.java | 22 +- .../apache/zookeeper/server/quorum/Vote.java | 34 ++- .../server/quorum/flexible/QuorumMaj.java | 6 + .../server/quorum/FLEDontCareTest.java | 263 ++++++++++++++++++ 6 files changed, 402 insertions(+), 50 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 9db91b23167..5ab9fbb3169 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -162,6 +162,8 @@ BUGFIXES: ZOOKEEPER-1597. Windows build failing (michim via fpj) + ZOOKEEPER-1817. Fix don't care for b3.4 (fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index d3dc665c8d4..6b79d04a1ec 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -66,14 +66,6 @@ public class FastLeaderElection implements Election { */ final static int maxNotificationInterval = 60000; - - /** - * This value is passed to the methods that check the quorum - * majority of an established ensemble for those values that - * should not be taken into account in the comparison - * (electionEpoch and zxid). - */ - final static int IGNOREVALUE = -1; /** * Connection manager. Fast leader election uses TCP for @@ -330,7 +322,8 @@ public void run() { * Version added in 3.4.6 */ - n.version = (response.buffer.remaining() >= 4) ? response.buffer.getInt() : 0x0; + n.version = (response.buffer.remaining() >= 4) ? + response.buffer.getInt() : 0x0; /* * Print notification info @@ -377,14 +370,29 @@ public void run() { Long.toHexString(current.getZxid()) + " leader=" + current.getId()); } - ToSend notmsg = new ToSend( - ToSend.mType.notification, - current.getId(), - current.getZxid(), - current.getElectionEpoch(), - self.getPeerState(), - response.sid, - current.getPeerEpoch()); + + ToSend notmsg; + if(n.version > 0x0) { + notmsg = new ToSend( + ToSend.mType.notification, + current.getId(), + current.getZxid(), + current.getElectionEpoch(), + self.getPeerState(), + response.sid, + current.getPeerEpoch()); + + } else { + Vote bcVote = self.getBCVote(); + notmsg = new ToSend( + ToSend.mType.notification, + bcVote.getId(), + bcVote.getZxid(), + bcVote.getElectionEpoch(), + self.getPeerState(), + response.sid, + bcVote.getPeerEpoch()); + } sendqueue.offer(notmsg); } } @@ -625,7 +633,7 @@ protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, l * @param l Identifier of the vote received last * @param zxid zxid of the the vote received last */ - private boolean termPredicate( + protected boolean termPredicate( HashMap votes, Vote vote) { @@ -655,7 +663,7 @@ private boolean termPredicate( * @param leader leader id * @param electionEpoch epoch id */ - private boolean checkLeader( + protected boolean checkLeader( HashMap votes, long leader, long electionEpoch){ @@ -678,6 +686,30 @@ private boolean checkLeader( return predicate; } + + /** + * This predicate checks that a leader has been elected. It doesn't + * make a lot of sense without context (check lookForLeader) and it + * has been separated for testing purposes. + * + * @param recv map of received votes + * @param ooe map containing out of election votes (LEADING or FOLLOWING) + * @param n Notification + * @return + */ + protected boolean ooePredicate(HashMap recv, + HashMap ooe, + Notification n) { + + return (termPredicate(recv, new Vote(n.version, + n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch, + n.state)) + && checkLeader(ooe, n.leader, n.electionEpoch)); + + } synchronized void updateProposal(long leader, long zxid, long epoch){ if(LOG.isDebugEnabled()){ @@ -881,7 +913,9 @@ else if(self.getVotingView().containsKey(n.sid)) { ServerState.LEADING: learningState()); Vote endVote = new Vote(proposedLeader, - proposedZxid, proposedEpoch); + proposedZxid, + logicalclock, + proposedEpoch); leaveInstance(endVote); return endVote; } @@ -897,51 +931,52 @@ else if(self.getVotingView().containsKey(n.sid)) { * together. */ if(n.electionEpoch == logicalclock){ - recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch)); - if(termPredicate(recvset, new Vote(n.leader, - n.zxid, n.electionEpoch, n.peerEpoch, n.state)) - && checkLeader(outofelection, n.leader, n.electionEpoch)) { + recvset.put(n.sid, new Vote(n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch)); + + if(ooePredicate(recvset, outofelection, n)) { self.setPeerState((n.leader == self.getId()) ? ServerState.LEADING: learningState()); - Vote endVote = new Vote(n.leader, n.zxid, n.peerEpoch); + Vote endVote = new Vote(n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch); leaveInstance(endVote); return endVote; } } /* - * Before joining an established ensemble, verify that - * a majority are following the same leader. - * Only peer epoch is used to check that the votes come - * from the same ensemble. This is because there is at - * least one corner case in which the ensemble can be - * created with inconsistent zxid and election epoch - * info. However, given that only one ensemble can be - * running at a single point in time and that each - * epoch is used only once, using only the epoch to - * compare the votes is sufficient. - * - * @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732 + * Before joining an established ensemble, verify + * a majority is following the same leader. */ - outofelection.put(n.sid, new Vote(n.leader, - IGNOREVALUE, IGNOREVALUE, n.peerEpoch, n.state)); - if (termPredicate(outofelection, new Vote(n.leader, - IGNOREVALUE, IGNOREVALUE, n.peerEpoch, n.state)) - && checkLeader(outofelection, n.leader, IGNOREVALUE)) { + outofelection.put(n.sid, new Vote(n.version, + n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch, + n.state)); + + if(ooePredicate(outofelection, outofelection, n)) { synchronized(this){ logicalclock = n.electionEpoch; self.setPeerState((n.leader == self.getId()) ? ServerState.LEADING: learningState()); } - Vote endVote = new Vote(n.leader, n.zxid, n.peerEpoch); + Vote endVote = new Vote(n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch); leaveInstance(endVote); return endVote; } break; default: - LOG.warn("Notification state unrecoginized: " + n.state - + " (n.state), " + n.sid + " (n.sid)"); + LOG.warn("Notification state unrecognized: {} (n.state), {} (n.sid)", + n.state, n.sid); break; } } else { 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 d6e8bba907b..a43e92eafe0 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -204,6 +204,11 @@ public long getId() { * This is who I think the leader currently is. */ volatile private Vote currentVote; + + /** + * ... and its counterpart for backward compatibility + */ + volatile private Vote bcVote; public synchronized Vote getCurrentVote(){ return currentVote; @@ -211,8 +216,20 @@ public synchronized Vote getCurrentVote(){ public synchronized void setCurrentVote(Vote v){ currentVote = v; - } + } + + synchronized Vote getBCVote() { + if (bcVote == null) { + return currentVote; + } else { + return bcVote; + } + } + synchronized void setBCVote(Vote v) { + bcVote = v; + } + volatile boolean running = true; /** @@ -715,6 +732,7 @@ public void run() { }; try { roZkMgr.start(); + setBCVote(null); setCurrentVote(makeLEStrategy().lookForLeader()); } catch (Exception e) { LOG.warn("Unexpected exception",e); @@ -727,6 +745,7 @@ public void run() { } } else { try { + setBCVote(null); setCurrentVote(makeLEStrategy().lookForLeader()); } catch (Exception e) { LOG.warn("Unexpected exception", e); @@ -1202,6 +1221,7 @@ public void setAcceptedEpoch(long e) throws IOException { */ protected void updateElectionVote(long newEpoch) { Vote currentVote = getCurrentVote(); + setBCVote(currentVote); if (currentVote != null) { setCurrentVote(new Vote(currentVote.getId(), currentVote.getZxid(), diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Vote.java b/src/java/main/org/apache/zookeeper/server/quorum/Vote.java index 82639e09418..aca9a9a2170 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Vote.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Vote.java @@ -19,9 +19,12 @@ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Vote { + private static final Logger LOG = LoggerFactory.getLogger(Vote.class); public Vote(long id, long zxid) { @@ -125,19 +128,42 @@ public boolean equals(Object o) { return false; } Vote other = (Vote) o; - return (id == other.id + + + /* + * There are two things going on in the logic below. + * First, we compare votes of servers out of election + * using only id and peer epoch. Second, if one version + * is 0x0 and the other isn't, then we only use the + * leader id. This case is here to enable rolling upgrades. + * + * {@see https://issues.apache.org/jira/browse/ZOOKEEPER-1805} + */ + if ((state == ServerState.LOOKING) || + (other.state == ServerState.LOOKING)) { + return (id == other.id && zxid == other.zxid && electionEpoch == other.electionEpoch && peerEpoch == other.peerEpoch); - + } else { + if ((version > 0x0) ^ (other.version > 0x0)) { + return id == other.id; + } else { + return (id == other.id + && peerEpoch == other.peerEpoch); + } + } } - + @Override public int hashCode() { return (int) (id & zxid); } public String toString() { - return "(" + id + ", " + Long.toHexString(zxid) + ", " + Long.toHexString(peerEpoch) + ")"; + return String.format("(%d, %s, %s)", + id, + Long.toHexString(zxid), + Long.toHexString(peerEpoch)); } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumMaj.java b/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumMaj.java index 3427147da4e..04773d71196 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumMaj.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumMaj.java @@ -20,6 +20,10 @@ import java.util.HashSet; +//import org.apache.zookeeper.server.quorum.QuorumCnxManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class implements a validator for majority quorums. The @@ -27,6 +31,8 @@ * */ public class QuorumMaj implements QuorumVerifier { + private static final Logger LOG = LoggerFactory.getLogger(QuorumMaj.class); + int half; /** diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java new file mode 100644 index 00000000000..826d91f70a4 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java @@ -0,0 +1,263 @@ +/** + * 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.server.quorum; + +import java.io.File; +import java.net.InetSocketAddress; +import java.util.HashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.server.quorum.FastLeaderElection; +import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.quorum.Vote; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.apache.zookeeper.server.util.ZxidUtils; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.FLETest; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + +public class FLEDontCareTest { + protected static final Logger LOG = LoggerFactory.getLogger(FLEDontCareTest.class); + + class MockFLE extends FastLeaderElection { + MockFLE(QuorumPeer peer, QuorumCnxManager cnxManager) { + super(peer, cnxManager); + } + + public boolean termPredicate(HashMap votes, Vote vote) { + return super.termPredicate(votes, vote); + } + + public boolean checkLeader(HashMap votes, long leader, long electionEpoch) { + return super.checkLeader(votes, leader, electionEpoch); + } + + public boolean ooePredicate(HashMap recv, + HashMap ooe, + FastLeaderElection.Notification n) { + return super.ooePredicate(recv, ooe, n); + + } + } + + HashMap peers; + QuorumPeer peer; + File tmpdir; + + @Before + public void setUp() + throws Exception { + tmpdir = ClientBase.createTmpDir(); + peers = new HashMap(); + for(int i = 0; i < 5; i++) { + peers.put(Long.valueOf(i), + new QuorumServer(Long.valueOf(i), + new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); + } + peer = new QuorumPeer(peers, + tmpdir, + tmpdir, + PortAssignment.unique(), + 3, 3, 1000, 2, 2); + } + + @After + public void tearDown(){ + tmpdir.delete(); + } + + @Test + public void testDontCare() { + MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + + HashMap votes = new HashMap(); + votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 2, ServerState.FOLLOWING)); + votes.put(1L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 2), 1, 2, ServerState.FOLLOWING)); + votes.put(3L, new Vote(0x1, 4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING)); + votes.put(4L, new Vote(0x1, 4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LEADING)); + + Assert.assertTrue(fle.termPredicate(votes, + new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING))); + } + + @Test + public void testDontCareVersion() { + MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + + HashMap votes = new HashMap(); + votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.FOLLOWING)); + votes.put(1L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.FOLLOWING)); + votes.put(3L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING)); + votes.put(4L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LEADING)); + + Assert.assertTrue(fle.termPredicate(votes, + new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING))); + } + + @Test + public void testLookingNormal() { + MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + + HashMap votes = new HashMap(); + votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); + votes.put(1L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); + votes.put(3L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); + votes.put(4L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LEADING)); + + Assert.assertTrue(fle.termPredicate(votes, + new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING))); + } + + @Test + public void testLookingDiffRounds() { + MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + + HashMap votes = new HashMap(); + votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.LOOKING)); + votes.put(1L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LOOKING)); + votes.put(3L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 3, 2, ServerState.LOOKING)); + votes.put(4L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 3, 2, ServerState.LEADING)); + + Assert.assertFalse(fle.termPredicate(votes, + new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LOOKING))); + } + + + /** + * Helper method to build notifications and populate outofelection. + * + * + * @param version + * @param leader + * @param zxid + * @param electionEpoch + * @param state + * @param sid + * @param peerEpoch + * @param outofelection + * @return + */ + FastLeaderElection.Notification genNotification(int version, + long leader, + long zxid, + long electionEpoch, + ServerState state, + long sid, + long peerEpoch, + HashMap outofelection) { + FastLeaderElection.Notification n = new FastLeaderElection.Notification(); + n.version = version; + n.leader = leader; + n.zxid = zxid; + n.electionEpoch = electionEpoch; + n.state = state; + n.sid = sid; + n.peerEpoch = peerEpoch; + + outofelection.put(n.sid, new Vote(n.version, + n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch, + n.state)); + + return n; + } + + @Test + public void testOutofElection() { + MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + HashMap outofelection = new HashMap(); + + /* + * Generates notifications emulating servers 1, 2, 4, and 5. + * Server 5 is the elected leader. + */ + + genNotification( 0x0, + 5, + ZxidUtils.makeZxid(15, 0), + 0xa, + ServerState.FOLLOWING, + 1, + 0x17, + outofelection); + + genNotification( 0x0, + 5, + ZxidUtils.makeZxid(15, 0), + 0xa, + ServerState.FOLLOWING, + 2, + 0x17, + outofelection); + + genNotification( 0x1, + 5, + ZxidUtils.makeZxid(15, 0), + 0xa, + ServerState.FOLLOWING, + 4, + 0x18, + outofelection); + + FastLeaderElection.Notification n = genNotification( 0x1, + 5, + ZxidUtils.makeZxid(15, 0), + 0xa, + ServerState.LEADING, + 5, + 0x18, + outofelection); + + /* + * fle represents the FLE instance of server 3.Here we set + * its logical clock to 1. + */ + fle.logicalclock = 0x1; + + + /* + * Here we test the predicates we use in FLE. + */ + Assert.assertTrue("Termination predicate failed", + fle.termPredicate(outofelection, + new Vote(n.version, + n.leader, + n.zxid, + n.electionEpoch, + n.peerEpoch, + n.state))); + Assert.assertTrue("Leader check failed", + fle.checkLeader(outofelection, + n.leader, + n.electionEpoch)); + + Assert.assertTrue("Out of election predicate failed", + fle.ooePredicate( outofelection, outofelection, n )); + + } +} From c62f2ffa7b9fb026004f66d788ebf1e293d2fdf8 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Tue, 26 Nov 2013 23:43:29 +0000 Subject: [PATCH 204/444] ZOOKEEPER-1653. zookeeper fails to start because of inconsistent epoch (michim via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1545883 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/quorum/Learner.java | 15 +++ .../zookeeper/server/quorum/QuorumPeer.java | 17 ++- .../server/quorum/QuorumPeerMainTest.java | 113 ++++++++++++++++++ .../server/quorum/QuorumPeerTestBase.java | 8 +- 5 files changed, 154 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5ab9fbb3169..eb4537087e1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -164,6 +164,9 @@ BUGFIXES: ZOOKEEPER-1817. Fix don't care for b3.4 (fpj) + ZOOKEEPER-1653. zookeeper fails to start because of inconsistent + epoch (michim via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index 46bf31598ec..0af88449b35 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.File; import java.io.IOException; import java.net.ConnectException; import java.net.InetSocketAddress; @@ -423,8 +424,22 @@ else if (qp.getType() == Leader.SNAP) { self.cnxnFactory.setZooKeeperServer(zk); break outerLoop; case Leader.NEWLEADER: // it will be NEWLEADER in v1.0 + // Create updatingEpoch file and remove it after current + // epoch is set. QuorumPeer.loadDataBase() uses this file to + // detect the case where the server was terminated after + // taking a snapshot but before setting the current epoch. + File updating = new File(self.getTxnFactory().getSnapDir(), + QuorumPeer.UPDATING_EPOCH_FILENAME); + if (!updating.exists() && !updating.createNewFile()) { + throw new IOException("Failed to create " + + updating.toString()); + } zk.takeSnapshot(); self.setCurrentEpoch(newEpoch); + if (!updating.delete()) { + throw new IOException("Failed to delete " + + updating.toString()); + } snapshotTaken = true; writePacket(new QuorumPacket(Leader.ACK, newLeaderZxid, null, null), true); break; 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 a43e92eafe0..df2a7413f31 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -443,7 +443,9 @@ public synchronized void start() { super.start(); } - private void loadDataBase() { + private void loadDataBase() { + File updating = new File(getTxnFactory().getSnapDir(), + UPDATING_EPOCH_FILENAME); try { zkDb.loadDataBase(); @@ -452,6 +454,17 @@ private void loadDataBase() { long epochOfZxid = ZxidUtils.getEpochFromZxid(lastProcessedZxid); try { currentEpoch = readLongFromFile(CURRENT_EPOCH_FILENAME); + if (epochOfZxid > currentEpoch && updating.exists()) { + LOG.info("{} found. The server was terminated after " + + "taking a snapshot but before updating current " + + "epoch. Setting current epoch to {}.", + UPDATING_EPOCH_FILENAME, epochOfZxid); + setCurrentEpoch(epochOfZxid); + if (!updating.delete()) { + throw new IOException("Failed to delete " + + updating.toString()); + } + } } catch(FileNotFoundException e) { // pick a reasonable epoch number // this should only happen once when moving to a @@ -1156,6 +1169,8 @@ private long readLongFromFile(String name) throws IOException { public static final String ACCEPTED_EPOCH_FILENAME = "acceptedEpoch"; + public static final String UPDATING_EPOCH_FILENAME = "updatingEpoch"; + /** * Write a long value to disk atomically. Either succeeds or an exception * is thrown. 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 24cd060dac6..ffb3f0207eb 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -20,8 +20,13 @@ import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileReader; import java.io.LineNumberReader; +import java.io.OutputStreamWriter; import java.io.StringReader; import java.io.IOException; import java.net.InetSocketAddress; @@ -29,6 +34,7 @@ import java.nio.channels.SocketChannel; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.apache.log4j.Layout; @@ -44,8 +50,10 @@ import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper.States; +import org.apache.zookeeper.common.AtomicFileOutputStream; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; +import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.test.ClientBase; import org.junit.Assert; import org.junit.Test; @@ -56,6 +64,9 @@ * */ public class QuorumPeerMainTest extends QuorumPeerTestBase { + protected static final Logger LOG = + Logger.getLogger(QuorumPeerMainTest.class); + /** * Verify the ability to start a cluster. */ @@ -669,4 +680,106 @@ public void testQuorumPeerExitTime() throws Exception { " to shutdown, expected " + maxwait); } } + + static long readLongFromFile(File file) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(file)); + String line = ""; + try { + line = br.readLine(); + return Long.parseLong(line); + } catch(NumberFormatException e) { + throw new IOException("Found " + line + " in " + file); + } finally { + br.close(); + } + } + + static void writeLongToFile(File file, long value) throws IOException { + AtomicFileOutputStream out = new AtomicFileOutputStream(file); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out)); + try { + bw.write(Long.toString(value)); + bw.flush(); + out.flush(); + out.close(); + } catch (IOException e) { + LOG.error("Failed to write new file " + file, e); + out.abort(); + throw e; + } + } + + /** + * ZOOKEEPER-1653 Make sure the server starts if the current epoch is less + * than the epoch from last logged zxid and updatingEpoch file exists. + */ + @Test + public void testUpdatingEpoch() throws Exception { + // Create a cluster and restart them multiple times to bump the epoch. + int numServers = 3; + Servers servers = LaunchServers(numServers); + File currentEpochFile; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < numServers; j++) { + servers.mt[j].shutdown(); + } + waitForAll(servers.zk, States.CONNECTING); + for (int j = 0; j < numServers; j++) { + servers.mt[j].start(); + } + waitForAll(servers.zk, States.CONNECTED); + } + + // Current epoch is 11 now. + for (int i = 0; i < numServers; i++) { + currentEpochFile = new File( + new File(servers.mt[i].dataDir, "version-2"), + QuorumPeer.CURRENT_EPOCH_FILENAME); + LOG.info("Validating current epoch: " + servers.mt[i].dataDir); + Assert.assertEquals("Current epoch should be 11.", 11, + readLongFromFile(currentEpochFile)); + } + + // Find a follower and get epoch from the last logged zxid. + int followerIndex = -1; + for (int i = 0; i < numServers; i++) { + if (servers.mt[i].main.quorumPeer.leader == null) { + followerIndex = i; + break; + } + } + Assert.assertTrue("Found a valid follower", + followerIndex >= 0 && followerIndex < numServers); + MainThread follower = servers.mt[followerIndex]; + long zxid = follower.main.quorumPeer.getLastLoggedZxid(); + long epochFromZxid = ZxidUtils.getEpochFromZxid(zxid); + + // Shutdown the cluster + for (int i = 0; i < numServers; i++) { + servers.mt[i].shutdown(); + } + waitForAll(servers.zk, States.CONNECTING); + + // Make current epoch less than epoch from the last logged zxid. + // The server should fail to start. + File followerDataDir = new File(follower.dataDir, "version-2"); + currentEpochFile = new File(followerDataDir, + QuorumPeer.CURRENT_EPOCH_FILENAME); + writeLongToFile(currentEpochFile, epochFromZxid - 1); + follower.start(); + Assert.assertTrue(follower.mainFailed.await(10, TimeUnit.SECONDS)); + + // Touch the updateEpoch file. Now the server should start. + File updatingEpochFile = new File(followerDataDir, + QuorumPeer.UPDATING_EPOCH_FILENAME); + updatingEpochFile.createNewFile(); + for (int i = 0; i < numServers; i++) { + servers.mt[i].start(); + } + waitForAll(servers.zk, States.CONNECTED); + Assert.assertNotNull("Make sure the server started with acceptEpoch", + follower.main.quorumPeer.getActiveServer()); + Assert.assertFalse("updatingEpoch file should get deleted", + updatingEpochFile.exists()); + } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java index 57ed4f89e21..ef552db09ba 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; @@ -57,6 +58,8 @@ public void shutdown() { public static class MainThread implements Runnable { final File confFile; volatile TestQPMain main; + final File dataDir; + CountDownLatch mainFailed; public MainThread(int myid, int clientPort, String quorumCfgSection) throws IOException { @@ -70,7 +73,7 @@ public MainThread(int myid, int clientPort, String quorumCfgSection) fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); - File dataDir = new File(tmpDir, "data"); + dataDir = new File(tmpDir, "data"); if (!dataDir.mkdir()) { throw new IOException("Unable to mkdir " + dataDir); } @@ -101,6 +104,7 @@ synchronized public void start() { main = new TestQPMain(); currentThread = new Thread(this); currentThread.start(); + mainFailed = new CountDownLatch(1); } public void run() { @@ -111,6 +115,8 @@ public void run() { } catch (Exception e) { // test will still fail even though we just log/ignore LOG.error("unexpected exception in run", e); + main.shutdown(); + mainFailed.countDown(); } finally { currentThread = null; } From 605c8d1b0db0baa57582bb39229c80bfec832ae3 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 27 Nov 2013 23:23:11 +0000 Subject: [PATCH 205/444] ZOOKEEPER-1821. very ugly warning when compiling load_gen.c (german blanco via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1546231 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/load_gen.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index eb4537087e1..a4b5f3d55fe 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -167,6 +167,9 @@ BUGFIXES: ZOOKEEPER-1653. zookeeper fails to start because of inconsistent epoch (michim via fpj) + ZOOKEEPER-1821. very ugly warning when compiling load_gen.c + (german blanco via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/src/load_gen.c b/src/c/src/load_gen.c index 3527948c2d4..72b5950765c 100644 --- a/src/c/src/load_gen.c +++ b/src/c/src/load_gen.c @@ -106,6 +106,7 @@ int doCreateNodes(const char* root, int count){ } if(rc!=ZOK) return rc; } + return ZOK; } int createRoot(const char* root){ @@ -131,6 +132,7 @@ int doWrites(const char* root, int count){ rc=zoo_aset(zh, nodeName, "second", 6,-1,write_completion, 0); if(rc!=ZOK) return rc; } + return ZOK; } void read_completion(int rc, const char *value, int value_len, @@ -159,6 +161,7 @@ int doReads(const char* root, int count){ rc=zoo_aget(zh, nodeName,0,read_completion, 0); if(rc!=ZOK) return rc; } + return ZOK; } void delete_completion(int rc, const void *data) { @@ -176,6 +179,7 @@ int doDeletes(const char* root, int count){ rc=zoo_adelete(zh, nodeName,-1,delete_completion, 0); if(rc!=ZOK) return rc; } + return ZOK; } static int free_String_vector(struct String_vector *v) { From a1cdea63de42ed1eaba224570ad0304c224b21c1 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Wed, 4 Dec 2013 04:20:53 +0000 Subject: [PATCH 206/444] ZOOKEEPER-1632. fix memory leaks in cli_st (fpj via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1547703 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/src/cli.c | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a4b5f3d55fe..c4d1d62990a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -170,6 +170,8 @@ BUGFIXES: ZOOKEEPER-1821. very ugly warning when compiling load_gen.c (german blanco via fpj) + ZOOKEEPER-1632. fix memory leaks in cli_st (fpj via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/src/cli.c b/src/c/src/cli.c index 44ff9d45cb0..7b3cd61b5ed 100644 --- a/src/c/src/cli.c +++ b/src/c/src/cli.c @@ -182,6 +182,11 @@ void my_string_completion(int rc, const char *name, const void *data) { shutdownThisThing=1; } +void my_string_completion_free_data(int rc, const char *name, const void *data) { + my_string_completion(rc, name, data); + free((void*)data); +} + void my_data_completion(int rc, const char *value, int value_len, const struct Stat *stat, const void *data) { struct timeval tv; @@ -418,7 +423,7 @@ void processline(char *line) { // my_string_completion, strdup(line)); // } rc = zoo_acreate(zh, line, "new", 3, &ZOO_OPEN_ACL_UNSAFE, flags, - my_string_completion, strdup(line)); + my_string_completion_free_data, strdup(line)); if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } @@ -442,7 +447,7 @@ void processline(char *line) { fprintf(stderr, "Path must start with /, found: %s\n", line); return; } - rc = zoo_async(zh, line, my_string_completion, strdup(line)); + rc = zoo_async(zh, line, my_string_completion_free_data, strdup(line)); if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } From f23b85bde1bf53b284a85a8ec6c6251cd63e9feb Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 7 Dec 2013 10:17:54 +0000 Subject: [PATCH 207/444] ZOOKEEPER-1459. Standalone ZooKeeperServer is not closing the transaction log files on shutdown (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1548826 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/server/ZooKeeperServerMain.java | 11 ++++++--- .../server/ZooKeeperServerMainTest.java | 23 +++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c4d1d62990a..0aa848bd8bb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -172,6 +172,9 @@ BUGFIXES: ZOOKEEPER-1632. fix memory leaks in cli_st (fpj via michim) + ZOOKEEPER-1459. Standalone ZooKeeperServer is not closing + the transaction log files on shutdown (Rakesh R via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java index 52d3820cbae..c7eb9d64011 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java @@ -93,6 +93,7 @@ protected void initializeAndRun(String[] args) */ public void runFromConfig(ServerConfig config) throws IOException { LOG.info("Starting server"); + FileTxnSnapLog txnLog = null; try { // Note that this thread isn't going to be doing anything else, // so rather than spawning another thread, we will just call @@ -100,9 +101,9 @@ public void runFromConfig(ServerConfig config) throws IOException { // create a file logger url from the command line args ZooKeeperServer zkServer = new ZooKeeperServer(); - FileTxnSnapLog ftxn = new FileTxnSnapLog(new - File(config.dataLogDir), new File(config.dataDir)); - zkServer.setTxnLogFactory(ftxn); + txnLog = new FileTxnSnapLog(new File(config.dataLogDir), new File( + config.dataDir)); + zkServer.setTxnLogFactory(txnLog); zkServer.setTickTime(config.tickTime); zkServer.setMinSessionTimeout(config.minSessionTimeout); zkServer.setMaxSessionTimeout(config.maxSessionTimeout); @@ -117,6 +118,10 @@ public void runFromConfig(ServerConfig config) throws IOException { } catch (InterruptedException e) { // warn, but generally this is ok LOG.warn("Server interrupted", e); + } finally { + if (txnLog != null) { + txnLog.close(); + } } } diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java index e3f6dc2a51c..d2760596e20 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java @@ -47,10 +47,11 @@ public class ZooKeeperServerMainTest extends ZKTestCase implements Watcher { public static class MainThread extends Thread { final File confFile; final TestZKSMain main; + final File tmpDir; public MainThread(int clientPort) throws IOException { super("Standalone server with clientPort:" + clientPort); - File tmpDir = ClientBase.createTmpDir(); + tmpDir = ClientBase.createTmpDir(); confFile = new File(tmpDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); @@ -89,9 +90,25 @@ public void run() { } } - public void shutdown() { + public void shutdown() throws IOException { main.shutdown(); } + + void deleteDirs() throws IOException{ + delete(tmpDir); + } + + void delete(File f) throws IOException { + if (f.isDirectory()) { + for (File c : f.listFiles()) + delete(c); + } + if (!f.delete()) + // double check for the file existence + if (f.exists()) { + throw new IOException("Failed to delete file: " + f); + } + } } public static class TestZKSMain extends ZooKeeperServerMain { @@ -126,6 +143,8 @@ public void testStandalone() throws Exception { zk.close(); main.shutdown(); + main.join(); + main.deleteDirs(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, From 4822626f958742baf98303fd9cdfcb51ce95cb46 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Tue, 10 Dec 2013 20:43:22 +0000 Subject: [PATCH 208/444] ZOOKEEPER-1019. zkfuse doesn't list dependency on boost in README (Raul Gutierrez Segales via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1549963 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/zkfuse/README.txt | 1 + src/contrib/zkfuse/configure.ac | 6 ++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0aa848bd8bb..ec13bc0a783 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -175,6 +175,9 @@ BUGFIXES: ZOOKEEPER-1459. Standalone ZooKeeperServer is not closing the transaction log files on shutdown (Rakesh R via fpj) + ZOOKEEPER-1019. zkfuse doesn't list dependency on boost in README + (Raul Gutierrez Segales via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/contrib/zkfuse/README.txt b/src/contrib/zkfuse/README.txt index ee8ed9ee656..901d3634fed 100644 --- a/src/contrib/zkfuse/README.txt +++ b/src/contrib/zkfuse/README.txt @@ -11,6 +11,7 @@ Pre-requisites a. fuse b. log4cxx c. pthread + d. boost Build instructions ------------------ diff --git a/src/contrib/zkfuse/configure.ac b/src/contrib/zkfuse/configure.ac index ae2996bc506..c86e7cdc84c 100644 --- a/src/contrib/zkfuse/configure.ac +++ b/src/contrib/zkfuse/configure.ac @@ -23,7 +23,7 @@ AC_PROG_CXX # Checks for libraries. AC_CHECK_LIB([fuse], [main]) -AC_CHECK_LIB([log4cxx], [main]) +AC_CHECK_LIB([log4cxx], [main], [], [AC_MSG_ERROR("We need log4cxx to build zkfuse")]) AC_CHECK_LIB([thread], [thr_create]) AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([rt], [clock_gettime]) @@ -32,7 +32,8 @@ AC_CHECK_LIB([nsl], [gethostbyname]) AC_CHECK_LIB([ulockmgr], [ulockmgr_op]) ZOOKEEPER_PATH=${BUILD_PATH}/../../c -AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER_LD="-L${ZOOKEEPER_PATH}/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_PATH}/.libs"]) +ZOOKEEPER_BUILD_PATH=${BUILD_PATH}/../../../build/c +AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER_LD="-L${ZOOKEEPER_BUILD_PATH}/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_BUILD_PATH}/.libs"]) AC_SUBST(ZOOKEEPER_PATH) AC_SUBST(ZOOKEEPER_LD) @@ -41,6 +42,7 @@ AC_SUBST(ZOOKEEPER_LD) AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h]) +AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/utility.hpp boost/weak_ptr.hpp],, AC_MSG_ERROR([boost library headers not found. Please install boost library.])) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL From b666d3bccc2e8a0851c4b68024a4c3ac2d97713c Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Wed, 11 Dec 2013 00:12:53 +0000 Subject: [PATCH 209/444] ZOOKEEPER-1834. Catch IOException in FileTxnLog (fpj via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1550006 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../org/apache/zookeeper/server/persistence/FileTxnLog.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index ec13bc0a783..dc02878372e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -178,6 +178,8 @@ BUGFIXES: ZOOKEEPER-1019. zkfuse doesn't list dependency on boost in README (Raul Gutierrez Segales via michim) + ZOOKEEPER-1834. Catch IOException in FileTxnLog (fpj via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index 64f3af7385f..55cdc8db8f5 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -627,6 +627,9 @@ record = SerializeUtils.deserializeTxn(bytes, hdr); } // if we went to the next log file, we should call next() again return next(); + } catch (IOException e) { + inputStream.close(); + throw e; } return true; } From 63b1f92102877757815eec5e0866b7a143868e2d Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Wed, 11 Dec 2013 18:31:05 +0000 Subject: [PATCH 210/444] =?UTF-8?q?ZOOKEEPER-1382.=20Zookeeper=20server=20?= =?UTF-8?q?holds=20onto=20dead/expired=20session=20ids=20in=20the=20watch?= =?UTF-8?q?=20data=20structures=20=20=20(Germ=C3=A1n=20Blanco=20and=20Mich?= =?UTF-8?q?ael=20Morello=20via=20camille)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1550220 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/NIOServerCnxn.java | 12 +- .../zookeeper/server/ZooKeeperServer.java | 2 +- .../test/org/apache/zookeeper/MockPacket.java | 46 +++ .../zookeeper/server/MockNIOServerCnxn.java | 45 +++ .../server/quorum/WatchLeakTest.java | 306 ++++++++++++++++++ 6 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/MockPacket.java create mode 100644 src/java/test/org/apache/zookeeper/server/MockNIOServerCnxn.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/WatchLeakTest.java diff --git a/CHANGES.txt b/CHANGES.txt index dc02878372e..dd033f90773 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -179,6 +179,9 @@ BUGFIXES: (Raul Gutierrez Segales via michim) ZOOKEEPER-1834. Catch IOException in FileTxnLog (fpj via michim) + + ZOOKEEPER-1382. Zookeeper server holds onto dead/expired session ids in the watch data structures + (Germán Blanco and Michael Morello via camille) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index b0cad0642d7..d832ad93f3b 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -204,9 +204,19 @@ private void readPayload() throws IOException, InterruptedException { } } + /** + * Only used in order to allow testing + */ + protected boolean isSocketOpen() { + return sock.isOpen(); + } + + /** + * Handles read/write IO on connection. + */ void doIO(SelectionKey k) throws InterruptedException { try { - if (sock.isOpen() == false) { + if (isSocketOpen() == false) { LOG.warn("trying to do i/o on a null socket for session:0x" + Long.toHexString(sessionId)); diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index a049a31f367..c1812c453fe 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -619,9 +619,9 @@ public void finishSessionInit(ServerCnxn cnxn, boolean valid) { + " with negotiated timeout " + cnxn.getSessionTimeout() + " for client " + cnxn.getRemoteSocketAddress()); + cnxn.enableRecv(); } - cnxn.enableRecv(); } catch (Exception e) { LOG.warn("Exception while establishing session, closing", e); cnxn.close(); diff --git a/src/java/test/org/apache/zookeeper/MockPacket.java b/src/java/test/org/apache/zookeeper/MockPacket.java new file mode 100644 index 00000000000..f4bb19a5a05 --- /dev/null +++ b/src/java/test/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; + } + +} diff --git a/src/java/test/org/apache/zookeeper/server/MockNIOServerCnxn.java b/src/java/test/org/apache/zookeeper/server/MockNIOServerCnxn.java new file mode 100644 index 00000000000..53ad1f691fb --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/MockNIOServerCnxn.java @@ -0,0 +1,45 @@ +/** + * 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.server; + +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.io.IOException; + +public class MockNIOServerCnxn extends NIOServerCnxn { + + public MockNIOServerCnxn(ZooKeeperServer zk, SocketChannel sock, + SelectionKey sk, NIOServerCnxnFactory factory) + throws IOException { + super(zk, sock, sk, factory); + } + + /** + * Handles read/write IO on connection. + */ + public void doIO(SelectionKey k) throws InterruptedException { + super.doIO(k); + } + + @Override + protected boolean isSocketOpen() { + return true; + } + +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/WatchLeakTest.java b/src/java/test/org/apache/zookeeper/server/quorum/WatchLeakTest.java new file mode 100644 index 00000000000..74713813d38 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/WatchLeakTest.java @@ -0,0 +1,306 @@ +/** + * 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.server.quorum; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.junit.Assert.*; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.apache.jute.InputArchive; +import org.apache.jute.OutputArchive; +import org.apache.zookeeper.MockPacket; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.proto.ConnectRequest; +import org.apache.zookeeper.proto.ReplyHeader; +import org.apache.zookeeper.proto.RequestHeader; +import org.apache.zookeeper.proto.SetWatches; +import org.apache.zookeeper.server.MockNIOServerCnxn; +import org.apache.zookeeper.server.NIOServerCnxnFactory; +import org.apache.zookeeper.server.ZKDatabase; +import org.apache.zookeeper.server.ZooTrace; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Demonstrate ZOOKEEPER-1382 : Watches leak on expired session + */ +public class WatchLeakTest { + + protected static final Logger LOG = LoggerFactory + .getLogger(WatchLeakTest.class); + + final long SESSION_ID = 0xBABEL; + + /** + * ZOOKEEPR-1382 test class + */ + @Test + public void testWatchesWithClientSessionTimeout() throws Exception { + + NIOServerCnxnFactory serverCnxnFactory = new NIOServerCnxnFactory(); + + ZKDatabase database = new ZKDatabase(null); + database.setlastProcessedZxid(2L); + QuorumPeer quorumPeer = mock(QuorumPeer.class); + FileTxnSnapLog logfactory = mock(FileTxnSnapLog.class); + // Directories are not used but we need it to avoid NPE + when(logfactory.getDataDir()).thenReturn(new File("/tmp")); + when(logfactory.getSnapDir()).thenReturn(new File("/tmp")); + FollowerZooKeeperServer fzks = null; + try { + fzks = new FollowerZooKeeperServer(logfactory, quorumPeer, null, + database); + fzks.startup(); + fzks.setServerCnxnFactory(serverCnxnFactory); + quorumPeer.follower = new MyFollower(quorumPeer, fzks); + final SelectionKey sk = new FakeSK(); + // Simulate a socket channel between a client and a follower + final SocketChannel socketChannel = createClientSocketChannel(); + // Create the NIOServerCnxn that will handle the client requests + final MockNIOServerCnxn nioCnxn = new MockNIOServerCnxn(fzks, + socketChannel, sk, serverCnxnFactory); + // Send the connection request as a client do + nioCnxn.doIO(sk); + // Send the invalid session packet to the follower + QuorumPacket qp = createInvalidSessionPacket(); + quorumPeer.follower.processPacket(qp); + // OK, now the follower knows that the session is invalid, let's try + // to + // send it the watches + nioCnxn.doIO(sk); + // wait for the the request processor to do his job + Thread.sleep(1000L); + // Session has not been re-validated ! + // If session has not been validated, there must be NO watches + int watchCount = database.getDataTree().getWatchCount(); + LOG.info("watches = " + watchCount); + assertEquals(0, watchCount); + } finally { + if (fzks != null) { + fzks.shutdown(); + } + } + } + + /** + * A follower with no real leader connection + */ + public static class MyFollower extends Follower { + /** + * Create a follower with a mocked leader connection + * + * @param self + * @param zk + */ + MyFollower(QuorumPeer self, FollowerZooKeeperServer zk) { + super(self, zk); + leaderOs = mock(OutputArchive.class); + leaderIs = mock(InputArchive.class); + bufferedOutput = mock(BufferedOutputStream.class); + } + } + + /** + * Simulate the behavior of a real selection key + */ + private static class FakeSK extends SelectionKey { + + @Override + public SelectableChannel channel() { + return null; + } + + @Override + public Selector selector() { + return mock(Selector.class); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public void cancel() { + } + + @Override + public int interestOps() { + return ops; + } + + private int ops = OP_WRITE + OP_READ; + + @Override + public SelectionKey interestOps(int ops) { + this.ops = ops; + return this; + } + + @Override + public int readyOps() { + return ops; + } + + } + + /** + * Create a watches message with a single watch on / + * + * @return + */ + private ByteBuffer createWatchesMessage() { + List dataWatches = new ArrayList(1); + dataWatches.add("/"); + List existWatches = Collections.emptyList(); + List childWatches = Collections.emptyList(); + SetWatches sw = new SetWatches(1L, dataWatches, existWatches, + childWatches); + RequestHeader h = new RequestHeader(); + h.setType(ZooDefs.OpCode.setWatches); + h.setXid(-8); + MockPacket p = new MockPacket(h, new ReplyHeader(), sw, null, null); + return p.createAndReturnBB(); + } + + /** + * This is the secret that we use to generate passwords, for the moment it + * is more of a sanity check. + */ + static final private long superSecret = 0XB3415C00L; + + /** + * Create a connection request + * + * @return + */ + private ByteBuffer createConnRequest() { + Random r = new Random(SESSION_ID ^ superSecret); + byte p[] = new byte[16]; + r.nextBytes(p); + ConnectRequest conReq = new ConnectRequest(0, 1L, 30000, SESSION_ID, p); + MockPacket packet = new MockPacket(null, null, conReq, null, null, false); + return packet.createAndReturnBB(); + } + + /** + * Mock a client channel with a connection request and a watches message + * inside. + * + * @return a socket channel + * @throws IOException + */ + private SocketChannel createClientSocketChannel() throws IOException { + + SocketChannel socketChannel = mock(SocketChannel.class); + Socket socket = mock(Socket.class); + InetSocketAddress socketAddress = new InetSocketAddress(1234); + when(socket.getRemoteSocketAddress()).thenReturn(socketAddress); + when(socketChannel.socket()).thenReturn(socket); + + // Send watches packet to server connection + final ByteBuffer connRequest = createConnRequest(); + final ByteBuffer watchesMessage = createWatchesMessage(); + final ByteBuffer request = ByteBuffer.allocate(connRequest.limit() + + watchesMessage.limit()); + request.put(connRequest); + request.put(watchesMessage); + + Answer answer = new Answer() { + int i = 0; + + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + ByteBuffer bb = (ByteBuffer) args[0]; + for (int k = 0; k < bb.limit(); k++) { + bb.put(request.get(i)); + i = i + 1; + } + return bb.limit(); + } + }; + when(socketChannel.read(any(ByteBuffer.class))).thenAnswer(answer); + return socketChannel; + } + + /** + * Forge an invalid session packet as a LEADER do + * + * @throws Exception + */ + private QuorumPacket createInvalidSessionPacket() throws Exception { + QuorumPacket qp = createValidateSessionQuorumPacket(); + ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); + DataInputStream dis = new DataInputStream(bis); + long id = dis.readLong(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + dos.writeLong(id); + // false means that the session has expired + dos.writeBoolean(false); + qp.setData(bos.toByteArray()); + return qp; + } + + /** + * Forge an validate session packet as a LEARNER do + * + * @return + * @throws Exception + */ + private QuorumPacket createValidateSessionQuorumPacket() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeLong(SESSION_ID); + dos.writeInt(3000); + dos.close(); + QuorumPacket qp = new QuorumPacket(Leader.REVALIDATE, -1, + baos.toByteArray(), null); + if (LOG.isTraceEnabled()) { + ZooTrace.logTraceMessage(LOG, ZooTrace.SESSION_TRACE_MASK, + "To validate session 0x" + Long.toHexString(2L)); + } + return qp; + } + +} \ No newline at end of file From fc22886f0c12c0b28780dc29a10c334d1221d9b9 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sat, 14 Dec 2013 08:36:46 +0000 Subject: [PATCH 211/444] ZOOKEEPER-1715. Upgrade netty version (Sean Bridges via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1550902 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ ivy.xml | 2 +- .../apache/zookeeper/server/NettyServerCnxn.java | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dd033f90773..6f379e33f3c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -219,6 +219,8 @@ IMPROVEMENTS: ZOOKEEPER-1786. ZooKeeper data model documentation is incorrect (Niraj Tolia via fpj) + ZOOKEEPER-1715. Upgrade netty version (Sean Bridges via michim) + Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/ivy.xml b/ivy.xml index c78124f32c2..8cf03f8c987 100644 --- a/ivy.xml +++ b/ivy.xml @@ -46,7 +46,7 @@ - + diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index ec63c0498d7..e4c25be1d95 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -155,9 +155,13 @@ static class ResumeMessageEvent implements MessageEvent { ResumeMessageEvent(Channel channel) { this.channel = channel; } + @Override public Object getMessage() {return null;} + @Override public SocketAddress getRemoteAddress() {return null;} + @Override public Channel getChannel() {return channel;} + @Override public ChannelFuture getFuture() {return null;} }; @@ -739,7 +743,7 @@ public void receiveMessage(ChannelBuffer message) { zks.processPacket(this, bb); if (zks.shouldThrottle(outstandingCount.incrementAndGet())) { - disableRecv(); + disableRecvNoWait(); } } else { LOG.debug("got conn req request from " @@ -803,13 +807,17 @@ public void receiveMessage(ChannelBuffer message) { @Override public void disableRecv() { + disableRecvNoWait().awaitUninterruptibly(); + } + + private ChannelFuture disableRecvNoWait() { throttled = true; if (LOG.isDebugEnabled()) { LOG.debug("Throttling - disabling recv " + this); } - channel.setReadable(false).awaitUninterruptibly(); + return channel.setReadable(false); } - + @Override public long getOutstandingRequests() { return outstandingCount.longValue(); @@ -832,6 +840,7 @@ public InetSocketAddress getRemoteSocketAddress() { /** Send close connection packet to the client. */ + @Override public void sendCloseSession() { sendBuffer(ServerCnxnFactory.closeConn); } From ec70dd2c43bc1d780a480487fe4437cb95d01e65 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 14 Dec 2013 11:23:24 +0000 Subject: [PATCH 212/444] =?UTF-8?q?ZOOKEEPER-1837.=20Fix=20JMXEnv=20checks?= =?UTF-8?q?=20(potential=20race=20conditions)=20(Germ=C3=A1n=20Blanco=20vi?= =?UTF-8?q?a=20fpj)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1550923 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/test/org/apache/zookeeper/test/ClientBase.java | 3 +-- src/java/test/org/apache/zookeeper/test/JMXEnv.java | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6f379e33f3c..12439beb3e4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -183,6 +183,9 @@ BUGFIXES: ZOOKEEPER-1382. Zookeeper server holds onto dead/expired session ids in the watch data structures (Germán Blanco and Michael Morello via camille) + ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions) + (Germán Blanco via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 2948239215a..dd53252d237 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -188,14 +188,13 @@ protected TestableZooKeeper createClient(CountdownWatcher watcher, } if (allClients != null) { allClients.add(zk); + JMXEnv.ensureAll("0x" + Long.toHexString(zk.getSessionId())); } else { // test done - close the zk, not needed zk.close(); } } - JMXEnv.ensureAll("0x" + Long.toHexString(zk.getSessionId())); - return zk; } diff --git a/src/java/test/org/apache/zookeeper/test/JMXEnv.java b/src/java/test/org/apache/zookeeper/test/JMXEnv.java index 48a107f1e4d..b3284283bcd 100644 --- a/src/java/test/org/apache/zookeeper/test/JMXEnv.java +++ b/src/java/test/org/apache/zookeeper/test/JMXEnv.java @@ -122,16 +122,22 @@ public static Set ensureAll(String... expectedNames) * Note that these are components of the name, and in particular * order matters - you want the more specific name (leafs) specified * before their parent(s) (since names are hierarchical) + * It waits in a loop up to 5 seconds before failing if there is a + * mismatch. * @param expectedNames * @return * @throws IOException * @throws MalformedObjectNameException */ public static Set ensureOnly(String... expectedNames) - throws IOException + throws IOException, InterruptedException { LOG.info("ensureOnly:" + Arrays.toString(expectedNames)); Set beans = ensureAll(expectedNames); + for (int i = 0; (i < 50) && (beans.size() != 0); i++) { + Thread.sleep(100); + beans = ensureAll(expectedNames); + } for (ObjectName bean : beans) { LOG.info("unexpected:" + bean.toString()); } From 7e1c76814a267d52c4392c7c181e116b3eb31233 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Mon, 16 Dec 2013 04:20:40 +0000 Subject: [PATCH 213/444] ZOOKEEPER-1839. Deadlock in NettyServerCnxn (Rakesh R via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551112 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../zookeeper/server/NettyServerCnxn.java | 10 ++--- .../server/NettyServerCnxnFactory.java | 41 ++++++++++--------- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 12439beb3e4..6c317c920f7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -186,6 +186,8 @@ BUGFIXES: ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions) (Germán Blanco via fpj) + ZOOKEEPER-1839. Deadlock in NettyServerCnxn (Rakesh R via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index e4c25be1d95..cf5cd7ee652 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -108,12 +108,12 @@ public void close() { .getRemoteAddress()).getAddress()); s.remove(this); } - - if (channel.isOpen()) { - channel.close(); - } - factory.unregisterConnection(this); } + + if (channel.isOpen()) { + channel.close(); + } + factory.unregisterConnection(this); } @Override diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index 32692e218d1..f21c7ecc4ac 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -260,20 +260,22 @@ public void closeAll() { LOG.debug("closeAll()"); } + NettyServerCnxn[] allCnxns = null; synchronized (cnxns) { - // got to clear all the connections that we have in the selector - for (NettyServerCnxn cnxn : cnxns.toArray(new NettyServerCnxn[cnxns.size()])) { - try { - cnxn.close(); - } catch (Exception e) { - LOG.warn("Ignoring exception closing cnxn sessionid 0x" - + Long.toHexString(cnxn.getSessionId()), e); - } + allCnxns = cnxns.toArray(new NettyServerCnxn[cnxns.size()]); + } + // got to clear all the connections that we have in the selector + for (NettyServerCnxn cnxn : allCnxns) { + try { + cnxn.close(); + } catch (Exception e) { + LOG.warn("Ignoring exception closing cnxn sessionid 0x" + + Long.toHexString(cnxn.getSessionId()), e); } } if (LOG.isDebugEnabled()) { - LOG.debug("allChannels size:" + allChannels.size() - + " cnxns size:" + cnxns.size()); + LOG.debug("allChannels size:" + allChannels.size() + " cnxns size:" + + allCnxns.length); } } @@ -282,17 +284,18 @@ public void closeSession(long sessionId) { if (LOG.isDebugEnabled()) { LOG.debug("closeSession sessionid:0x" + sessionId); } - + NettyServerCnxn[] allCnxns = null; synchronized (cnxns) { - for (NettyServerCnxn cnxn : cnxns.toArray(new NettyServerCnxn[cnxns.size()])) { - if (cnxn.getSessionId() == sessionId) { - try { - cnxn.close(); - } catch (Exception e) { - LOG.warn("exception during session close", e); - } - break; + allCnxns = cnxns.toArray(new NettyServerCnxn[cnxns.size()]); + } + for (NettyServerCnxn cnxn : allCnxns) { + if (cnxn.getSessionId() == sessionId) { + try { + cnxn.close(); + } catch (Exception e) { + LOG.warn("exception during session close", e); } + break; } } } From a6dec3afdae22bafd8e8fd3c56b258b2a3bbabe8 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 16 Dec 2013 06:27:56 +0000 Subject: [PATCH 214/444] ZOOKEEPER-1622. session ids will be negative in the year 2022 (Eric Newton via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551118 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/server/SessionTrackerImpl.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6c317c920f7..33b40f87a7d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -188,6 +188,9 @@ BUGFIXES: ZOOKEEPER-1839. Deadlock in NettyServerCnxn (Rakesh R via michim) + ZOOKEEPER-1622. session ids will be negative in the year 2022 + (Eric Newton via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java index 31f278528d9..0cf2fa9fc6f 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java @@ -74,7 +74,7 @@ public static class SessionImpl implements Session { public static long initializeNextSession(long id) { long nextSid = 0; - nextSid = (System.currentTimeMillis() << 24) >> 8; + nextSid = (System.currentTimeMillis() << 24) >>> 8; nextSid = nextSid | (id <<56); return nextSid; } From 5dd0361a5db1e2a5f998344c8abdd0751988eba1 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Mon, 16 Dec 2013 21:49:50 +0000 Subject: [PATCH 215/444] ZOOKEEPER-1756. zookeeper_interest() in C client can return a timeval of 0 (Eric Lindvall via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551367 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 16 +++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 33b40f87a7d..e10da94a239 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -191,6 +191,9 @@ BUGFIXES: ZOOKEEPER-1622. session ids will be negative in the year 2022 (Eric Newton via phunt) + ZOOKEEPER-1756. zookeeper_interest() in C client can return a timeval of 0 + (Eric Lindvall via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 758e49b0a4c..27ecf8a6aee 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1650,13 +1650,15 @@ int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, // a PING if (zh->state==ZOO_CONNECTED_STATE) { send_to = zh->recv_timeout/3 - idle_send; - if (send_to <= 0 && zh->sent_requests.head==0) { -// LOG_DEBUG(("Sending PING to %s (exceeded idle by %dms)", -// format_current_endpoint_info(zh),-send_to)); - int rc=send_ping(zh); - if (rc < 0){ - LOG_ERROR(("failed to send PING request (zk retcode=%d)",rc)); - return api_epilog(zh,rc); + if (send_to <= 0) { + if (zh->sent_requests.head==0) { +// LOG_DEBUG(("Sending PING to %s (exceeded idle by %dms)", +// format_current_endpoint_info(zh),-send_to)); + int rc=send_ping(zh); + if (rc < 0){ + LOG_ERROR(("failed to send PING request (zk retcode=%d)",rc)); + return api_epilog(zh,rc); + } } send_to = zh->recv_timeout/3; } From 6c483a2d67d4aee937e0f4873e4b410b7008e703 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 17 Dec 2013 16:57:03 +0000 Subject: [PATCH 216/444] ZOOKEEPER-1388. Client side 'PathValidation' is missing for the multi-transaction api. (Rakesh R via marshallm, phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551625 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../main/org/apache/zookeeper/CreateMode.java | 6 +- src/java/main/org/apache/zookeeper/Op.java | 19 ++++ .../main/org/apache/zookeeper/ZooKeeper.java | 4 + .../zookeeper/test/MultiTransactionTest.java | 101 +++++++++++++++++- 5 files changed, 130 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e10da94a239..e0e58abcb09 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -194,6 +194,9 @@ BUGFIXES: ZOOKEEPER-1756. zookeeper_interest() in C client can return a timeval of 0 (Eric Lindvall via michim) + ZOOKEEPER-1388. Client side 'PathValidation' is missing for the + multi-transaction api. (Rakesh R via marshallm, phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/CreateMode.java b/src/java/main/org/apache/zookeeper/CreateMode.java index 6e9c89a0160..d87f410c84a 100644 --- a/src/java/main/org/apache/zookeeper/CreateMode.java +++ b/src/java/main/org/apache/zookeeper/CreateMode.java @@ -83,8 +83,10 @@ static public CreateMode fromFlag(int flag) throws KeeperException { case 3: return CreateMode.EPHEMERAL_SEQUENTIAL ; default: - LOG.error("Received an invalid flag value to convert to a CreateMode"); - throw new KeeperException.BadArgumentsException(); + String errMsg = "Received an invalid flag value: " + flag + + " to convert to a CreateMode"; + LOG.error(errMsg); + throw new KeeperException.BadArgumentsException(errMsg); } } } diff --git a/src/java/main/org/apache/zookeeper/Op.java b/src/java/main/org/apache/zookeeper/Op.java index ca9ec64583e..1bc20610142 100644 --- a/src/java/main/org/apache/zookeeper/Op.java +++ b/src/java/main/org/apache/zookeeper/Op.java @@ -18,6 +18,7 @@ package org.apache.zookeeper; import org.apache.jute.Record; +import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.proto.CheckVersionRequest; import org.apache.zookeeper.proto.CreateRequest; @@ -162,6 +163,18 @@ public String getPath() { */ abstract Op withChroot(String addRootPrefix); + /** + * Performs client path validations. + * + * @throws IllegalArgumentException + * if an invalid path is specified + * @throws KeeperException.BadArgumentsException + * if an invalid create mode flag is specified + */ + void validate() throws KeeperException { + PathUtils.validatePath(path); + } + ////////////////// // these internal classes are public, but should not generally be referenced. // @@ -222,6 +235,12 @@ public Record toRequestRecord() { Op withChroot(String path) { return new Create(path, data, acl, flags); } + + @Override + void validate() throws KeeperException { + CreateMode createMode = CreateMode.fromFlag(flags); + PathUtils.validatePath(getPath(), createMode.isSequential()); + } } public static class Delete extends Op { diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index 86c619d8d01..6385781f883 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -899,10 +899,14 @@ public void delete(final String path, int version) * partially succeeded if this exception is thrown. * @throws KeeperException If the operation could not be completed * due to some error in doing one of the specified ops. + * @throws IllegalArgumentException if an invalid path is specified * * @since 3.4.0 */ public List multi(Iterable ops) throws InterruptedException, KeeperException { + for (Op op : ops) { + op.validate(); + } // reconstructing transaction with the chroot prefix List transaction = new ArrayList(); for (Op op : ops) { diff --git a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java index 75b0fe3251e..de1d50fca88 100644 --- a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java +++ b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java @@ -60,7 +60,106 @@ public void setUp() throws Exception { zk = createClient(); } - + /** + * Test verifies the multi calls with invalid znode path + */ + @Test(timeout = 90000) + public void testInvalidPath() throws Exception { + // create with CreateMode + List opList = Arrays.asList(Op.create("/multi0", new byte[0], + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create( + "/multi1/", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (IllegalArgumentException e) { + // expected + } + + // create with valid sequential flag + opList = Arrays.asList(Op.create("/multi0", new byte[0], + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create( + "multi1/", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL_SEQUENTIAL.toFlag()), Op.create("/multi2", + new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (IllegalArgumentException e) { + // expected + } + + // check + opList = Arrays.asList(Op.check("/multi0", -1), + Op.check("/multi1/", 100), Op.check("/multi2", 5)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (IllegalArgumentException e) { + // expected + } + + // delete + opList = Arrays.asList(Op.delete("/multi0", -1), + Op.delete("/multi1/", 100), Op.delete("/multi2", 5)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (IllegalArgumentException e) { + // expected + } + + // setdata + opList = Arrays.asList(Op.setData("/multi0", new byte[0], -1), + Op.setData("/multi1/", new byte[0], -1), + Op.setData("/multi2", new byte[0], -1), + Op.setData("multi3", new byte[0], -1)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * Test verifies the multi calls with blank znode path + */ + @Test(timeout = 90000) + public void testBlankPath() throws Exception { + // delete + List opList = Arrays.asList(Op.delete("/multi0", -1), + Op.delete(null, 100), Op.delete("/multi2", 5), + Op.delete("", -1)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * Test verifies the multi.create with invalid createModeFlag + */ + @Test(timeout = 90000) + public void testInvalidCreateModeFlag() throws Exception { + int createModeFlag = 6789; + List opList = Arrays.asList(Op.create("/multi0", new byte[0], + Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create( + "/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, createModeFlag), + Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT)); + try { + zk.multi(opList); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (KeeperException.BadArgumentsException e) { + // expected + } + } + @Test public void testChRootCreateDelete() throws Exception { // creating the subtree for chRoot clients. From 54eefdb4fc57e18f28bbb520e4abb163de2f936a Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 18 Dec 2013 13:05:31 +0000 Subject: [PATCH 217/444] =?UTF-8?q?ZOOKEEPER-1841.=20problem=20in=20Quorum?= =?UTF-8?q?Test=20(Germ=C3=A1n=20via=20fpj)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551930 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../apache/zookeeper/test/FollowerTest.java | 233 ++++++++++++++++++ .../org/apache/zookeeper/test/QuorumTest.java | 164 ------------ 3 files changed, 235 insertions(+), 164 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/FollowerTest.java diff --git a/CHANGES.txt b/CHANGES.txt index e0e58abcb09..21742b8443b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -197,6 +197,8 @@ BUGFIXES: ZOOKEEPER-1388. Client side 'PathValidation' is missing for the multi-transaction api. (Rakesh R via marshallm, phunt) + ZOOKEEPER-1841. problem in QuorumTest (Germán via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/FollowerTest.java b/src/java/test/org/apache/zookeeper/test/FollowerTest.java new file mode 100644 index 00000000000..701d80638f0 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/FollowerTest.java @@ -0,0 +1,233 @@ +/** + * 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.test; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.zookeeper.AsyncCallback; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.Op; +import org.apache.zookeeper.OpResult; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.quorum.Leader; +import org.apache.zookeeper.server.quorum.LearnerHandler; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FollowerTest extends ZKTestCase { + private static final Logger LOG = LoggerFactory.getLogger(FollowerTest.class); + public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; + + volatile int counter = 0; + volatile int errors = 0; + + /** + * See ZOOKEEPER-790 for details + * */ + @Test + public void testFollowersStartAfterLeader() throws Exception { + QuorumUtil qu = new QuorumUtil(1); + CountdownWatcher watcher = new CountdownWatcher(); + qu.startQuorum(); + + int index = 1; + while(qu.getPeer(index).peer.leader == null) { + index++; + } + + ZooKeeper zk = new ZooKeeper( + "127.0.0.1:" + qu.getPeer((index == 1)?2:1).peer.getClientPort(), + ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(CONNECTION_TIMEOUT); + + // break the quorum + qu.shutdown(index); + + // Wait until we disconnect to proceed + watcher.waitForDisconnected(CONNECTION_TIMEOUT); + + // try to reestablish the quorum + qu.start(index); + + try{ + watcher.waitForConnected(30000); + } catch(TimeoutException e) { + Assert.fail("client could not connect to reestablished quorum: giving up after 30+ seconds."); + } + + zk.close(); + + qu.tearDown(); + } + + /** + * Tests if closeSession can be logged before a leader gets established, which + * could lead to a locked-out follower (see ZOOKEEPER-790). + * + * The test works as follows. It has a client connecting to a follower f and + * sending batches of 1,000 updates. The goal is that f has a zxid higher than + * all other servers in the initial leader election. This way we can crash and + * recover the follower so that the follower believes it is the leader once it + * recovers (LE optimization: once a server receives a message from all other + * servers, it picks a leader. + * + * It also makes the session timeout very short so that we force the false + * leader to close the session and write it to the log in the buggy code (before + * ZOOKEEPER-790). Once f drops leadership and finds the current leader, its epoch + * is higher, and it rejects the leader. Now, if we prevent the leader from closing + * the session by only starting up (see Leader.lead()) once it obtains a quorum of + * supporters, then f will find the current leader and support it because it won't + * have a highe epoch. + * + */ + @Test + public void testNoLogBeforeLeaderEstablishment () + throws Exception { + final Semaphore sem = new Semaphore(0); + System.setProperty("zookeeper.cnxTimeout", "50"); + + QuorumUtil qu = new QuorumUtil(2, 10); + qu.startQuorum(); + + + int index = 1; + while(qu.getPeer(index).peer.leader == null) { + index++; + } + + Leader leader = qu.getPeer(index).peer.leader; + + Assert.assertNotNull(leader); + + /* + * Reusing the index variable to select a follower to connect to + */ + index = (index == 1) ? 2 : 1; + + ZooKeeper zk = new DisconnectableZooKeeper( + "127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), + ClientBase.CONNECTION_TIMEOUT, new Watcher() { + public void process(WatchedEvent event) { } + }); + + zk.create("/blah", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + + for(int i = 0; i < 50000; i++) { + zk.setData("/blah", new byte[0], -1, new AsyncCallback.StatCallback() { + public void processResult(int rc, String path, Object ctx, + Stat stat) { + counter++; + if (rc != 0) { + errors++; + } + if(counter == 20000){ + sem.release(); + } + } + }, null); + + if(i == 5000){ + qu.shutdown(index); + LOG.info("Shutting down s1"); + } + if(i == 12000){ + qu.start(index); + LOG.info("Setting up server: " + index); + } + if((i % 1000) == 0){ + Thread.sleep(500); + } + } + + // Wait until all updates return + sem.tryAcquire(15, TimeUnit.SECONDS); + + // Verify that server is following and has the same epoch as the leader + Assert.assertTrue("Not following", qu.getPeer(index).peer.follower != null); + long epochF = (qu.getPeer(index).peer.getActiveServer().getZxid() >> 32L); + long epochL = (leader.getEpoch() >> 32L); + Assert.assertTrue("Zxid: " + qu.getPeer(index).peer.getActiveServer().getZxid() + + "Current epoch: " + epochF, epochF == epochL); + + qu.tearDown(); + + } + + // skip superhammer and clientcleanup as they are too expensive for quorum + + /** + * Tests if a multiop submitted to a non-leader propagates to the leader properly + * (see ZOOKEEPER-1124). + * + * The test works as follows. It has a client connect to a follower and submit a multiop + * to the follower. It then verifies that the multiop successfully gets committed by the leader. + * + * Without the fix in ZOOKEEPER-1124, this fails with a ConnectionLoss KeeperException. + */ + @Test + public void testMultiToFollower() throws Exception { + QuorumUtil qu = new QuorumUtil(1); + CountdownWatcher watcher = new CountdownWatcher(); + qu.startQuorum(); + + int index = 1; + while(qu.getPeer(index).peer.leader == null) { + index++; + } + + ZooKeeper zk = new ZooKeeper( + "127.0.0.1:" + qu.getPeer((index == 1)?2:1).peer.getClientPort(), + ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(CONNECTION_TIMEOUT); + + List results = new ArrayList(); + + results = zk.multi(Arrays.asList( + Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), + Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), + Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) + )); + zk.getData("/multi0", false, null); + zk.getData("/multi1", false, null); + zk.getData("/multi2", false, null); + + zk.close(); + + qu.tearDown(); + } +} diff --git a/src/java/test/org/apache/zookeeper/test/QuorumTest.java b/src/java/test/org/apache/zookeeper/test/QuorumTest.java index 3b31786a90f..6f827ec7dba 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumTest.java @@ -293,168 +293,4 @@ ClientBase.CONNECTION_TIMEOUT, new DiscoWatcher(), zk.close(); } - /** - * See ZOOKEEPER-790 for details - * */ - @Test - public void testFollowersStartAfterLeader() throws Exception { - QuorumUtil qu = new QuorumUtil(1); - CountdownWatcher watcher = new CountdownWatcher(); - qu.startQuorum(); - - int index = 1; - while(qu.getPeer(index).peer.leader == null) - index++; - - ZooKeeper zk = new ZooKeeper( - "127.0.0.1:" + qu.getPeer((index == 1)?2:1).peer.getClientPort(), - ClientBase.CONNECTION_TIMEOUT, watcher); - watcher.waitForConnected(CONNECTION_TIMEOUT); - - // break the quorum - qu.shutdown(index); - - // Wait until we disconnect to proceed - watcher.waitForDisconnected(CONNECTION_TIMEOUT); - - // try to reestablish the quorum - qu.start(index); - - try{ - watcher.waitForConnected(30000); - } catch(TimeoutException e) { - Assert.fail("client could not connect to reestablished quorum: giving up after 30+ seconds."); - } - - zk.close(); - } - - /** - * Tests if closeSession can be logged before a leader gets established, which - * could lead to a locked-out follower (see ZOOKEEPER-790). - * - * The test works as follows. It has a client connecting to a follower f and - * sending batches of 1,000 updates. The goal is that f has a zxid higher than - * all other servers in the initial leader election. This way we can crash and - * recover the follower so that the follower believes it is the leader once it - * recovers (LE optimization: once a server receives a message from all other - * servers, it picks a leader. - * - * It also makes the session timeout very short so that we force the false - * leader to close the session and write it to the log in the buggy code (before - * ZOOKEEPER-790). Once f drops leadership and finds the current leader, its epoch - * is higher, and it rejects the leader. Now, if we prevent the leader from closing - * the session by only starting up (see Leader.lead()) once it obtains a quorum of - * supporters, then f will find the current leader and support it because it won't - * have a highe epoch. - * - */ - @Test - public void testNoLogBeforeLeaderEstablishment () - throws IOException, InterruptedException, KeeperException{ - final Semaphore sem = new Semaphore(0); - - QuorumUtil qu = new QuorumUtil(2, 10); - qu.startQuorum(); - - - int index = 1; - while(qu.getPeer(index).peer.leader == null) - index++; - - Leader leader = qu.getPeer(index).peer.leader; - - Assert.assertNotNull(leader); - - /* - * Reusing the index variable to select a follower to connect to - */ - index = (index == 1) ? 2 : 1; - - ZooKeeper zk = new DisconnectableZooKeeper( - "127.0.0.1:" + qu.getPeer(index).peer.getClientPort(), - ClientBase.CONNECTION_TIMEOUT, new Watcher() { - public void process(WatchedEvent event) { } - }); - - zk.create("/blah", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - - for(int i = 0; i < 50000; i++) { - zk.setData("/blah", new byte[0], -1, new AsyncCallback.StatCallback() { - public void processResult(int rc, String path, Object ctx, - Stat stat) { - counter++; - if (rc != 0) { - errors++; - } - if(counter == 20000){ - sem.release(); - } - } - }, null); - - if(i == 5000){ - qu.shutdown(index); - LOG.info("Shutting down s1"); - } - if(i == 12000){ - qu.start(index); - LOG.info("Setting up server: " + index); - } - if((i % 1000) == 0){ - Thread.sleep(500); - } - } - - // Wait until all updates return - sem.tryAcquire(15, TimeUnit.SECONDS); - - // Verify that server is following and has the same epoch as the leader - Assert.assertTrue("Not following", qu.getPeer(index).peer.follower != null); - long epochF = (qu.getPeer(index).peer.getActiveServer().getZxid() >> 32L); - long epochL = (leader.getEpoch() >> 32L); - Assert.assertTrue("Zxid: " + qu.getPeer(index).peer.getActiveServer().getZxid() + - "Current epoch: " + epochF, epochF == epochL); - - } - - // skip superhammer and clientcleanup as they are too expensive for quorum - - /** - * Tests if a multiop submitted to a non-leader propagates to the leader properly - * (see ZOOKEEPER-1124). - * - * The test works as follows. It has a client connect to a follower and submit a multiop - * to the follower. It then verifies that the multiop successfully gets committed by the leader. - * - * Without the fix in ZOOKEEPER-1124, this fails with a ConnectionLoss KeeperException. - */ - @Test - public void testMultiToFollower() throws Exception { - QuorumUtil qu = new QuorumUtil(1); - CountdownWatcher watcher = new CountdownWatcher(); - qu.startQuorum(); - - int index = 1; - while(qu.getPeer(index).peer.leader == null) - index++; - - ZooKeeper zk = new ZooKeeper( - "127.0.0.1:" + qu.getPeer((index == 1)?2:1).peer.getClientPort(), - ClientBase.CONNECTION_TIMEOUT, watcher); - watcher.waitForConnected(CONNECTION_TIMEOUT); - - List results = new ArrayList(); - - results = zk.multi(Arrays.asList( - Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), - Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), - Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) - )); - zk.getData("/multi0", false, null); - zk.getData("/multi1", false, null); - zk.getData("/multi2", false, null); - - zk.close(); - } } From 5420ead4bb71dfa83f09c4cce72fa72e649eac62 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 18 Dec 2013 15:48:19 +0000 Subject: [PATCH 218/444] ZOOKEEPER-1733. FLETest#testLE is flaky on windows boxes (michim via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551985 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/test/org/apache/zookeeper/test/FLETest.java | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 21742b8443b..6ab175e6e42 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -199,6 +199,9 @@ BUGFIXES: ZOOKEEPER-1841. problem in QuorumTest (Germán via fpj) + ZOOKEEPER-1733. FLETest#testLE is flaky on windows boxes + (michim via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/FLETest.java b/src/java/test/org/apache/zookeeper/test/FLETest.java index b9a35d0222a..d9e4e7976e5 100644 --- a/src/java/test/org/apache/zookeeper/test/FLETest.java +++ b/src/java/test/org/apache/zookeeper/test/FLETest.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Random; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,6 +75,7 @@ int countVotes(HashSet hs, long id) { volatile long leader = -1; //volatile int round = 1; Random rand = new Random(); + Set joinedThreads; @Before public void setUp() throws Exception { @@ -87,6 +89,7 @@ public void setUp() throws Exception { port = new int[count]; successCount = 0; finalObj = new Object(); + joinedThreads = new HashSet(); } @After @@ -180,6 +183,7 @@ public void run() { if(leader == i){ synchronized(finalObj){ successCount++; + joinedThreads.add((long)i); if(successCount > (count/2)) finalObj.notify(); } @@ -224,6 +228,7 @@ public void run() { if (leader == votes[i].getId()) { synchronized(finalObj){ successCount++; + joinedThreads.add((long)i); if(successCount > (count/2)) finalObj.notify(); } break; @@ -309,8 +314,10 @@ public void testLE() throws Exception { Assert.fail("Fewer than a a majority has joined"); } - if(threads.get((int) leader).isAlive()){ - Assert.fail("Leader hasn't joined: " + leader); + synchronized(finalObj){ + if(!joinedThreads.contains(leader)){ + Assert.fail("Leader hasn't joined: " + leader); + } } } From 058185d6359393e3898acd745f341f1799b276ce Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 18 Dec 2013 15:52:16 +0000 Subject: [PATCH 219/444] ZOOKEEPER-1733. FLETest#testLE is flaky on windows boxes (michim, Jeffrey Zhong via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1551987 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6ab175e6e42..325d29e8293 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -200,7 +200,7 @@ BUGFIXES: ZOOKEEPER-1841. problem in QuorumTest (Germán via fpj) ZOOKEEPER-1733. FLETest#testLE is flaky on windows boxes - (michim via fpj) + (michim, Jeffrey Zhong via fpj) IMPROVEMENTS: From 7b272f5d5e4d1c170bc72c9b79a462517c06d6d4 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 19 Dec 2013 22:36:08 +0000 Subject: [PATCH 220/444] =?UTF-8?q?ZOOKEEPER-1849.=20Need=20to=20properly?= =?UTF-8?q?=20tear=20down=20tests=20in=20various=20=20=20cases=20(Germ?= =?UTF-8?q?=C3=A1n=20via=20fpj)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1552447 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 6 +++++- .../org/apache/zookeeper/JUnit4ZKTestRunner.java | 8 ++++++++ .../test/org/apache/zookeeper/server/CRCTest.java | 1 + .../test/org/apache/zookeeper/test/ACLCountTest.java | 4 ++-- src/java/test/org/apache/zookeeper/test/ACLTest.java | 5 +++-- .../apache/zookeeper/test/ClientPortBindTest.java | 1 + .../apache/zookeeper/test/InvalidSnapshotTest.java | 1 + .../org/apache/zookeeper/test/LoadFromLogTest.java | 3 +++ .../test/org/apache/zookeeper/test/MaxCnxnsTest.java | 12 +++++++++++- src/java/test/org/apache/zookeeper/test/OOMTest.java | 1 + .../test/org/apache/zookeeper/test/PurgeTxnTest.java | 1 + .../test/org/apache/zookeeper/test/RecoveryTest.java | 3 +++ .../org/apache/zookeeper/test/RepeatStartupTest.java | 1 + .../zookeeper/test/RestoreCommittedLogTest.java | 2 ++ .../test/org/apache/zookeeper/test/SessionTest.java | 4 +++- 15 files changed, 46 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 325d29e8293..ceaac2c5ea3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -199,9 +199,13 @@ BUGFIXES: ZOOKEEPER-1841. problem in QuorumTest (Germán via fpj) - ZOOKEEPER-1733. FLETest#testLE is flaky on windows boxes + ZOOKEEPER-1733 FLETest#testLE is flaky oo.l windows boxes (michim, Jeffrey Zhong via fpj) + ZOOKEEPER-1849. Need to properly tear down tests in various + cases (Germán via fpj) + + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java b/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java index 188c9f746af..588dcd19867 100644 --- a/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java +++ b/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java @@ -50,6 +50,14 @@ 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) { LOG.info("TEST METHOD FAILED " + name, t); throw t; diff --git a/src/java/test/org/apache/zookeeper/server/CRCTest.java b/src/java/test/org/apache/zookeeper/server/CRCTest.java index 36f1c6285ce..2b7fb4697a1 100644 --- a/src/java/test/org/apache/zookeeper/server/CRCTest.java +++ b/src/java/test/org/apache/zookeeper/server/CRCTest.java @@ -131,6 +131,7 @@ public void testChecksums() throws Exception { zk.close(); } f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); diff --git a/src/java/test/org/apache/zookeeper/test/ACLCountTest.java b/src/java/test/org/apache/zookeeper/test/ACLCountTest.java index 772dc920d70..88b8869adf3 100644 --- a/src/java/test/org/apache/zookeeper/test/ACLCountTest.java +++ b/src/java/test/org/apache/zookeeper/test/ACLCountTest.java @@ -110,8 +110,8 @@ public void testAclCount() throws Exception { Assert.assertTrue(false); } - Assert.assertTrue(true); - + f.shutdown(); + zks.shutdown(); } diff --git a/src/java/test/org/apache/zookeeper/test/ACLTest.java b/src/java/test/org/apache/zookeeper/test/ACLTest.java index 51ef4f9d329..793265c510d 100644 --- a/src/java/test/org/apache/zookeeper/test/ACLTest.java +++ b/src/java/test/org/apache/zookeeper/test/ACLTest.java @@ -71,7 +71,7 @@ public void testDisconnectedAddAuth() throws Exception { } } finally { f.shutdown(); - + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); @@ -122,6 +122,7 @@ public void testAcls() throws Exception { } finally { // now shutdown the server and restart it f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); } @@ -157,7 +158,7 @@ public void testAcls() throws Exception { zk.close(); } finally { f.shutdown(); - + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); diff --git a/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java b/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java index dede0ccb6f4..bf83f14ac3f 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java @@ -99,6 +99,7 @@ public void testBindByAddress() throws Exception { zk.close(); } finally { f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, diff --git a/src/java/test/org/apache/zookeeper/test/InvalidSnapshotTest.java b/src/java/test/org/apache/zookeeper/test/InvalidSnapshotTest.java index 191260b2cf5..c5a7b4fcaf8 100644 --- a/src/java/test/org/apache/zookeeper/test/InvalidSnapshotTest.java +++ b/src/java/test/org/apache/zookeeper/test/InvalidSnapshotTest.java @@ -107,6 +107,7 @@ public void testSnapshot() throws Exception { zk.close(); } f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); diff --git a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java index aefc0eb8e7c..c01a4326243 100644 --- a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java +++ b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java @@ -115,6 +115,7 @@ public void testLoad() throws Exception { }while(itr.next()); Assert.assertTrue("processed all transactions. " + expectedZxid + " == " + TOTAL_TRANSACTIONS, (expectedZxid == TOTAL_TRANSACTIONS)); + zks.shutdown(); } @@ -366,6 +367,7 @@ public void testRestore() throws Exception { + " expected " + NUM_MESSAGES, (children.length == NUM_MESSAGES)); f.shutdown(); + zks.shutdown(); } /** @@ -433,5 +435,6 @@ public void testRestoreWithTransactionErrors() throws Exception { .waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); f.shutdown(); + zks.shutdown(); } } \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/test/MaxCnxnsTest.java b/src/java/test/org/apache/zookeeper/test/MaxCnxnsTest.java index 38e84fa82b3..0a31ac9b7cc 100644 --- a/src/java/test/org/apache/zookeeper/test/MaxCnxnsTest.java +++ b/src/java/test/org/apache/zookeeper/test/MaxCnxnsTest.java @@ -51,6 +51,7 @@ public CnxnThread(int i) { } public void run() { + SocketChannel sChannel = null; try { /* * For future unwary socket programmers: although connect 'blocks' it @@ -58,7 +59,7 @@ public void run() { * you can not assume that all the sockets are connected at the end of * this for loop. */ - SocketChannel sChannel = SocketChannel.open(); + sChannel = SocketChannel.open(); sChannel.connect(new InetSocketAddress(host,port)); // Construct a connection request ConnectRequest conReq = new ConnectRequest(0, 0, @@ -97,6 +98,15 @@ public void run() { catch (IOException io) { // "Connection reset by peer" } + finally { + if (sChannel != null) { + try { + sChannel.close(); + } + catch (Exception e) { + } + } + } } } diff --git a/src/java/test/org/apache/zookeeper/test/OOMTest.java b/src/java/test/org/apache/zookeeper/test/OOMTest.java index 808f7d1cb30..465df98380b 100644 --- a/src/java/test/org/apache/zookeeper/test/OOMTest.java +++ b/src/java/test/org/apache/zookeeper/test/OOMTest.java @@ -98,6 +98,7 @@ public void testOOM() throws IOException, InterruptedException, KeeperException hog.get(0)[0] = (byte) 1; f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown("127.0.0.1:" + PORT, CONNECTION_TIMEOUT)); diff --git a/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java index 8c4c9262771..2f8fec54f05 100644 --- a/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java +++ b/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java @@ -78,6 +78,7 @@ public void testPurge() throws Exception { } } Assert.assertTrue("exactly 3 snapshots ", (numSnaps == 3)); + zks.shutdown(); } public void process(WatchedEvent event) { diff --git a/src/java/test/org/apache/zookeeper/test/RecoveryTest.java b/src/java/test/org/apache/zookeeper/test/RecoveryTest.java index 2127e98c93d..c084a68fcb3 100644 --- a/src/java/test/org/apache/zookeeper/test/RecoveryTest.java +++ b/src/java/test/org/apache/zookeeper/test/RecoveryTest.java @@ -105,6 +105,7 @@ public void testRecovery() throws Exception { } f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); @@ -142,6 +143,7 @@ public void testRecovery() throws Exception { } } f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, @@ -181,6 +183,7 @@ public void testRecovery() throws Exception { zk.close(); f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, diff --git a/src/java/test/org/apache/zookeeper/test/RepeatStartupTest.java b/src/java/test/org/apache/zookeeper/test/RepeatStartupTest.java index 3421004f78f..f714a2caf13 100644 --- a/src/java/test/org/apache/zookeeper/test/RepeatStartupTest.java +++ b/src/java/test/org/apache/zookeeper/test/RepeatStartupTest.java @@ -60,6 +60,7 @@ public void testFail() throws Exception { ClientBase.waitForServerUp("127.0.0.1:" + PORT, QuorumTest.CONNECTION_TIMEOUT)); factory.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown("127.0.0.1:" + PORT, QuorumTest.CONNECTION_TIMEOUT)); diff --git a/src/java/test/org/apache/zookeeper/test/RestoreCommittedLogTest.java b/src/java/test/org/apache/zookeeper/test/RestoreCommittedLogTest.java index 9a9ff831386..65cc0e2f3f4 100644 --- a/src/java/test/org/apache/zookeeper/test/RestoreCommittedLogTest.java +++ b/src/java/test/org/apache/zookeeper/test/RestoreCommittedLogTest.java @@ -70,6 +70,7 @@ public void testRestoreCommittedLog() throws Exception { zk.close(); } f.shutdown(); + zks.shutdown(); Assert.assertTrue("waiting for server to shutdown", ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); @@ -80,6 +81,7 @@ public void testRestoreCommittedLog() throws Exception { int logsize = committedLog.size(); LOG.info("committedLog size = " + logsize); Assert.assertTrue("log size != 0", (logsize != 0)); + zks.shutdown(); } public void process(WatchedEvent event) { diff --git a/src/java/test/org/apache/zookeeper/test/SessionTest.java b/src/java/test/org/apache/zookeeper/test/SessionTest.java index df9bd547c31..0740ebbd1e9 100644 --- a/src/java/test/org/apache/zookeeper/test/SessionTest.java +++ b/src/java/test/org/apache/zookeeper/test/SessionTest.java @@ -57,6 +57,7 @@ public class SessionTest extends ZKTestCase { PortAssignment.unique(); private ServerCnxnFactory serverFactory; + private ZooKeeperServer zs; private CountDownLatch startSignal; @@ -71,7 +72,7 @@ public void setUp() throws Exception { } ClientBase.setupTestEnv(); - ZooKeeperServer zs = new ZooKeeperServer(tmpDir, tmpDir, TICK_TIME); + zs = new ZooKeeperServer(tmpDir, tmpDir, TICK_TIME); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); serverFactory = ServerCnxnFactory.createFactory(PORT, -1); @@ -85,6 +86,7 @@ public void setUp() throws Exception { @After public void tearDown() throws Exception { serverFactory.shutdown(); + zs.shutdown(); Assert.assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); From 05bc123a83d0e355f703001c78b3a54988c678e4 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 20 Dec 2013 01:29:22 +0000 Subject: [PATCH 221/444] ZOOKEEPER-1430. add maven deploy support to the build (Giridharan Kesavan via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1552470 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ build.xml | 112 ++++++++++++++++++++++++++++++++++++++-------------- ivy.xml | 3 ++ 3 files changed, 88 insertions(+), 30 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ceaac2c5ea3..1f045f4f893 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -244,6 +244,9 @@ IMPROVEMENTS: ZOOKEEPER-1715. Upgrade netty version (Sean Bridges via michim) + ZOOKEEPER-1430. add maven deploy support to the build + (Giridharan Kesavan via phunt) + Release 3.4.5 - 2012-09-30 Backward compatible changes: diff --git a/build.xml b/build.xml index e608f9c0b26..a86456a2256 100644 --- a/build.xml +++ b/build.xml @@ -22,12 +22,15 @@ xmlns:ivy="antlib:org.apache.ivy.ant" xmlns:artifact="antlib:org.apache.maven.artifact.ant" xmlns:maven="antlib:org.apache.maven.artifact.ant"> + + + - + @@ -177,6 +180,24 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + + + + + + + + + + + + + + @@ -365,6 +386,11 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + + + + @@ -688,7 +714,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + @@ -703,7 +729,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + @@ -776,7 +802,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + @@ -802,26 +828,18 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + pomfile="${dist.maven.dir}/${name}.pom"> - - + + - - - - - - - - + tofile="${dist.maven.dir}/${final.name}-tests.jar"/> + + @@ -1095,30 +1113,64 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - + - + The version is ${zookeeper-pom.version} diff --git a/ivy.xml b/ivy.xml index 8cf03f8c987..561832ae6d5 100644 --- a/ivy.xml +++ b/ivy.xml @@ -29,6 +29,7 @@ + @@ -42,6 +43,8 @@ + + From 7fc3de3a05c9ace88b0e6b914750b2712f43f7c0 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 27 Dec 2013 13:29:08 +0000 Subject: [PATCH 222/444] =?UTF-8?q?ZOOKEEPER-1179.=20NettyServerCnxn=20doe?= =?UTF-8?q?s=20not=20properly=20close=20=20=20socket=20on=204=20letter=20w?= =?UTF-8?q?ord=20requests=20(Rakesh=20R,=20Germ=C3=A1n=20Blanco=20=20=20vi?= =?UTF-8?q?a=20fpj)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1553672 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 + .../zookeeper/server/NettyServerCnxn.java | 2 +- .../zookeeper/server/NettyServerCnxnTest.java | 103 ++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 1f045f4f893..bf2d6d4faaa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -205,6 +205,10 @@ BUGFIXES: ZOOKEEPER-1849. Need to properly tear down tests in various cases (Germán via fpj) + ZOOKEEPER-1179. NettyServerCnxn does not properly close + socket on 4 letter word requests (Rakesh R, Germán Blanco + via fpj) + IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index cf5cd7ee652..cf43c8b22d8 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -215,7 +215,7 @@ public void enableRecv() { @Override public void sendBuffer(ByteBuffer sendBuffer) { if (sendBuffer == ServerCnxnFactory.closeConn) { - channel.close(); + close(); return; } channel.write(wrappedBuffer(sendBuffer)); diff --git a/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java b/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java new file mode 100644 index 00000000000..19fcbd0b6f5 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java @@ -0,0 +1,103 @@ +/** + * 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.server; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.Assert; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.NettyServerCnxnFactory.CnxnChannelHandler; +import org.apache.zookeeper.test.ClientBase; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Test verifies the behavior of NettyServerCnxn which represents a connection + * from a client to the server. + */ +public class NettyServerCnxnTest extends ClientBase { + private static final Logger LOG = LoggerFactory + .getLogger(NettyServerCnxnTest.class); + + @Override + public void setUp() throws Exception { + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, + "org.apache.zookeeper.server.NettyServerCnxnFactory"); + super.setUp(); + } + + /** + * Test verifies the channel closure - while closing the channel + * servercnxnfactory should remove all channel references to avoid + * duplicate channel closure. Duplicate closure may result in + * indefinite hanging due to netty open issue: + * + * {@see https://issues.jboss.org/browse/NETTY-412} + */ + @Test(timeout = 30000) + public void testSendCloseSession() throws Exception { + Assert.assertTrue( + "Didn't instantiate ServerCnxnFactory with NettyServerCnxnFactory!", + serverFactory instanceof NettyServerCnxnFactory); + + NettyServerCnxnFactory nettyServerFactory = (NettyServerCnxnFactory) serverFactory; + final CountDownLatch channelLatch = new CountDownLatch(1); + CnxnChannelHandler channelHandler = nettyServerFactory.new CnxnChannelHandler() { + @Override + public void channelDisconnected(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + LOG.info("Recieves channel disconnected event"); + channelLatch.countDown(); + } + }; + LOG.info("Adding custom channel handler for simulation"); + nettyServerFactory.bootstrap.getPipeline().remove("servercnxnfactory"); + nettyServerFactory.bootstrap.getPipeline().addLast("servercnxnfactory", + channelHandler); + + final ZooKeeper zk = createClient(); + final String path = "/a"; + try { + // make sure zkclient works + zk.create(path, "test".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Assert.assertNotNull("Didn't create znode:" + path, + zk.exists(path, false)); + Iterable connections = serverFactory.getConnections(); + Assert.assertEquals("Mismatch in number of live connections!", 1, + serverFactory.getNumAliveConnections()); + for (ServerCnxn serverCnxn : connections) { + serverCnxn.sendCloseSession(); + } + LOG.info("Waiting for the channel disconnected event"); + channelLatch.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertEquals("Mismatch in number of live connections!", 0, + serverFactory.getNumAliveConnections()); + } finally { + zk.close(); + } + } +} From 80ef32504a46387e051ca9b58e1b5b58b387b358 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 27 Dec 2013 16:50:46 +0000 Subject: [PATCH 223/444] =?UTF-8?q?ZOOKEEPER-1179.=20NettyServerCnxn=20doe?= =?UTF-8?q?s=20not=20properly=20close=20socket=20on=204=20letter=20word=20?= =?UTF-8?q?requests.=20(Rakesh=20R,=20Germ=C3=A1n=20Blanco=20via=20fpj)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1553694 13f79535-47bb-0310-9956-ffa450edef68 --- .../test/org/apache/zookeeper/server/NettyServerCnxnTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java b/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java index 19fcbd0b6f5..52265c6607c 100644 --- a/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java +++ b/src/java/test/org/apache/zookeeper/server/NettyServerCnxnTest.java @@ -53,9 +53,9 @@ public void setUp() throws Exception { * Test verifies the channel closure - while closing the channel * servercnxnfactory should remove all channel references to avoid * duplicate channel closure. Duplicate closure may result in - * indefinite hanging due to netty open issue: + * indefinite hanging due to netty open issue. * - * {@see https://issues.jboss.org/browse/NETTY-412} + * @see NETTY-412 */ @Test(timeout = 30000) public void testSendCloseSession() throws Exception { From 68a0f7315d59f4a8090b3a08bb8dab8453bb2fe9 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 3 Jan 2014 00:23:23 +0000 Subject: [PATCH 224/444] ZOOKEEPER-1852. ServerCnxnFactory instance is not properly cleanedup (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1554979 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../org/apache/zookeeper/test/ClientBase.java | 42 ++++++++++++------- .../apache/zookeeper/test/TruncateTest.java | 8 +++- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bf2d6d4faaa..101cc026de6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -209,6 +209,9 @@ BUGFIXES: socket on 4 letter word requests (Rakesh R, Germán Blanco via fpj) + ZOOKEEPER-1852. ServerCnxnFactory instance is not properly + cleanedup (Rakesh R via fpj) + IMPROVEMENTS: diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index dd53252d237..45265802bcd 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -324,20 +324,25 @@ private static int getPort(String hostPort) { return Integer.parseInt(portstr); } - public static ServerCnxnFactory createNewServerInstance(File dataDir, - ServerCnxnFactory factory, String hostPort, int maxCnxns) - throws IOException, InterruptedException - { + public static void startServerInstance(File dataDir, + ServerCnxnFactory factory, String hostPort) throws IOException, + InterruptedException { + final int port = getPort(hostPort); + LOG.info("STARTING server instance 127.0.0.1:{}", port); ZooKeeperServer zks = new ZooKeeperServer(dataDir, dataDir, 3000); - final int PORT = getPort(hostPort); - if (factory == null) { - factory = ServerCnxnFactory.createFactory(PORT, maxCnxns); - } factory.startup(zks); - Assert.assertTrue("waiting for server up", - ClientBase.waitForServerUp("127.0.0.1:" + PORT, - CONNECTION_TIMEOUT)); + Assert.assertTrue("waiting for server up", ClientBase.waitForServerUp( + "127.0.0.1:" + port, CONNECTION_TIMEOUT)); + } + public static ServerCnxnFactory createNewServerInstance( + ServerCnxnFactory factory, String hostPort, int maxCnxns) + throws IOException, InterruptedException { + final int port = getPort(hostPort); + LOG.info("CREATING server instance 127.0.0.1:{}", port); + if (factory == null) { + factory = ServerCnxnFactory.createFactory(port, maxCnxns); + } return factory; } @@ -345,15 +350,18 @@ static void shutdownServerInstance(ServerCnxnFactory factory, String hostPort) { if (factory != null) { - ZKDatabase zkDb; + ZKDatabase zkDb = null; { ZooKeeperServer zs = getServer(factory); - - zkDb = zs.getZKDatabase(); + if (zs != null) { + zkDb = zs.getZKDatabase(); + } } factory.shutdown(); try { - zkDb.close(); + if (zkDb != null) { + zkDb.close(); + } } catch (IOException ie) { LOG.warn("Error closing logs ", ie); } @@ -411,7 +419,9 @@ public void setUp() throws Exception { protected void startServer() throws Exception { LOG.info("STARTING server"); - serverFactory = createNewServerInstance(tmpDir, serverFactory, hostPort, maxCnxns); + serverFactory = createNewServerInstance(serverFactory, hostPort, + maxCnxns); + startServerInstance(tmpDir, serverFactory, hostPort); // ensure that only server and data bean are registered JMXEnv.ensureOnly("InMemoryDataTree", "StandaloneServer_port"); } diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index 14a793c1eee..48078e7bff2 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -128,7 +128,10 @@ private void append(ZKDatabase zkdb, int i) throws IOException { public void testTruncate() throws IOException, InterruptedException, KeeperException { // Prime the server that is going to come in late with 50 txns String hostPort = "127.0.0.1:" + baseHostPort; - ServerCnxnFactory factory = ClientBase.createNewServerInstance(dataDir1, null, hostPort, 100); + int maxCnxns = 100; + ServerCnxnFactory factory = ClientBase.createNewServerInstance(null, + hostPort, maxCnxns); + ClientBase.startServerInstance(dataDir1, factory, hostPort); ClientBase.shutdownServerInstance(factory, hostPort); // standalone starts with 0 epoch while quorum starts with 1 @@ -136,7 +139,8 @@ public void testTruncate() throws IOException, InterruptedException, KeeperExcep File newfile = new File(new File(dataDir1, "version-2"), "snapshot.100000000"); origfile.renameTo(newfile); - factory = ClientBase.createNewServerInstance(dataDir1, null, hostPort, 100); + factory = ClientBase.createNewServerInstance(null, hostPort, maxCnxns); + ClientBase.startServerInstance(dataDir1, factory, hostPort); ZooKeeper zk = new ZooKeeper(hostPort, 15000, nullWatcher); for(int i = 0; i < 50; i++) { From 89448077733a8d025734b79346e403cbcdaa9cea Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 3 Jan 2014 00:44:27 +0000 Subject: [PATCH 225/444] ZOOKEEPER-1852. ServerCnxnFactory instance is not properly cleanedup (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1554983 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/zookeeper/test/ClientBase.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 45265802bcd..c17b8129cf1 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -324,6 +324,9 @@ private static int getPort(String hostPort) { return Integer.parseInt(portstr); } + /** + * Starting the given server instance + */ public static void startServerInstance(File dataDir, ServerCnxnFactory factory, String hostPort) throws IOException, InterruptedException { @@ -335,6 +338,20 @@ public static void startServerInstance(File dataDir, "127.0.0.1:" + port, CONNECTION_TIMEOUT)); } + /** + * This method instantiates a new server. Starting of the server + * instance has been moved to a separate method + * {@link ClientBase#startServerInstance(File, ServerCnxnFactory, String)}. + * Because any exception on starting the server would leave the server + * running and the caller would not be able to shutdown the instance. This + * may affect other test cases. + * + * @return newly created server instance + * + * @see ZOOKEEPER-1852 + * for more information. + */ public static ServerCnxnFactory createNewServerInstance( ServerCnxnFactory factory, String hostPort, int maxCnxns) throws IOException, InterruptedException { From ca8d611d2db1234bfc0a3f64f2a2a8a15021c8a3 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 9 Jan 2014 19:22:57 +0000 Subject: [PATCH 226/444] ZOOKEEPER-1414. QuorumPeerMainTest.testQuorum, testBadPackets are failing intermittently (Rakesh R via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1556926 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../apache/zookeeper/server/quorum/QuorumPeerMainTest.java | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 101cc026de6..8897781b826 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -212,6 +212,8 @@ BUGFIXES: ZOOKEEPER-1852. ServerCnxnFactory instance is not properly cleanedup (Rakesh R via fpj) + ZOOKEEPER-1414. QuorumPeerMainTest.testQuorum, testBadPackets are failing + intermittently (Rakesh R via michim) IMPROVEMENTS: 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 ffb3f0207eb..6753adc180f 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -98,7 +98,7 @@ public void testQuorum() throws Exception { ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT, this); - + waitForOne(zk, States.CONNECTED); zk.create("/foo_q1", "foobar1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Assert.assertEquals(new String(zk.getData("/foo_q1", null, null)), "foobar1"); @@ -106,7 +106,7 @@ public void testQuorum() throws Exception { zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT, this); - + waitForOne(zk, States.CONNECTED); zk.create("/foo_q2", "foobar2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Assert.assertEquals(new String(zk.getData("/foo_q2", null, null)), "foobar2"); @@ -578,7 +578,7 @@ public void testBadPackets() throws Exception { ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT, this); - + waitForOne(zk, States.CONNECTED); zk.create("/foo_q1", "foobar1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Assert.assertEquals(new String(zk.getData("/foo_q1", null, null)), "foobar1"); From a6528185040b0f782c3d59650b694cf478e01b6a Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 9 Jan 2014 21:03:16 +0000 Subject: [PATCH 227/444] =?UTF-8?q?ZOOKEEPER-1057.=20zookeeper=20c-client,?= =?UTF-8?q?=20connection=20to=20offline=20server=20fails=20to=20successful?= =?UTF-8?q?ly=20fallback=20to=20second=20zk=20host=20(Germ=C3=A1n=20Blanco?= =?UTF-8?q?=20via=20michim)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1556949 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/tests/TestClient.cc | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 8897781b826..0dcaad52579 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -215,6 +215,9 @@ BUGFIXES: ZOOKEEPER-1414. QuorumPeerMainTest.testQuorum, testBadPackets are failing intermittently (Rakesh R via michim) + ZOOKEEPER-1057. zookeeper c-client, connection to offline server fails to + successfully fallback to second zk host (Germán Blanco via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/c/tests/TestClient.cc b/src/c/tests/TestClient.cc index ba12259583c..154fbed9eab 100644 --- a/src/c/tests/TestClient.cc +++ b/src/c/tests/TestClient.cc @@ -186,6 +186,7 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture CPPUNIT_TEST_SUITE(Zookeeper_simpleSystem); CPPUNIT_TEST(testAsyncWatcherAutoReset); CPPUNIT_TEST(testDeserializeString); + CPPUNIT_TEST(testFirstServerDown); #ifdef THREADED CPPUNIT_TEST(testNullData); #ifdef ZOO_IPV6_ENABLED @@ -287,6 +288,17 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture struct String_vector str_vec = {0, NULL}; zrc = zoo_wget_children(zzh, "/mytest", default_zoo_watcher, NULL, &str_vec); } + + /** ZOOKEEPER-1057 This checks that the client connects to the second server when the first is not reachable **/ + void testFirstServerDown() { + watchctx_t ctx; + + zoo_deterministic_conn_order(true); + + zhandle_t* zk = createClient("127.0.0.1:22182,127.0.0.1:22181", &ctx); + CPPUNIT_ASSERT(zk != 0); + CPPUNIT_ASSERT(ctx.waitForConnected(zk)); + } /** this checks for a deadlock in calling zookeeper_close and calls from a default watcher that might get triggered just when zookeeper_close() is in progress **/ void testHangingClient() { From 19e4b850e48cbe8f100ab23cada9f2ea0a05d2be Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 9 Jan 2014 23:01:19 +0000 Subject: [PATCH 228/444] =?UTF-8?q?ZOOKEEPER-1857.=20PrepRequestProcessotT?= =?UTF-8?q?est=20doesn't=20shutdown=20ZooKeeper=20server=20(Germ=C3=A1n=20?= =?UTF-8?q?Blanco=20via=20michim)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1556977 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/server/PrepRequestProcessorTest.java | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0dcaad52579..4e4e1bed947 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -218,6 +218,9 @@ BUGFIXES: ZOOKEEPER-1057. zookeeper c-client, connection to offline server fails to successfully fallback to second zk host (Germán Blanco via michim) + ZOOKEEPER-1857. PrepRequestProcessotTest doesn't shutdown ZooKeeper server + (Germán Blanco via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java index bdbe45ad13a..d5769cb8903 100644 --- a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java +++ b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.PrintWriter; import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.KeeperException.Code; @@ -43,6 +44,7 @@ public class PrepRequestProcessorTest extends ClientBase { private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final int CONNECTION_TIMEOUT = 3000; + private final CountDownLatch testEnd = new CountDownLatch(1); @Test public void testPRequest() throws Exception { @@ -59,14 +61,17 @@ public void testPRequest() throws Exception { PrepRequestProcessor processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); Request foo = new Request(null, 1l, 1, OpCode.create, ByteBuffer.allocate(3), null); processor.pRequest(foo); + testEnd.await(5, java.util.concurrent.TimeUnit.SECONDS); + f.shutdown(); + zks.shutdown(); } private class MyRequestProcessor implements RequestProcessor { @Override public void processRequest(Request request) { - Assert.assertEquals("Request should have marshalling error", new ErrorTxn(Code.MARSHALLINGERROR.intValue()), request.txn); - + Assert.assertEquals("Request should have marshalling error", new ErrorTxn(Code.MARSHALLINGERROR.intValue()), request.txn); + testEnd.countDown(); } @Override public void shutdown() { From 9436a70a24d62d38afea2692d2413bd935981b83 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sun, 12 Jan 2014 21:36:17 +0000 Subject: [PATCH 229/444] ZOOKEEPER-1238. when the linger time was changed for NIO the patch missed Netty (Skye Wanderman-Milne via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1557614 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/server/NettyServerCnxnFactory.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4e4e1bed947..647e73a91f1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -221,6 +221,9 @@ BUGFIXES: ZOOKEEPER-1857. PrepRequestProcessotTest doesn't shutdown ZooKeeper server (Germán Blanco via michim) + ZOOKEEPER-1238. when the linger time was changed for NIO the patch missed + Netty (Skye Wanderman-Milne via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index f21c7ecc4ac..7a84ce91111 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -249,7 +249,8 @@ public void writeComplete(ChannelHandlerContext ctx, bootstrap.setOption("reuseAddress", true); // child channels bootstrap.setOption("child.tcpNoDelay", true); - bootstrap.setOption("child.soLinger", 2); + /* set socket linger to off, so that socket close does not block */ + bootstrap.setOption("child.soLinger", -1); bootstrap.getPipeline().addLast("servercnxnfactory", channelHandler); } From 2a7782057185f43b4b8dd66167e90a4bafeb0a3a Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 16 Jan 2014 22:59:54 +0000 Subject: [PATCH 230/444] =?UTF-8?q?ZOOKEEPER-1837.=20Fix=20JMXEnv=20checks?= =?UTF-8?q?=20(potential=20race=20conditions)=20=20=20(Germ=C3=A1n=20Blanc?= =?UTF-8?q?o=20via=20fpj)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1558951 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../org/apache/zookeeper/test/JMXEnv.java | 96 ++++++++++++------- .../org/apache/zookeeper/test/QuorumUtil.java | 2 + 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 647e73a91f1..67f799a7808 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -224,6 +224,9 @@ BUGFIXES: ZOOKEEPER-1238. when the linger time was changed for NIO the patch missed Netty (Skye Wanderman-Milne via fpj) + ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions) + (Germán Blanco via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/JMXEnv.java b/src/java/test/org/apache/zookeeper/test/JMXEnv.java index b3284283bcd..2f955ac26a2 100644 --- a/src/java/test/org/apache/zookeeper/test/JMXEnv.java +++ b/src/java/test/org/apache/zookeeper/test/JMXEnv.java @@ -84,34 +84,43 @@ public static MBeanServerConnection conn() throws IOException { * Note that these are components of the name, and in particular * order matters - you want the more specific name (leafs) specified * before their parent(s) (since names are hierarchical) + * It waits in a loop up to 60 seconds before failing if there is a + * mismatch. * @param expectedNames * @return * @throws IOException * @throws MalformedObjectNameException */ public static Set ensureAll(String... expectedNames) - throws IOException + throws IOException, InterruptedException { Set beans; - try { - beans = conn().queryNames( - new ObjectName(CommonNames.DOMAIN + ":*"), null); - } catch (MalformedObjectNameException e) { - throw new RuntimeException(e); - } + Set found; + int nTry = 0; + do { + if (nTry++ > 0) { + Thread.sleep(100); + } + try { + beans = conn().queryNames( + new ObjectName(CommonNames.DOMAIN + ":*"), null); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } - Set found = new HashSet(); - for (String name : expectedNames) { - LOG.info("expect:" + name); - for (ObjectName bean : beans) { - if (bean.toString().contains(name)) { - LOG.info("found:" + name + " " + bean); - found.add(bean); - break; + found = new HashSet(); + for (String name : expectedNames) { + LOG.info("expect:" + name); + for (ObjectName bean : beans) { + if (bean.toString().contains(name)) { + LOG.info("found:" + name + " " + bean); + found.add(bean); + break; + } } + beans.removeAll(found); } - beans.removeAll(found); - } + } while ((expectedNames.length != found.size()) && (nTry < 600)); TestCase.assertEquals("expected " + Arrays.toString(expectedNames), expectedNames.length, found.size()); return beans; @@ -122,8 +131,6 @@ public static Set ensureAll(String... expectedNames) * Note that these are components of the name, and in particular * order matters - you want the more specific name (leafs) specified * before their parent(s) (since names are hierarchical) - * It waits in a loop up to 5 seconds before failing if there is a - * mismatch. * @param expectedNames * @return * @throws IOException @@ -134,10 +141,6 @@ public static Set ensureOnly(String... expectedNames) { LOG.info("ensureOnly:" + Arrays.toString(expectedNames)); Set beans = ensureAll(expectedNames); - for (int i = 0; (i < 50) && (beans.size() != 0); i++) { - Thread.sleep(100); - beans = ensureAll(expectedNames); - } for (ObjectName bean : beans) { LOG.info("unexpected:" + bean.toString()); } @@ -146,23 +149,44 @@ public static Set ensureOnly(String... expectedNames) } public static void ensureNone(String... expectedNames) - throws IOException + throws IOException, InterruptedException { Set beans; - try { - beans = conn().queryNames( - new ObjectName(CommonNames.DOMAIN + ":*"), null); - } catch (MalformedObjectNameException e) { - throw new RuntimeException(e); - } - - for (String name : expectedNames) { - for (ObjectName bean : beans) { - if (bean.toString().contains(name)) { - LOG.info("didntexpect:" + name); - TestCase.fail(name + " " + bean.toString()); + int nTry = 0; + boolean foundUnexpected = false; + String unexpectedName = ""; + do { + if (nTry++ > 0) { + Thread.sleep(100); + } + try { + beans = conn().queryNames( + new ObjectName(CommonNames.DOMAIN + ":*"), null); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + + foundUnexpected = false; + for (String name : expectedNames) { + for (ObjectName bean : beans) { + if (bean.toString().contains(name)) { + LOG.info("didntexpect:" + name); + foundUnexpected = true; + unexpectedName = name + " " + bean.toString(); + break; + } } + if (foundUnexpected) { + break; + } + } + } while ((foundUnexpected) && (nTry < 600)); + if (foundUnexpected) { + LOG.info("List of all beans follows:"); + for (ObjectName bean : beans) { + LOG.info("bean:" + bean.toString()); } + TestCase.fail(unexpectedName); } } diff --git a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java index 54df0914d27..8b0c2a6df21 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java @@ -161,6 +161,8 @@ public void startAll() throws IOException { JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()])); } catch (IOException e) { LOG.warn("IOException during JMXEnv operation", e); + } catch (InterruptedException e) { + LOG.warn("InterruptedException during JMXEnv operation", e); } } From 57295cc197052c13387589bc7e1cbf5007f6612d Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Fri, 24 Jan 2014 17:06:22 +0000 Subject: [PATCH 231/444] ZOOKEEPER-1858. JMX checks - potential race conditions while stopping and starting server (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1561069 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../org/apache/zookeeper/test/ClientBase.java | 49 +++++++++++++- .../test/FourLetterWordsQuorumTest.java | 2 +- .../zookeeper/test/FourLetterWordsTest.java | 2 +- .../org/apache/zookeeper/test/JMXEnv.java | 66 +++++++++++++++++++ 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 67f799a7808..30d18d7fed5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -227,6 +227,9 @@ BUGFIXES: ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions) (Germán Blanco via fpj) + ZOOKEEPER-1858. JMX checks - potential race conditions while stopping + and starting server (Rakesh R via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index c17b8129cf1..3dedec2d5f1 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -27,15 +27,20 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.management.MBeanServerConnection; +import javax.management.ObjectName; + +import junit.framework.TestCase; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; @@ -188,7 +193,7 @@ protected TestableZooKeeper createClient(CountdownWatcher watcher, } if (allClients != null) { allClients.add(zk); - JMXEnv.ensureAll("0x" + Long.toHexString(zk.getSessionId())); + JMXEnv.ensureAll(getHexSessionId(zk.getSessionId())); } else { // test done - close the zk, not needed zk.close(); @@ -439,8 +444,46 @@ protected void startServer() throws Exception { serverFactory = createNewServerInstance(serverFactory, hostPort, maxCnxns); startServerInstance(tmpDir, serverFactory, hostPort); - // ensure that only server and data bean are registered - JMXEnv.ensureOnly("InMemoryDataTree", "StandaloneServer_port"); + // ensure that server and data bean are registered + Set children = JMXEnv.ensureParent("InMemoryDataTree", + "StandaloneServer_port"); + // Remove beans which are related to zk client sessions. Strong + // assertions cannot be done for these client sessions because + // registeration of these beans with server will happen only on their + // respective reconnection interval + verifyUnexpectedBeans(children); + } + + private void verifyUnexpectedBeans(Set children) { + if (allClients != null) { + for (ZooKeeper zkc : allClients) { + Iterator childItr = children.iterator(); + while (childItr.hasNext()) { + ObjectName clientBean = childItr.next(); + if (clientBean.toString().contains( + getHexSessionId(zkc.getSessionId()))) { + LOG.info("found name:" + zkc.getSessionId() + + " client bean:" + clientBean.toString()); + childItr.remove(); + } + } + } + } + for (ObjectName bean : children) { + LOG.info("unexpected:" + bean.toString()); + } + TestCase.assertEquals("Unexpected bean exists!", 0, children.size()); + } + + /** + * Returns a string representation of the given long value session id + * + * @param sessionId + * long value of session id + * @return string representation of session id + */ + protected static String getHexSessionId(long sessionId) { + return "0x" + Long.toHexString(sessionId); } protected void stopServer() throws Exception { diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java index d9a6713bede..49d90f76e07 100644 --- a/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java @@ -55,7 +55,7 @@ public void testFourLetterWords() throws Exception { verify(hp, "cons", "queued"); TestableZooKeeper zk = createClient(hp); - String sid = "0x" + Long.toHexString(zk.getSessionId()); + String sid = getHexSessionId(zk.getSessionId()); verify(hp, "stat", "queued"); verify(hp, "srvr", "Outstanding"); diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java index 1ff198a0dc8..281b1786954 100644 --- a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java @@ -57,7 +57,7 @@ public void testFourLetterWords() throws Exception { verify("cons", "queued"); TestableZooKeeper zk = createClient(); - String sid = "0x" + Long.toHexString(zk.getSessionId()); + String sid = getHexSessionId(zk.getSessionId()); verify("stat", "queued"); verify("srvr", "Outstanding"); diff --git a/src/java/test/org/apache/zookeeper/test/JMXEnv.java b/src/java/test/org/apache/zookeeper/test/JMXEnv.java index 2f955ac26a2..d1c9e236b75 100644 --- a/src/java/test/org/apache/zookeeper/test/JMXEnv.java +++ b/src/java/test/org/apache/zookeeper/test/JMXEnv.java @@ -204,4 +204,70 @@ public static void dump() throws IOException { } } + /** + * Ensure that the specified parent names are registered. Note that these + * are components of the name. It waits in a loop up to 60 seconds before + * failing if there is a mismatch. This will return the beans which are not + * matched. + * + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1858} + * + * @param expectedNames + * - expected beans + * @return the beans which are not matched with the given expected names + * + * @throws IOException + * @throws InterruptedException + * + */ + public static Set ensureParent(String... expectedNames) + throws IOException, InterruptedException { + LOG.info("ensureParent:" + Arrays.toString(expectedNames)); + + Set beans; + int nTry = 0; + Set found = new HashSet(); + do { + if (nTry++ > 0) { + Thread.sleep(500); + } + try { + beans = conn().queryNames( + new ObjectName(CommonNames.DOMAIN + ":*"), null); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + found.clear(); + for (String name : expectedNames) { + LOG.info("expect:" + name); + for (ObjectName bean : beans) { + // check the existence of name in bean + if (compare(bean.toString(), name)) { + LOG.info("found:" + name + " " + bean); + found.add(bean); + break; + } + } + beans.removeAll(found); + } + } while (expectedNames.length != found.size() && nTry < 120); + TestCase.assertEquals("expected " + Arrays.toString(expectedNames), + expectedNames.length, found.size()); + return beans; + } + + /** + * Comparing that the given name exists in the bean. For component beans, + * the component name will be present at the end of the bean name + * + * For example 'StandaloneServer' will present in the bean name like + * 'org.apache.ZooKeeperService:name0=StandaloneServer_port-1' + */ + private static boolean compare(String bean, String name) { + String[] names = bean.split("="); + if (names.length > 0 && names[names.length - 1].contains(name)) { + return true; + } + return false; + } } From d25a054da357cf1fcad07f80131d69434416a73b Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Mon, 27 Jan 2014 13:38:03 +0000 Subject: [PATCH 232/444] ZOOKEEPER-1867. Bug in ZkDatabaseCorruptionTest (fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1561673 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../zookeeper/test/ZkDatabaseCorruptionTest.java | 16 ++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 30d18d7fed5..b307d422092 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -230,6 +230,8 @@ BUGFIXES: ZOOKEEPER-1858. JMX checks - potential race conditions while stopping and starting server (Rakesh R via fpj) + ZOOKEEPER-1867. Bug in ZkDatabaseCorruptionTest (fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java b/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java index a45d8b47bf4..5fdbd4dd8f3 100644 --- a/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java +++ b/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java @@ -121,12 +121,16 @@ public void process(WatchedEvent event) { } //wait for servers to be up String[] list = qb.hostPort.split(","); - for (int i =0; i < 4; i++) { - String hp = list[i]; - Assert.assertTrue("waiting for server up", - ClientBase.waitForServerUp(hp, - CONNECTION_TIMEOUT)); - LOG.info(hp + " is accepting client connections"); + for (int i = 0; i < 5; i++) { + if(leaderSid != (i + 1)) { + String hp = list[i]; + Assert.assertTrue("waiting for server up", + ClientBase.waitForServerUp(hp, + CONNECTION_TIMEOUT)); + LOG.info("{} is accepting client connections", hp); + } else { + LOG.info("Skipping the leader"); + } } zk = qb.createClient(); From ea52795eb7e27024bd00ad4ebef1e99c0012574b Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sun, 9 Feb 2014 22:24:55 +0000 Subject: [PATCH 233/444] ZOOKEEPER-1872. QuorumPeer is not shutdown in few cases (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1566453 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../server/quorum/CnxManagerTest.java | 66 ++++++++++--------- .../server/quorum/QuorumPeerMainTest.java | 61 ++++++++++------- .../org/apache/zookeeper/test/JMXEnv.java | 8 ++- 4 files changed, 80 insertions(+), 58 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b307d422092..b6dd1c476c1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -231,6 +231,9 @@ BUGFIXES: and starting server (Rakesh R via fpj) ZOOKEEPER-1867. Bug in ZkDatabaseCorruptionTest (fpj) + + ZOOKEEPER-1872. QuorumPeer is not shutdown in few cases + (Rakesh R via fpj) IMPROVEMENTS: diff --git a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java index e8fdf6ba527..13874df8e86 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java @@ -387,41 +387,43 @@ public void testSocketTimeout() throws Exception { @Test public void testWorkerThreads() throws Exception { ArrayList peerList = new ArrayList(); - - for (int sid = 0; sid < 3; sid++) { - QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[sid], peerTmpdir[sid], - peerClientPort[sid], 3, sid, 1000, 2, 2); - LOG.info("Starting peer " + peer.getId()); - peer.start(); - peerList.add(sid, peer); - } - String failure = verifyThreadCount(peerList, 4); - if (failure != null) { - Assert.fail(failure); - } - for (int myid = 0; myid < 3; myid++) { - for (int i = 0; i < 5; i++) { - // halt one of the listeners and verify count - QuorumPeer peer = peerList.get(myid); - LOG.info("Round " + i + ", halting peer " + peer.getId()); - peer.shutdown(); - peerList.remove(myid); - failure = verifyThreadCount(peerList, 2); - if (failure != null) { - Assert.fail(failure); - } - - // Restart halted node and verify count - peer = new QuorumPeer(peers, peerTmpdir[myid], peerTmpdir[myid], - peerClientPort[myid], 3, myid, 1000, 2, 2); - LOG.info("Round " + i + ", restarting peer " + peer.getId()); + try { + for (int sid = 0; sid < 3; sid++) { + QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[sid], peerTmpdir[sid], + peerClientPort[sid], 3, sid, 1000, 2, 2); + LOG.info("Starting peer {}", peer.getId()); peer.start(); - peerList.add(myid, peer); - failure = verifyThreadCount(peerList, 4); - if (failure != null) { - Assert.fail(failure); + peerList.add(sid, peer); + } + String failure = verifyThreadCount(peerList, 4); + if (failure != null) { + Assert.fail(failure); + } + for (int myid = 0; myid < 3; myid++) { + for (int i = 0; i < 5; i++) { + // halt one of the listeners and verify count + QuorumPeer peer = peerList.get(myid); + LOG.info("Round {}, halting peer {}", new Object[] { i, + peer.getId() }); + peer.shutdown(); + peerList.remove(myid); + failure = verifyThreadCount(peerList, 2); + Assert.assertNull(failure, failure); + // Restart halted node and verify count + peer = new QuorumPeer(peers, peerTmpdir[myid], peerTmpdir[myid], + peerClientPort[myid], 3, myid, 1000, 2, 2); + LOG.info("Round {}, restarting peer {}" + + new Object[] { i, peer.getId() }); + peer.start(); + peerList.add(myid, peer); + failure = verifyThreadCount(peerList, 4); + Assert.assertNull(failure, failure); } } + } finally { + for (QuorumPeer quorumPeer : peerList) { + quorumPeer.shutdown(); + } } } 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 6753adc180f..9ae4da841cc 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -33,7 +33,6 @@ import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; @@ -44,17 +43,15 @@ import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.apache.zookeeper.server.quorum.Leader.Proposal; -import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.test.ClientBase; +import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -67,6 +64,22 @@ public class QuorumPeerMainTest extends QuorumPeerTestBase { protected static final Logger LOG = Logger.getLogger(QuorumPeerMainTest.class); + private Servers servers; + private int numServers = 0; + + @After + public void tearDown() throws Exception { + if (servers == null || servers.mt == null) { + LOG.info("No servers to shutdown!"); + return; + } + for (int i = 0; i < numServers; i++) { + if (i < servers.mt.length) { + servers.mt[i].shutdown(); + } + } + } + /** * Verify the ability to start a cluster. */ @@ -229,14 +242,14 @@ public void testEarlyLeaderAbandonment() throws Exception { */ @Test public void testHighestZxidJoinLate() throws Exception { - int numServers = 3; - Servers svrs = LaunchServers(numServers); + numServers = 3; + servers = LaunchServers(numServers); String path = "/hzxidtest"; int leader=-1; // find the leader for (int i=0; i < numServers; i++) { - if (svrs.mt[i].main.quorumPeer.leader != null) { + if (servers.mt[i].main.quorumPeer.leader != null) { leader = i; } } @@ -251,47 +264,47 @@ public void testHighestZxidJoinLate() throws Exception { byte[] output; // Create a couple of nodes - svrs.zk[leader].create(path+leader, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - svrs.zk[leader].create(path+nonleader, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + servers.zk[leader].create(path+leader, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + servers.zk[leader].create(path+nonleader, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // make sure the updates indeed committed. If it is not // the following statement will throw. - output = svrs.zk[leader].getData(path+nonleader, false, null); + output = servers.zk[leader].getData(path+nonleader, false, null); // Shutdown every one else but the leader for (int i=0; i < numServers; i++) { if (i != leader) { - svrs.mt[i].shutdown(); + servers.mt[i].shutdown(); } } input[0] = 2; // Update the node on the leader - svrs.zk[leader].setData(path+leader, input, -1, null, null); + servers.zk[leader].setData(path+leader, input, -1, null, null); // wait some time to let this get written to disk Thread.sleep(500); // shut the leader down - svrs.mt[leader].shutdown(); + servers.mt[leader].shutdown(); System.gc(); - waitForAll(svrs.zk, States.CONNECTING); + waitForAll(servers.zk, States.CONNECTING); // Start everyone but the leader for (int i=0; i < numServers; i++) { if (i != leader) { - svrs.mt[i].start(); + servers.mt[i].start(); } } // wait to connect to one of these - waitForOne(svrs.zk[nonleader], States.CONNECTED); + waitForOne(servers.zk[nonleader], States.CONNECTED); // validate that the old value is there and not the new one - output = svrs.zk[nonleader].getData(path+leader, false, null); + output = servers.zk[nonleader].getData(path+leader, false, null); Assert.assertEquals( "Expecting old value 1 since 2 isn't committed yet", @@ -299,22 +312,22 @@ public void testHighestZxidJoinLate() throws Exception { // Do some other update, so we bump the maxCommttedZxid // by setting the value to 2 - svrs.zk[nonleader].setData(path+nonleader, input, -1); + servers.zk[nonleader].setData(path+nonleader, input, -1); // start the old leader - svrs.mt[leader].start(); + servers.mt[leader].start(); // connect to it - waitForOne(svrs.zk[leader], States.CONNECTED); + waitForOne(servers.zk[leader], States.CONNECTED); // make sure it doesn't have the new value that it alone had logged - output = svrs.zk[leader].getData(path+leader, false, null); + output = servers.zk[leader].getData(path+leader, false, null); Assert.assertEquals( "Validating that the deposed leader has rolled back that change it had written", output[0], 1); // make sure the leader has the subsequent changes that were made while it was offline - output = svrs.zk[leader].getData(path+nonleader, false, null); + output = servers.zk[leader].getData(path+nonleader, false, null); Assert.assertEquals( "Validating that the deposed leader caught up on changes it missed", output[0], 2); @@ -716,8 +729,8 @@ static void writeLongToFile(File file, long value) throws IOException { @Test public void testUpdatingEpoch() throws Exception { // Create a cluster and restart them multiple times to bump the epoch. - int numServers = 3; - Servers servers = LaunchServers(numServers); + numServers = 3; + servers = LaunchServers(numServers); File currentEpochFile; for (int i = 0; i < 10; i++) { for (int j = 0; j < numServers; j++) { diff --git a/src/java/test/org/apache/zookeeper/test/JMXEnv.java b/src/java/test/org/apache/zookeeper/test/JMXEnv.java index d1c9e236b75..f9cdef74992 100644 --- a/src/java/test/org/apache/zookeeper/test/JMXEnv.java +++ b/src/java/test/org/apache/zookeeper/test/JMXEnv.java @@ -60,14 +60,18 @@ public static void setUp() throws IOException { public static void tearDown() { try { - cc.close(); + if (cc != null) { + cc.close(); + } } catch (IOException e) { LOG.warn("Unexpected, ignoring", e); } cc = null; try { - cs.stop(); + if (cs != null) { + cs.stop(); + } } catch (IOException e) { LOG.warn("Unexpected, ignoring", e); From efec318a6a95417f934b34f468aaabece5784a0a Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 10 Feb 2014 20:52:25 +0000 Subject: [PATCH 234/444] ZOOKEEPER-1573. Unable to load database due to missing parent node (Vinayakumar B via phunt, fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1566740 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../server/persistence/FileTxnSnapLog.java | 22 ++-- .../zookeeper/test/LoadFromLogTest.java | 116 +++++++++++------- 3 files changed, 80 insertions(+), 61 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b6dd1c476c1..bd600a7eb5e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -235,6 +235,9 @@ BUGFIXES: ZOOKEEPER-1872. QuorumPeer is not shutdown in few cases (Rakesh R via fpj) + ZOOKEEPER-1573. Unable to load database due to missing parent node + (Vinayakumar B via phunt, fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index 50486c99178..98ac3c80e33 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -199,23 +199,17 @@ public void processTransaction(TxnHeader hdr,DataTree dt, } /** - * This should never happen. A NONODE can never show up in the - * transaction logs. This is more indicative of a corrupt transaction - * log. Refer ZOOKEEPER-1333 for more info. + * Snapshots are lazily created. So when a snapshot is in progress, + * there is a chance for later transactions to make into the + * snapshot. Then when the snapshot is restored, NONODE/NODEEXISTS + * errors could occur. It should be safe to ignore these. */ - if (rc.err != Code.OK.intValue()) { - if (hdr.getType() == OpCode.create && rc.err == Code.NONODE.intValue()) { - int lastSlash = rc.path.lastIndexOf('/'); - String parentName = rc.path.substring(0, lastSlash); - LOG.error("Parent {} missing for {}", parentName, rc.path); - throw new KeeperException.NoNodeException(parentName); - } else { - LOG.debug("Ignoring processTxn failure hdr: " + hdr.getType() + - " : error: " + rc.err); - } + if (rc.err != Code.OK.intValue()) { + LOG.debug("Ignoring processTxn failure hdr:" + hdr.getType() + + ", error: " + rc.err + ", path: " + rc.path); } } - + /** * the last logged zxid on the transaction logs * @return the last logged zxid diff --git a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java index c01a4326243..419683f0faf 100644 --- a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java +++ b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java @@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -37,6 +38,7 @@ import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerCnxnFactory; @@ -278,22 +280,7 @@ public void testRestore() throws Exception { f.startup(zks); Assert.assertTrue("waiting for server being up ", ClientBase .waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); - ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - - long start = System.currentTimeMillis(); - while (!connected) { - long end = System.currentTimeMillis(); - if (end - start > 5000) { - Assert.assertTrue("Could not connect with server in 5 seconds", - false); - } - try { - Thread.sleep(200); - } catch (Exception e) { - LOG.warn("Intrrupted"); - } - - } + ZooKeeper zk = getConnectedZkClient(); // generate some transactions String lastPath = null; try { @@ -333,21 +320,7 @@ public void testRestore() throws Exception { // Verify lastProcessedZxid is set correctly Assert.assertTrue("Restore failed expected zxid=" + eZxid + " found=" + fZxid, fZxid == eZxid); - zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - start = System.currentTimeMillis(); - while (!connected) { - long end = System.currentTimeMillis(); - if (end - start > 5000) { - Assert.assertTrue("Could not connect with server in 5 seconds", - false); - } - try { - Thread.sleep(200); - } catch (Exception e) { - LOG.warn("Intrrupted"); - } - - } + zk = getConnectedZkClient(); // Verify correctness of data and whether sequential znode creation // proceeds correctly after this point String[] children; @@ -386,22 +359,7 @@ public void testRestoreWithTransactionErrors() throws Exception { f.startup(zks); Assert.assertTrue("waiting for server being up ", ClientBase .waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); - ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - - long start = System.currentTimeMillis(); - while (!connected) { - long end = System.currentTimeMillis(); - if (end - start > 5000) { - Assert.assertTrue("Could not connect with server in 5 seconds", - false); - } - try { - Thread.sleep(200); - } catch (Exception e) { - LOG.warn("Intrrupted"); - } - - } + ZooKeeper zk = getConnectedZkClient(); // generate some transactions try { for (int i = 0; i < NUM_MESSAGES; i++) { @@ -437,4 +395,68 @@ public void testRestoreWithTransactionErrors() throws Exception { f.shutdown(); zks.shutdown(); } + + /** + * ZOOKEEPER-1573: test restoring a snapshot with deleted txns ahead of the + * snapshot file's zxid. + */ + @Test + public void testReloadSnapshotWithMissingParent() throws Exception { + // setup a single server cluster + File tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(10000); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); + ZooKeeper zk = getConnectedZkClient(); + + // create transactions to create the snapshot with create/delete pattern + zk.create("/a", "".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + Stat stat = zk.exists("/a", false); + long createZxId = stat.getMzxid(); + zk.create("/a/b", "".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.delete("/a/b", -1); + zk.delete("/a", -1); + // force the zxid to be behind the content + zks.getZKDatabase().setlastProcessedZxid(createZxId); + LOG.info("Set lastProcessedZxid to {}", zks.getZKDatabase() + .getDataTreeLastProcessedZxid()); + // Force snapshot and restore + zks.takeSnapshot(); + zks.shutdown(); + f.shutdown(); + + zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(10000); + f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); + f.shutdown(); + } + + private ZooKeeper getConnectedZkClient() throws IOException { + ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); + + long start = System.currentTimeMillis(); + while (!connected) { + long end = System.currentTimeMillis(); + if (end - start > 5000) { + Assert.assertTrue("Could not connect with server in 5 seconds", + false); + } + try { + Thread.sleep(200); + } catch (Exception e) { + LOG.warn("Interrupted"); + } + } + return zk; + } } \ No newline at end of file From 3cf7cfc21c69f38f97cc059ed37abe8f356bcef7 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 10 Feb 2014 21:26:54 +0000 Subject: [PATCH 235/444] ZOOKEEPER-1811. The ZooKeeperSaslClient service name principal is hardcoded to "zookeeper" (Harsh J via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1566749 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/ClientCnxn.java | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index bd600a7eb5e..7d581cec3a8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -238,6 +238,9 @@ BUGFIXES: ZOOKEEPER-1573. Unable to load database due to missing parent node (Vinayakumar B via phunt, fpj) + ZOOKEEPER-1811. The ZooKeeperSaslClient service name principal is + hardcoded to "zookeeper" (Harsh J via phunt) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index eafc0c81dc6..6b230d5849b 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -85,6 +85,9 @@ public class ClientCnxn { private static final Logger LOG = LoggerFactory.getLogger(ClientCnxn.class); + private static final String ZK_SASL_CLIENT_USERNAME = + "zookeeper.sasl.client.username"; + /** This controls whether automatic watch resetting is enabled. * Clients automatically reset watches during session reconnect, this * option allows the client to turn off this behavior by setting @@ -941,7 +944,11 @@ private void startConnect() throws IOException { "(" + addr.getHostName() + ":" + addr.getPort() + ")")); if (ZooKeeperSaslClient.isEnabled()) { try { - zooKeeperSaslClient = new ZooKeeperSaslClient("zookeeper/"+addr.getHostName()); + String principalUserName = System.getProperty( + ZK_SASL_CLIENT_USERNAME, "zookeeper"); + zooKeeperSaslClient = + new ZooKeeperSaslClient( + principalUserName+"/"+addr.getHostName()); } catch (LoginException e) { // An authentication error occurred when the SASL client tried to initialize: // for Kerberos this means that the client failed to authenticate with the KDC. From c6a3d5bc921b1a68be76c6fcf2750fae176e8358 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Wed, 12 Feb 2014 01:43:10 +0000 Subject: [PATCH 236/444] ZOOKEEPER-1873. Unnecessarily InstanceNotFoundException is coming when unregister failed jmxbeans (Rakesh R via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1567490 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7d581cec3a8..c761a217a8e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -241,6 +241,9 @@ BUGFIXES: ZOOKEEPER-1811. The ZooKeeperSaslClient service name principal is hardcoded to "zookeeper" (Harsh J via phunt) + ZOOKEEPER-1873. Unnecessarily InstanceNotFoundException is coming when + unregister failed jmxbeans (Rakesh R via michim) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java b/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java index 0e12d130718..895b81864c9 100644 --- a/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java +++ b/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java @@ -89,13 +89,13 @@ public void register(ZKMBeanInfo bean, ZKMBeanInfo parent) assert path != null; } path = makeFullPath(path, parent); - mapBean2Path.put(bean, path); - mapName2Bean.put(bean.getName(), bean); if(bean.isHidden()) return; ObjectName oname = makeObjectName(path, bean); try { mBeanServer.registerMBean(bean, oname); + mapBean2Path.put(bean, path); + mapName2Bean.put(bean.getName(), bean); } catch (JMException e) { LOG.warn("Failed to register MBean " + bean.getName()); throw e; From a5065590468b0c2a8dbed0bba8391bd5153c0217 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 12 Feb 2014 16:00:34 +0000 Subject: [PATCH 237/444] ZOOKEEPER-1844. TruncateTest fails on windows (Rakesh R via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1567661 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/persistence/FileTxnLog.java | 44 +++++++++++----- .../server/persistence/FileTxnSnapLog.java | 50 +++++++++++-------- .../apache/zookeeper/test/TruncateTest.java | 2 + 4 files changed, 64 insertions(+), 34 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c761a217a8e..cf16ec80738 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -244,6 +244,8 @@ BUGFIXES: ZOOKEEPER-1873. Unnecessarily InstanceNotFoundException is coming when unregister failed jmxbeans (Rakesh R via michim) + ZOOKEEPER-1844. TruncateTest fails on windows (Rakesh R via fpj) + IMPROVEMENTS: ZOOKEEPER-1564. Allow JUnit test build with IBM Java diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index 55cdc8db8f5..b0dd79c1c37 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -285,9 +285,10 @@ public long getLastLoggedZxid() { // if a log file is more recent we must scan it to find // the highest zxid long zxid = maxLog; + TxnIterator itr = null; try { FileTxnLog txn = new FileTxnLog(logDir); - TxnIterator itr = txn.read(maxLog); + itr = txn.read(maxLog); while (true) { if(!itr.next()) break; @@ -296,10 +297,22 @@ public long getLastLoggedZxid() { } } catch (IOException e) { LOG.warn("Unexpected exception", e); + } finally { + close(itr); } return zxid; } + private void close(TxnIterator itr) { + if (itr != null) { + try { + itr.close(); + } catch (IOException ioe) { + LOG.warn("Error closing file iterator", ioe); + } + } + } + /** * commit the logs. make sure that evertyhing hits the * disk @@ -347,17 +360,22 @@ public TxnIterator read(long zxid) throws IOException { * @return true if successful false if not */ public boolean truncate(long zxid) throws IOException { - FileTxnIterator itr = new FileTxnIterator(this.logDir, zxid); - PositionInputStream input = itr.inputStream; - long pos = input.getPosition(); - // now, truncate at the current position - RandomAccessFile raf=new RandomAccessFile(itr.logFile,"rw"); - raf.setLength(pos); - raf.close(); - while(itr.goToNextLog()) { - if (!itr.logFile.delete()) { - LOG.warn("Unable to truncate " + itr.logFile); + FileTxnIterator itr = null; + try { + itr = new FileTxnIterator(this.logDir, zxid); + PositionInputStream input = itr.inputStream; + long pos = input.getPosition(); + // now, truncate at the current position + RandomAccessFile raf = new RandomAccessFile(itr.logFile, "rw"); + raf.setLength(pos); + raf.close(); + while (itr.goToNextLog()) { + if (!itr.logFile.delete()) { + LOG.warn("Unable to truncate {}", itr.logFile); + } } + } finally { + close(itr); } return true; } @@ -657,7 +675,9 @@ public Record getTxn() { * and release the resources. */ public void close() throws IOException { - inputStream.close(); + if (inputStream != null) { + inputStream.close(); + } } } diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index 98ac3c80e33..6f0df515f53 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -132,30 +132,36 @@ public long restore(DataTree dt, Map sessions, TxnIterator itr = txnLog.read(dt.lastProcessedZxid+1); long highestZxid = dt.lastProcessedZxid; TxnHeader hdr; - while (true) { - // iterator points to - // the first valid txn when initialized - hdr = itr.getHeader(); - if (hdr == null) { - //empty logs - return dt.lastProcessedZxid; + try { + while (true) { + // iterator points to + // the first valid txn when initialized + hdr = itr.getHeader(); + if (hdr == null) { + //empty logs + return dt.lastProcessedZxid; + } + if (hdr.getZxid() < highestZxid && highestZxid != 0) { + LOG.error("{}(higestZxid) > {}(next log) for type {}", + new Object[] { highestZxid, hdr.getZxid(), + hdr.getType() }); + } else { + highestZxid = hdr.getZxid(); + } + try { + processTransaction(hdr,dt,sessions, itr.getTxn()); + } catch(KeeperException.NoNodeException e) { + throw new IOException("Failed to process transaction type: " + + hdr.getType() + " error: " + e.getMessage(), e); + } + listener.onTxnLoaded(hdr, itr.getTxn()); + if (!itr.next()) + break; } - if (hdr.getZxid() < highestZxid && highestZxid != 0) { - LOG.error(highestZxid + "(higestZxid) > " - + hdr.getZxid() + "(next log) for type " - + hdr.getType()); - } else { - highestZxid = hdr.getZxid(); + } finally { + if (itr != null) { + itr.close(); } - try { - processTransaction(hdr,dt,sessions, itr.getTxn()); - } catch(KeeperException.NoNodeException e) { - throw new IOException("Failed to process transaction type: " + - hdr.getType() + " error: " + e.getMessage(), e); - } - listener.onTxnLoaded(hdr, itr.getTxn()); - if (!itr.next()) - break; } return highestZxid; } diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index 48078e7bff2..3d2854c17b0 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -111,6 +111,8 @@ public void testTruncationStreamReset() throws Exception { txn = iter.getTxn(); Assert.assertEquals(200, hdr.getZxid()); Assert.assertTrue(txn instanceof SetDataTxn); + iter.close(); + ClientBase.recursiveDelete(tmpdir); } private void append(ZKDatabase zkdb, int i) throws IOException { From 67155d1f38a20d07b2248880561fda714418e5fa Mon Sep 17 00:00:00 2001 From: Camille Fournier Date: Wed, 19 Feb 2014 01:29:43 +0000 Subject: [PATCH 238/444] ZOOKEEPER-1755. Concurrent operations of four letter 'dump' ephemeral command and killSession causing NPE (Rakesh R via camille) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1569588 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../org/apache/zookeeper/server/DataTree.java | 8 ++- .../{test => server}/DataTreeTest.java | 67 +++++++++++++++++-- 3 files changed, 70 insertions(+), 8 deletions(-) rename src/java/test/org/apache/zookeeper/{test => server}/DataTreeTest.java (65%) diff --git a/CHANGES.txt b/CHANGES.txt index cf16ec80738..b3824bbbfb2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -245,6 +245,9 @@ BUGFIXES: unregister failed jmxbeans (Rakesh R via michim) ZOOKEEPER-1844. TruncateTest fails on windows (Rakesh R via fpj) + + ZOOKEEPER-1755. Concurrent operations of four letter 'dump' ephemeral + command and killSession causing NPE (Rakesh R via camille) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index df3963a30a5..363b256b64b 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -1253,9 +1253,11 @@ public void dumpEphemerals(PrintWriter pwriter) { pwriter.print("0x" + Long.toHexString(k)); pwriter.println(":"); HashSet tmp = ephemerals.get(k); - synchronized (tmp) { - for (String path : tmp) { - pwriter.println("\t" + path); + if (tmp != null) { + synchronized (tmp) { + for (String path : tmp) { + pwriter.println("\t" + path); + } } } } diff --git a/src/java/test/org/apache/zookeeper/test/DataTreeTest.java b/src/java/test/org/apache/zookeeper/server/DataTreeTest.java similarity index 65% rename from src/java/test/org/apache/zookeeper/test/DataTreeTest.java rename to src/java/test/org/apache/zookeeper/server/DataTreeTest.java index ef01c10b5bb..ebc2fca0cd5 100644 --- a/src/java/test/org/apache/zookeeper/test/DataTreeTest.java +++ b/src/java/test/org/apache/zookeeper/server/DataTreeTest.java @@ -16,10 +16,12 @@ * limitations under the License. */ -package org.apache.zookeeper.test; +package org.apache.zookeeper.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.zookeeper.KeeperException.NoNodeException; +import org.apache.zookeeper.KeeperException.NodeExistsException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; @@ -30,14 +32,17 @@ import org.junit.Before; import org.junit.Test; import org.apache.zookeeper.server.DataNode; -import java.io.IOException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; + import org.apache.zookeeper.Quotas; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.common.PathTrie; import java.lang.reflect.*; +import java.util.concurrent.atomic.AtomicBoolean; public class DataTreeTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(DataTreeTest.class); @@ -54,7 +59,59 @@ public void tearDown() throws Exception { dt=null; } - @Test + /** + * For ZOOKEEPER-1755 - Test race condition when taking dumpEphemerals and + * removing the session related ephemerals from DataTree structure + */ + @Test(timeout = 60000) + public void testDumpEphemerals() throws Exception { + int count = 1000; + long session = 1000; + long zxid = 2000; + final DataTree dataTree = new DataTree(); + LOG.info("Create {} zkclient sessions and its ephemeral nodes", count); + createEphemeralNode(session, dataTree, count); + final AtomicBoolean exceptionDuringDumpEphemerals = new AtomicBoolean( + false); + final AtomicBoolean running = new AtomicBoolean(true); + Thread thread = new Thread() { + public void run() { + PrintWriter pwriter = new PrintWriter(new StringWriter()); + try { + while (running.get()) { + dataTree.dumpEphemerals(pwriter); + } + } catch (Exception e) { + LOG.error("Received exception while dumpEphemerals!", e); + exceptionDuringDumpEphemerals.set(true); + } + }; + }; + thread.start(); + LOG.debug("Killing {} zkclient sessions and its ephemeral nodes", count); + killZkClientSession(session, zxid, dataTree, count); + running.set(false); + thread.join(); + Assert.assertFalse("Should have got exception while dumpEphemerals!", + exceptionDuringDumpEphemerals.get()); + } + + private void killZkClientSession(long session, long zxid, + final DataTree dataTree, int count) { + for (int i = 0; i < count; i++) { + dataTree.killSession(session + i, zxid); + } + } + + private void createEphemeralNode(long session, final DataTree dataTree, + int count) throws NoNodeException, NodeExistsException { + for (int i = 0; i < count; i++) { + dataTree.createNode("/test" + i, new byte[0], null, session + i, + dataTree.getNode("/").stat.getCversion() + 1, 1, 1); + } + } + + @Test(timeout = 60000) public void testRootWatchTriggered() throws Exception { class MyWatcher implements Watcher{ boolean fired=false; @@ -74,7 +131,7 @@ public void process(WatchedEvent event) { /** * For ZOOKEEPER-1046 test if cversion is getting incremented correctly. */ - @Test + @Test(timeout = 60000) public void testIncrementCversion() throws Exception { dt.createNode("/test", new byte[0], null, 0, dt.getNode("/").stat.getCversion()+1, 1, 1); DataNode zk = dt.getNode("/test"); @@ -89,7 +146,7 @@ public void testIncrementCversion() throws Exception { (newCversion == prevCversion + 1 && newPzxid == prevPzxid + 1)); } - @Test + @Test(timeout = 60000) public void testPathTrieClearOnDeserialize() throws Exception { //Create a DataTree with quota nodes so PathTrie get updated From e84dd9004661dde04bc738e75bb02c67ae09347a Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 19 Feb 2014 23:07:59 +0000 Subject: [PATCH 239/444] Preparing for release 3.4.6 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1569958 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- build.xml | 2 +- docs/releasenotes.html | 780 +++++++++++++++++++++++++++++- src/c/configure.ac | 2 +- src/c/include/zookeeper_version.h | 2 +- 5 files changed, 779 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b3824bbbfb2..915fec2cbab 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Release 3.4.6 - TBD +Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/build.xml b/build.xml index a86456a2256..442d52e9961 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 97f028f8fc2..59cda186867 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -202,15 +202,18 @@ PDF -icon
    PDF
    -

    ZooKeeper 3.4.5 Release Notes

    +

    ZooKeeper 3.4.6 Release Notes

    + + +

    Changes Since 3.4.5

    +
    + + + +Changes Since ZooKeeper 3.4.5 + + +

    Sub-task +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.5
    IssueNotes
    ZOOKEEPER-1414 + +QuorumPeerMainTest.testQuorum, testBadPackets are failing intermittently +
    ZOOKEEPER-1459 + +Standalone ZooKeeperServer is not closing the transaction log files on shutdown +
    ZOOKEEPER-1558 + +Leader should not snapshot uncommitted state +
    ZOOKEEPER-1808 + Add version to FLE notifications for 3.4 branch +
    ZOOKEEPER-1817 + Fix don't care for b3.4 +
    ZOOKEEPER-1834 + Catch IOException in FileTxnLog +
    ZOOKEEPER-1837 + Fix JMXEnv checks (potential race conditions) +
    ZOOKEEPER-1838 + ZooKeeper shutdown hangs indefinitely at NioServerSocketChannelFactory.releaseExternalResources +
    ZOOKEEPER-1841 + problem in QuorumTest +
    ZOOKEEPER-1849 + Need to properly tear down tests in various cases +
    ZOOKEEPER-1852 + ServerCnxnFactory instance is not properly cleanedup +
    ZOOKEEPER-1854 + ClientBase ZooKeeper server clean-up +
    ZOOKEEPER-1857 + PrepRequestProcessotTest doesn't shutdown ZooKeeper server +
    ZOOKEEPER-1858 + JMX checks - potential race conditions while stopping and starting server +
    ZOOKEEPER-1867 + Bug in ZkDatabaseCorruptionTest +
    ZOOKEEPER-1873 + Unnecessarily InstanceNotFoundException is coming when unregister failed jmxbeans +
    +
    + + +
    + + + +Changes Since ZooKeeper 3.4.5 + +

    Bug +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.5
    ZOOKEEPER-87 + Follower does not shut itself down if its too far behind the leader. +
    ZOOKEEPER-732 + Improper translation of error into Python exception +
    ZOOKEEPER-753 + update log4j dependency from 1.2.15 to 1.2.16 in branch 3.4 +
    ZOOKEEPER-877 + zkpython does not work with python3.1 +
    ZOOKEEPER-1057 + zookeeper c-client, connection to offline server fails to successfully fallback to second zk host +
    ZOOKEEPER-1179 + NettyServerCnxn does not properly close socket on 4 letter word requests +
    ZOOKEEPER-1238 + when the linger time was changed for NIO the patch missed Netty +
    ZOOKEEPER-1334 + Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF is flawed +
    ZOOKEEPER-1379 + 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary +
    ZOOKEEPER-1382 + Zookeeper server holds onto dead/expired session ids in the watch data structures +
    ZOOKEEPER-1387 + Wrong epoch file created +
    ZOOKEEPER-1388 + Client side 'PathValidation' is missing for the multi-transaction api. +
    ZOOKEEPER-1448 + Node+Quota creation in transaction log can crash leader startup +
    ZOOKEEPER-1462 + Read-only server does not initialize database properly +
    ZOOKEEPER-1474 + Cannot build Zookeeper with IBM Java: use of Sun MXBean classes +
    ZOOKEEPER-1478 + Small bug in QuorumTest.testFollowersStartAfterLeader( ) +
    ZOOKEEPER-1495 + ZK client hangs when using a function not available on the server. +
    ZOOKEEPER-1513 + "Unreasonable length" exception while starting a server. +
    ZOOKEEPER-1535 + ZK Shell/Cli re-executes last command on exit +
    ZOOKEEPER-1548 + Cluster fails election loop in new and interesting way +
    ZOOKEEPER-1551 + Observers ignore txns that come after snapshot and UPTODATE +
    ZOOKEEPER-1553 + Findbugs configuration is missing some dependencies +
    ZOOKEEPER-1554 + Can't use zookeeper client without SASL +
    ZOOKEEPER-1557 + jenkins jdk7 test failure in testBadSaslAuthNotifiesWatch +
    ZOOKEEPER-1562 + Memory leaks in zoo_multi API +
    ZOOKEEPER-1573 + Unable to load database due to missing parent node +
    ZOOKEEPER-1578 + org.apache.zookeeper.server.quorum.Zab1_0Test failed due to hard code with 33556 port +
    ZOOKEEPER-1581 + change copyright in notice to 2012 +
    ZOOKEEPER-1596 + Zab1_0Test should ensure that the file is closed +
    ZOOKEEPER-1597 + Windows build failing +
    ZOOKEEPER-1599 + 3.3 server cannot join 3.4 quorum +
    ZOOKEEPER-1603 + StaticHostProviderTest testUpdateClientMigrateOrNot hangs +
    ZOOKEEPER-1606 + intermittent failures in ZkDatabaseCorruptionTest on jenkins +
    ZOOKEEPER-1610 + Some classes are using == or != to compare Long/String objects instead of .equals() +
    ZOOKEEPER-1613 + The documentation still points to 2008 in the copyright notice +
    ZOOKEEPER-1622 + session ids will be negative in the year 2022 +
    ZOOKEEPER-1624 + PrepRequestProcessor abort multi-operation incorrectly +
    ZOOKEEPER-1629 + testTransactionLogCorruption occasionally fails +
    ZOOKEEPER-1632 + fix memory leaks in cli_st +
    ZOOKEEPER-1633 + Introduce a protocol version to connection initiation message +
    ZOOKEEPER-1642 + Leader loading database twice +
    ZOOKEEPER-1645 + ZooKeeper OSGi package imports not complete +
    ZOOKEEPER-1646 + mt c client tests fail on Ubuntu Raring +
    ZOOKEEPER-1647 + OSGi package import/export changes not applied to bin-jar +
    ZOOKEEPER-1648 + Fix WatcherTest in JDK7 +
    ZOOKEEPER-1653 + zookeeper fails to start because of inconsistent epoch +
    ZOOKEEPER-1657 + Increased CPU usage by unnecessary SASL checks +
    ZOOKEEPER-1663 + scripts don't work when path contains spaces +
    ZOOKEEPER-1667 + Watch event isn't handled correctly when a client reestablish to a server +
    ZOOKEEPER-1696 + Fail to run zookeeper client on Weblogic application server +
    ZOOKEEPER-1697 + large snapshots can cause continuous quorum failure +
    ZOOKEEPER-1702 + ZooKeeper client may write operation packets before receiving successful response to connection request, can cause TCP RST +
    ZOOKEEPER-1706 + Typo in Double Barriers example +
    ZOOKEEPER-1711 + ZooKeeper server binds to all ip addresses for leader election and broadcast +
    ZOOKEEPER-1713 + wrong time calculation in zkfuse.cc +
    ZOOKEEPER-1714 + perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used +
    ZOOKEEPER-1719 + zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 +
    ZOOKEEPER-1731 + Unsynchronized access to ServerCnxnFactory.connectionBeans results in deadlock +
    ZOOKEEPER-1732 + ZooKeeper server unable to join established ensemble +
    ZOOKEEPER-1733 + FLETest#testLE is flaky on windows boxes +
    ZOOKEEPER-1744 + clientPortAddress breaks "zkServer.sh status" +
    ZOOKEEPER-1750 + Race condition producing NPE in NIOServerCnxn.toString +
    ZOOKEEPER-1751 + ClientCnxn#run could miss the second ping or connection get dropped before a ping +
    ZOOKEEPER-1753 + ClientCnxn is not properly releasing the resources, which are used to ping RwServer +
    ZOOKEEPER-1754 + Read-only server allows to create znode +
    ZOOKEEPER-1755 + Concurrent operations of four letter 'dump' ephemeral command and killSession causing NPE +
    ZOOKEEPER-1756 + zookeeper_interest() in C client can return a timeval of 0 +
    ZOOKEEPER-1764 + ZooKeeper attempts at SASL eventhough it shouldn't +
    ZOOKEEPER-1765 + Update code conventions link on "How to contribute" page +
    ZOOKEEPER-1770 + NullPointerException in SnapshotFormatter +
    ZOOKEEPER-1774 + QuorumPeerMainTest fails consistently with "complains about host" assertion failure +
    ZOOKEEPER-1775 + Ephemeral nodes not present in one of the members of the ensemble +
    ZOOKEEPER-1776 + Ephemeral nodes not present in one of the members of the ensemble +
    ZOOKEEPER-1781 + ZooKeeper Server fails if snapCount is set to 1 +
    ZOOKEEPER-1786 + ZooKeeper data model documentation is incorrect +
    ZOOKEEPER-1790 + Deal with special ObserverId in QuorumCnxManager.receiveConnection +
    ZOOKEEPER-1798 + Fix race condition in testNormalObserverRun +
    ZOOKEEPER-1799 + SaslAuthFailDesignatedClientTest.testAuth fails frequently on SUSE +
    ZOOKEEPER-1805 + "Don't care" value in ZooKeeper election breaks rolling upgrades +
    ZOOKEEPER-1811 + The ZooKeeperSaslClient service name principal is hardcoded to "zookeeper" +
    ZOOKEEPER-1812 + ZooInspector reconnection always fails if first connection fails +
    ZOOKEEPER-1821 + very ugly warning when compiling load_gen.c +
    ZOOKEEPER-1839 + Deadlock in NettyServerCnxn +
    ZOOKEEPER-1844 + TruncateTest fails on windows +
    ZOOKEEPER-1845 + FLETest.testLE fails on windows +
    ZOOKEEPER-1850 + cppunit test testNonexistingHost in TestZookeeperInit is failing on Unbuntu +
    +
    + + +
    + + + +Changes Since ZooKeeper 3.4.5 + +

    Improvement +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Changes Since ZooKeeper 3.4.5
    ZOOKEEPER-1019 + zkfuse doesn't list dependency on boost in README +
    ZOOKEEPER-1096 + Leader communication should listen on specified IP, not wildcard address +
    ZOOKEEPER-1324 + Remove Duplicate NEWLEADER packets from the Leader to the Follower. +
    ZOOKEEPER-1552 + Enable sync request processor in Observer +
    ZOOKEEPER-1564 + Allow JUnit test build with IBM Java +
    ZOOKEEPER-1583 + Document maxClientCnxns in conf/zoo_sample.cfg +
    ZOOKEEPER-1584 + Adding mvn-install target for deploying the zookeeper artifacts to .m2 repository. +
    ZOOKEEPER-1598 + Ability to support more digits in the version string +
    ZOOKEEPER-1615 + minor typos in ZooKeeper Programmer's Guide web page +
    ZOOKEEPER-1627 + Add org.apache.zookeeper.common to exported packages in OSGi MANIFEST headers +
    ZOOKEEPER-1666 + Avoid Reverse DNS lookup if the hostname in connection string is literal IP address. +
    ZOOKEEPER-1715 + Upgrade netty version +
    ZOOKEEPER-1758 + Add documentation for zookeeper.observer.syncEnabled flag +
    ZOOKEEPER-1771 + ZooInspector authentication +
    +
    + + +
    + + + +Changes Since ZooKeeper 3.4.5 + +

    Task +

    + + + + +
    Changes Since ZooKeeper 3.4.5
    ZOOKEEPER-1430 + add maven deploy support to the build +
    +
    + +

    Changes Since 3.4.4

    @@ -3995,7 +4766,6 @@

    Changes Since ZooKeeper 3.3.0

    -
    @@ -4019,7 +4789,7 @@

    Changes Since ZooKeeper 3.3.0

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tests failed! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tests failed! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/contrib/zooinspector/ivy.xml b/src/contrib/zooinspector/ivy.xml index 4e5107e36aa..096f05c3f83 100644 --- a/src/contrib/zooinspector/ivy.xml +++ b/src/contrib/zooinspector/ivy.xml @@ -1,48 +1,48 @@ - - - - - - - - ZooInspector - - - - - - - - - - - - - - - - - - - + + + + + + + + ZooInspector + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/contrib/zooinspector/lib/log4j.properties b/src/contrib/zooinspector/lib/log4j.properties index 970670c7177..6f96d844ac2 100644 --- a/src/contrib/zooinspector/lib/log4j.properties +++ b/src/contrib/zooinspector/lib/log4j.properties @@ -1,9 +1,9 @@ -# ***** Set root logger level to INFO and it appender to stdout. -log4j.rootLogger=info, stdout - -# ***** stdout is set to be a ConsoleAppender. -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -# ***** stdout uses PatternLayout. -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -# ***** Pattern to output the caller's file name and line number. +# ***** Set root logger level to INFO and it appender to stdout. +log4j.rootLogger=info, stdout + +# ***** stdout is set to be a ConsoleAppender. +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +# ***** stdout uses PatternLayout. +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +# ***** Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n \ No newline at end of file diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java index 28a4b2100bf..b637bfd4cf0 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java @@ -1,66 +1,66 @@ -/** - * 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.inspector; - -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; - -import javax.swing.JFrame; -import javax.swing.JOptionPane; -import javax.swing.UIManager; - -import org.apache.zookeeper.inspector.gui.ZooInspectorPanel; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorManagerImpl; - -/** - * - */ -public class ZooInspector { - /** - * @param args - * - not used. The value of these parameters will have no effect - * on the application - */ - public static void main(String[] args) { - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - JFrame frame = new JFrame("ZooInspector"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - final ZooInspectorPanel zooInspectorPanel = new ZooInspectorPanel( - new ZooInspectorManagerImpl()); - frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosed(WindowEvent e) { - super.windowClosed(e); - zooInspectorPanel.disconnect(true); - } - }); - - frame.setContentPane(zooInspectorPanel); - frame.setSize(1024, 768); - frame.setVisible(true); - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred loading ZooInspector", e); - JOptionPane.showMessageDialog(null, - "ZooInspector failed to start: " + e.getMessage(), "Error", - JOptionPane.ERROR_MESSAGE); - } - } -} +/** + * 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.inspector; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.UIManager; + +import org.apache.zookeeper.inspector.gui.ZooInspectorPanel; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.ZooInspectorManagerImpl; + +/** + * + */ +public class ZooInspector { + /** + * @param args + * - not used. The value of these parameters will have no effect + * on the application + */ + public static void main(String[] args) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + JFrame frame = new JFrame("ZooInspector"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + final ZooInspectorPanel zooInspectorPanel = new ZooInspectorPanel( + new ZooInspectorManagerImpl()); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + super.windowClosed(e); + zooInspectorPanel.disconnect(true); + } + }); + + frame.setContentPane(zooInspectorPanel); + frame.setSize(1024, 768); + frame.setVisible(true); + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred loading ZooInspector", e); + JOptionPane.showMessageDialog(null, + "ZooInspector failed to start: " + e.getMessage(), "Error", + JOptionPane.ERROR_MESSAGE); + } + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java index bab799c13a5..a9e5ac477cf 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java @@ -1,50 +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.inspector.encryption; - -/** - * - */ -public class BasicDataEncryptionManager implements DataEncryptionManager { - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.encryption.DataEncryptionManager#decryptData - * (byte[]) - */ - public String decryptData(byte[] encrypted) throws Exception { - return new String(encrypted); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.encryption.DataEncryptionManager#encryptData - * (java.lang.String) - */ - public byte[] encryptData(String data) throws Exception { - if (data == null) { - return new byte[0]; - } - return data.getBytes(); - } - -} +/** + * 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.inspector.encryption; + +/** + * + */ +public class BasicDataEncryptionManager implements DataEncryptionManager { + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.encryption.DataEncryptionManager#decryptData + * (byte[]) + */ + public String decryptData(byte[] encrypted) throws Exception { + return new String(encrypted); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.encryption.DataEncryptionManager#encryptData + * (java.lang.String) + */ + public byte[] encryptData(String data) throws Exception { + if (data == null) { + return new byte[0]; + } + return data.getBytes(); + } + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java index 8cca3ca69ec..15a9ee4350f 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java @@ -1,39 +1,39 @@ -/** - * 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.inspector.encryption; - -/** - * A class which describes how data should be encrypted and decrypted - */ -public interface DataEncryptionManager { - /** - * @param data - * - the data to be encrypted - * @return the encrypted data - * @throws Exception - */ - public byte[] encryptData(String data) throws Exception; - - /** - * @param encrypted - * - the data to be decrypted - * @return the decrypted data - * @throws Exception - */ - public String decryptData(byte[] encrypted) throws Exception; -} +/** + * 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.inspector.encryption; + +/** + * A class which describes how data should be encrypted and decrypted + */ +public interface DataEncryptionManager { + /** + * @param data + * - the data to be encrypted + * @return the encrypted data + * @throws Exception + */ + public byte[] encryptData(String data) throws Exception; + + /** + * @param encrypted + * - the data to be decrypted + * @return the decrypted data + * @throws Exception + */ + public String decryptData(byte[] encrypted) throws Exception; +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java index ce8d187d3fa..b0b1e9801c7 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java @@ -1,37 +1,37 @@ -/** - * 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.inspector.gui; - -import java.util.List; - -import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; - -/** - * A Listener for changes to the configuration of which node viewers are shown - */ -public interface NodeViewersChangeListener { - /** - * Called when the node viewers configuration is changed (i.e node viewers - * are added, removed or the order of the node viewers is changed) - * - * @param newViewers - * - a {@link List} of {@link ZooInspectorNodeViewer}s which are - * to be shown - */ - public void nodeViewersChanged(List newViewers); -} +/** + * 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.inspector.gui; + +import java.util.List; + +import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; + +/** + * A Listener for changes to the configuration of which node viewers are shown + */ +public interface NodeViewersChangeListener { + /** + * Called when the node viewers configuration is changed (i.e node viewers + * are added, removed or the order of the node viewers is changed) + * + * @param newViewers + * - a {@link List} of {@link ZooInspectorNodeViewer}s which are + * to be shown + */ + public void nodeViewersChanged(List newViewers); +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java index 1acb16aa8f7..fbae8f10fe2 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java @@ -1,80 +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.inspector.gui; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Frame; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; - -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JPanel; - -import org.apache.zookeeper.inspector.logger.LoggerFactory; - -/** - * The About Dialog for the application - */ -public class ZooInspectorAboutDialog extends JDialog { - /** - * @param frame - * - the Frame from which the dialog is displayed - */ - public ZooInspectorAboutDialog(Frame frame) { - super(frame); - this.setLayout(new BorderLayout()); - this.setIconImage(ZooInspectorIconResources.getInformationIcon() - .getImage()); - this.setTitle("About ZooInspector"); - this.setModal(true); - this.setAlwaysOnTop(true); - this.setResizable(false); - JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - JEditorPane aboutPane = new JEditorPane(); - aboutPane.setEditable(false); - aboutPane.setOpaque(false); - java.net.URL aboutURL = ZooInspectorAboutDialog.class - .getResource("about.html"); - try { - aboutPane.setPage(aboutURL); - } catch (IOException e) { - LoggerFactory.getLogger().error( - "Error loading about.html, file may be corrupt", e); - } - panel.add(aboutPane, BorderLayout.CENTER); - panel.setPreferredSize(new Dimension(600, 200)); - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); - JButton okButton = new JButton("OK"); - okButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ZooInspectorAboutDialog.this.dispose(); - } - }); - buttonsPanel.add(okButton); - this.add(panel, BorderLayout.CENTER); - this.add(buttonsPanel, BorderLayout.SOUTH); - this.pack(); - } -} +/** + * 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.inspector.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JPanel; + +import org.apache.zookeeper.inspector.logger.LoggerFactory; + +/** + * The About Dialog for the application + */ +public class ZooInspectorAboutDialog extends JDialog { + /** + * @param frame + * - the Frame from which the dialog is displayed + */ + public ZooInspectorAboutDialog(Frame frame) { + super(frame); + this.setLayout(new BorderLayout()); + this.setIconImage(ZooInspectorIconResources.getInformationIcon() + .getImage()); + this.setTitle("About ZooInspector"); + this.setModal(true); + this.setAlwaysOnTop(true); + this.setResizable(false); + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + JEditorPane aboutPane = new JEditorPane(); + aboutPane.setEditable(false); + aboutPane.setOpaque(false); + java.net.URL aboutURL = ZooInspectorAboutDialog.class + .getResource("about.html"); + try { + aboutPane.setPage(aboutURL); + } catch (IOException e) { + LoggerFactory.getLogger().error( + "Error loading about.html, file may be corrupt", e); + } + panel.add(aboutPane, BorderLayout.CENTER); + panel.setPreferredSize(new Dimension(600, 200)); + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ZooInspectorAboutDialog.this.dispose(); + } + }); + buttonsPanel.add(okButton); + this.add(panel, BorderLayout.CENTER); + this.add(buttonsPanel, BorderLayout.SOUTH); + this.pack(); + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java index 6c7e88d8fc2..c7db5243b2c 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java @@ -1,321 +1,321 @@ -/** - * 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.inspector.gui; - -import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Map.Entry; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.Pair; - -/** - * The connection properties dialog. This is used to determine the settings for - * connecting to a zookeeper instance - */ -public class ZooInspectorConnectionPropertiesDialog extends JDialog { - - private final HashMap components; - - /** - * @param lastConnectionProps - * - the last connection properties used. if this is the first - * conneciton since starting the applications this will be the - * default settings - * @param connectionPropertiesTemplateAndLabels - * - the connection properties and labels to show in this dialog - * @param zooInspectorPanel - * - the {@link ZooInspectorPanel} linked to this dialog - */ - public ZooInspectorConnectionPropertiesDialog( - Properties lastConnectionProps, - Pair>, Map> connectionPropertiesTemplateAndLabels, - final ZooInspectorPanel zooInspectorPanel) { - final Map> connectionPropertiesTemplate = connectionPropertiesTemplateAndLabels - .getKey(); - final Map connectionPropertiesLabels = connectionPropertiesTemplateAndLabels - .getValue(); - this.setLayout(new BorderLayout()); - this.setTitle("Connection Settings"); - this.setModal(true); - this.setAlwaysOnTop(true); - this.setResizable(false); - final JPanel options = new JPanel(); - final JFileChooser fileChooser = new JFileChooser(); - options.setLayout(new GridBagLayout()); - int i = 0; - components = new HashMap(); - for (Entry> entry : connectionPropertiesTemplate - .entrySet()) { - int rowPos = 2 * i + 1; - JLabel label = new JLabel(connectionPropertiesLabels.get(entry - .getKey())); - GridBagConstraints c1 = new GridBagConstraints(); - c1.gridx = 0; - c1.gridy = rowPos; - c1.gridwidth = 1; - c1.gridheight = 1; - c1.weightx = 0; - c1.weighty = 0; - c1.anchor = GridBagConstraints.WEST; - c1.fill = GridBagConstraints.HORIZONTAL; - c1.insets = new Insets(5, 5, 5, 5); - c1.ipadx = 0; - c1.ipady = 0; - options.add(label, c1); - if (entry.getValue().size() == 0) { - JTextField text = new JTextField(); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 2; - c2.gridy = rowPos; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.WEST; - c2.fill = GridBagConstraints.HORIZONTAL; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - options.add(text, c2); - components.put(entry.getKey(), text); - } else if (entry.getValue().size() == 1) { - JTextField text = new JTextField(entry.getValue().get(0)); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 2; - c2.gridy = rowPos; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.WEST; - c2.fill = GridBagConstraints.HORIZONTAL; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - options.add(text, c2); - components.put(entry.getKey(), text); - } else { - List list = entry.getValue(); - JComboBox combo = new JComboBox(list.toArray(new String[list - .size()])); - combo.setSelectedItem(list.get(0)); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 2; - c2.gridy = rowPos; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.WEST; - c2.fill = GridBagConstraints.HORIZONTAL; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - options.add(combo, c2); - components.put(entry.getKey(), combo); - } - i++; - } - loadConnectionProps(lastConnectionProps); - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new GridBagLayout()); - JButton loadPropsFileButton = new JButton("Load from file"); - loadPropsFileButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - int result = fileChooser - .showOpenDialog(ZooInspectorConnectionPropertiesDialog.this); - if (result == JFileChooser.APPROVE_OPTION) { - File propsFilePath = fileChooser.getSelectedFile(); - Properties props = new Properties(); - try { - FileReader reader = new FileReader(propsFilePath); - try { - props.load(reader); - loadConnectionProps(props); - } finally { - reader.close(); - } - } catch (IOException ex) { - LoggerFactory - .getLogger() - .error( - "An Error occurred loading connection properties from file", - ex); - JOptionPane - .showMessageDialog( - ZooInspectorConnectionPropertiesDialog.this, - "An Error occurred loading connection properties from file", - "Error", JOptionPane.ERROR_MESSAGE); - } - options.revalidate(); - options.repaint(); - } - - } - }); - GridBagConstraints c3 = new GridBagConstraints(); - c3.gridx = 0; - c3.gridy = 0; - c3.gridwidth = 1; - c3.gridheight = 1; - c3.weightx = 0; - c3.weighty = 1; - c3.anchor = GridBagConstraints.SOUTHWEST; - c3.fill = GridBagConstraints.NONE; - c3.insets = new Insets(5, 5, 5, 5); - c3.ipadx = 0; - c3.ipady = 0; - buttonsPanel.add(loadPropsFileButton, c3); - JButton saveDefaultPropsFileButton = new JButton("Set As Default"); - saveDefaultPropsFileButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - - Properties connectionProps = getConnectionProps(); - try { - zooInspectorPanel - .setdefaultConnectionProps(connectionProps); - } catch (IOException ex) { - LoggerFactory - .getLogger() - .error( - "An Error occurred saving the default connection properties file", - ex); - JOptionPane - .showMessageDialog( - ZooInspectorConnectionPropertiesDialog.this, - "An Error occurred saving the default connection properties file", - "Error", JOptionPane.ERROR_MESSAGE); - } - } - }); - GridBagConstraints c6 = new GridBagConstraints(); - c6.gridx = 1; - c6.gridy = 0; - c6.gridwidth = 1; - c6.gridheight = 1; - c6.weightx = 1; - c6.weighty = 1; - c6.anchor = GridBagConstraints.SOUTHWEST; - c6.fill = GridBagConstraints.NONE; - c6.insets = new Insets(5, 5, 5, 5); - c6.ipadx = 0; - c6.ipady = 0; - buttonsPanel.add(saveDefaultPropsFileButton, c6); - JButton okButton = new JButton("OK"); - okButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - ZooInspectorConnectionPropertiesDialog.this.dispose(); - Properties connectionProps = getConnectionProps(); - zooInspectorPanel.connect(connectionProps); - } - }); - GridBagConstraints c4 = new GridBagConstraints(); - c4.gridx = 2; - c4.gridy = 0; - c4.gridwidth = 1; - c4.gridheight = 1; - c4.weightx = 0; - c4.weighty = 1; - c4.anchor = GridBagConstraints.SOUTH; - c4.fill = GridBagConstraints.HORIZONTAL; - c4.insets = new Insets(5, 5, 5, 5); - c4.ipadx = 0; - c4.ipady = 0; - buttonsPanel.add(okButton, c4); - JButton cancelButton = new JButton("Cancel"); - cancelButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - ZooInspectorConnectionPropertiesDialog.this.dispose(); - } - }); - GridBagConstraints c5 = new GridBagConstraints(); - c5.gridx = 3; - c5.gridy = 0; - c5.gridwidth = 1; - c5.gridheight = 1; - c5.weightx = 0; - c5.weighty = 1; - c5.anchor = GridBagConstraints.SOUTH; - c5.fill = GridBagConstraints.HORIZONTAL; - c5.insets = new Insets(5, 5, 5, 5); - c5.ipadx = 0; - c5.ipady = 0; - buttonsPanel.add(cancelButton, c5); - this.add(options, BorderLayout.CENTER); - this.add(buttonsPanel, BorderLayout.SOUTH); - this.pack(); - } - - private void loadConnectionProps(Properties props) { - if (props != null) { - for (Object key : props.keySet()) { - String propsKey = (String) key; - if (components.containsKey(propsKey)) { - JComponent component = components.get(propsKey); - String value = props.getProperty(propsKey); - if (component instanceof JTextField) { - ((JTextField) component).setText(value); - } else if (component instanceof JComboBox) { - ((JComboBox) component).setSelectedItem(value); - } - } - } - } - } - - private Properties getConnectionProps() { - Properties connectionProps = new Properties(); - for (Entry entry : components.entrySet()) { - String value = null; - JComponent component = entry.getValue(); - if (component instanceof JTextField) { - value = ((JTextField) component).getText(); - } else if (component instanceof JComboBox) { - value = ((JComboBox) component).getSelectedItem().toString(); - } - connectionProps.put(entry.getKey(), value); - } - return connectionProps; - } -} +/** + * 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.inspector.gui; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Map.Entry; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.Pair; + +/** + * The connection properties dialog. This is used to determine the settings for + * connecting to a zookeeper instance + */ +public class ZooInspectorConnectionPropertiesDialog extends JDialog { + + private final HashMap components; + + /** + * @param lastConnectionProps + * - the last connection properties used. if this is the first + * conneciton since starting the applications this will be the + * default settings + * @param connectionPropertiesTemplateAndLabels + * - the connection properties and labels to show in this dialog + * @param zooInspectorPanel + * - the {@link ZooInspectorPanel} linked to this dialog + */ + public ZooInspectorConnectionPropertiesDialog( + Properties lastConnectionProps, + Pair>, Map> connectionPropertiesTemplateAndLabels, + final ZooInspectorPanel zooInspectorPanel) { + final Map> connectionPropertiesTemplate = connectionPropertiesTemplateAndLabels + .getKey(); + final Map connectionPropertiesLabels = connectionPropertiesTemplateAndLabels + .getValue(); + this.setLayout(new BorderLayout()); + this.setTitle("Connection Settings"); + this.setModal(true); + this.setAlwaysOnTop(true); + this.setResizable(false); + final JPanel options = new JPanel(); + final JFileChooser fileChooser = new JFileChooser(); + options.setLayout(new GridBagLayout()); + int i = 0; + components = new HashMap(); + for (Entry> entry : connectionPropertiesTemplate + .entrySet()) { + int rowPos = 2 * i + 1; + JLabel label = new JLabel(connectionPropertiesLabels.get(entry + .getKey())); + GridBagConstraints c1 = new GridBagConstraints(); + c1.gridx = 0; + c1.gridy = rowPos; + c1.gridwidth = 1; + c1.gridheight = 1; + c1.weightx = 0; + c1.weighty = 0; + c1.anchor = GridBagConstraints.WEST; + c1.fill = GridBagConstraints.HORIZONTAL; + c1.insets = new Insets(5, 5, 5, 5); + c1.ipadx = 0; + c1.ipady = 0; + options.add(label, c1); + if (entry.getValue().size() == 0) { + JTextField text = new JTextField(); + GridBagConstraints c2 = new GridBagConstraints(); + c2.gridx = 2; + c2.gridy = rowPos; + c2.gridwidth = 1; + c2.gridheight = 1; + c2.weightx = 0; + c2.weighty = 0; + c2.anchor = GridBagConstraints.WEST; + c2.fill = GridBagConstraints.HORIZONTAL; + c2.insets = new Insets(5, 5, 5, 5); + c2.ipadx = 0; + c2.ipady = 0; + options.add(text, c2); + components.put(entry.getKey(), text); + } else if (entry.getValue().size() == 1) { + JTextField text = new JTextField(entry.getValue().get(0)); + GridBagConstraints c2 = new GridBagConstraints(); + c2.gridx = 2; + c2.gridy = rowPos; + c2.gridwidth = 1; + c2.gridheight = 1; + c2.weightx = 0; + c2.weighty = 0; + c2.anchor = GridBagConstraints.WEST; + c2.fill = GridBagConstraints.HORIZONTAL; + c2.insets = new Insets(5, 5, 5, 5); + c2.ipadx = 0; + c2.ipady = 0; + options.add(text, c2); + components.put(entry.getKey(), text); + } else { + List list = entry.getValue(); + JComboBox combo = new JComboBox(list.toArray(new String[list + .size()])); + combo.setSelectedItem(list.get(0)); + GridBagConstraints c2 = new GridBagConstraints(); + c2.gridx = 2; + c2.gridy = rowPos; + c2.gridwidth = 1; + c2.gridheight = 1; + c2.weightx = 0; + c2.weighty = 0; + c2.anchor = GridBagConstraints.WEST; + c2.fill = GridBagConstraints.HORIZONTAL; + c2.insets = new Insets(5, 5, 5, 5); + c2.ipadx = 0; + c2.ipady = 0; + options.add(combo, c2); + components.put(entry.getKey(), combo); + } + i++; + } + loadConnectionProps(lastConnectionProps); + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new GridBagLayout()); + JButton loadPropsFileButton = new JButton("Load from file"); + loadPropsFileButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + int result = fileChooser + .showOpenDialog(ZooInspectorConnectionPropertiesDialog.this); + if (result == JFileChooser.APPROVE_OPTION) { + File propsFilePath = fileChooser.getSelectedFile(); + Properties props = new Properties(); + try { + FileReader reader = new FileReader(propsFilePath); + try { + props.load(reader); + loadConnectionProps(props); + } finally { + reader.close(); + } + } catch (IOException ex) { + LoggerFactory + .getLogger() + .error( + "An Error occurred loading connection properties from file", + ex); + JOptionPane + .showMessageDialog( + ZooInspectorConnectionPropertiesDialog.this, + "An Error occurred loading connection properties from file", + "Error", JOptionPane.ERROR_MESSAGE); + } + options.revalidate(); + options.repaint(); + } + + } + }); + GridBagConstraints c3 = new GridBagConstraints(); + c3.gridx = 0; + c3.gridy = 0; + c3.gridwidth = 1; + c3.gridheight = 1; + c3.weightx = 0; + c3.weighty = 1; + c3.anchor = GridBagConstraints.SOUTHWEST; + c3.fill = GridBagConstraints.NONE; + c3.insets = new Insets(5, 5, 5, 5); + c3.ipadx = 0; + c3.ipady = 0; + buttonsPanel.add(loadPropsFileButton, c3); + JButton saveDefaultPropsFileButton = new JButton("Set As Default"); + saveDefaultPropsFileButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + + Properties connectionProps = getConnectionProps(); + try { + zooInspectorPanel + .setdefaultConnectionProps(connectionProps); + } catch (IOException ex) { + LoggerFactory + .getLogger() + .error( + "An Error occurred saving the default connection properties file", + ex); + JOptionPane + .showMessageDialog( + ZooInspectorConnectionPropertiesDialog.this, + "An Error occurred saving the default connection properties file", + "Error", JOptionPane.ERROR_MESSAGE); + } + } + }); + GridBagConstraints c6 = new GridBagConstraints(); + c6.gridx = 1; + c6.gridy = 0; + c6.gridwidth = 1; + c6.gridheight = 1; + c6.weightx = 1; + c6.weighty = 1; + c6.anchor = GridBagConstraints.SOUTHWEST; + c6.fill = GridBagConstraints.NONE; + c6.insets = new Insets(5, 5, 5, 5); + c6.ipadx = 0; + c6.ipady = 0; + buttonsPanel.add(saveDefaultPropsFileButton, c6); + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + ZooInspectorConnectionPropertiesDialog.this.dispose(); + Properties connectionProps = getConnectionProps(); + zooInspectorPanel.connect(connectionProps); + } + }); + GridBagConstraints c4 = new GridBagConstraints(); + c4.gridx = 2; + c4.gridy = 0; + c4.gridwidth = 1; + c4.gridheight = 1; + c4.weightx = 0; + c4.weighty = 1; + c4.anchor = GridBagConstraints.SOUTH; + c4.fill = GridBagConstraints.HORIZONTAL; + c4.insets = new Insets(5, 5, 5, 5); + c4.ipadx = 0; + c4.ipady = 0; + buttonsPanel.add(okButton, c4); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + ZooInspectorConnectionPropertiesDialog.this.dispose(); + } + }); + GridBagConstraints c5 = new GridBagConstraints(); + c5.gridx = 3; + c5.gridy = 0; + c5.gridwidth = 1; + c5.gridheight = 1; + c5.weightx = 0; + c5.weighty = 1; + c5.anchor = GridBagConstraints.SOUTH; + c5.fill = GridBagConstraints.HORIZONTAL; + c5.insets = new Insets(5, 5, 5, 5); + c5.ipadx = 0; + c5.ipady = 0; + buttonsPanel.add(cancelButton, c5); + this.add(options, BorderLayout.CENTER); + this.add(buttonsPanel, BorderLayout.SOUTH); + this.pack(); + } + + private void loadConnectionProps(Properties props) { + if (props != null) { + for (Object key : props.keySet()) { + String propsKey = (String) key; + if (components.containsKey(propsKey)) { + JComponent component = components.get(propsKey); + String value = props.getProperty(propsKey); + if (component instanceof JTextField) { + ((JTextField) component).setText(value); + } else if (component instanceof JComboBox) { + ((JComboBox) component).setSelectedItem(value); + } + } + } + } + } + + private Properties getConnectionProps() { + Properties connectionProps = new Properties(); + for (Entry entry : components.entrySet()) { + String value = null; + JComponent component = entry.getValue(); + if (component instanceof JTextField) { + value = ((JTextField) component).getText(); + } else if (component instanceof JComboBox) { + value = ((JComboBox) component).getSelectedItem().toString(); + } + connectionProps.put(entry.getKey(), value); + } + return connectionProps; + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java index cc925a98fdb..fa45ab31ede 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java @@ -1,118 +1,118 @@ -/** - * 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.inspector.gui; - -import javax.swing.ImageIcon; - -/** - * A class containing static methods for retrieving {@link ImageIcon}s used in - * the application - */ -public class ZooInspectorIconResources { - - /** - * @return file icon - */ - public static ImageIcon getTreeLeafIcon() { - return new ImageIcon("icons/file_obj.gif"); //$NON-NLS-1$ - } - - /** - * @return folder open icon - */ - public static ImageIcon getTreeOpenIcon() { - return new ImageIcon("icons/fldr_obj.gif"); //$NON-NLS-1$ - } - - /** - * @return folder closed icon - */ - public static ImageIcon getTreeClosedIcon() { - return new ImageIcon("icons/fldr_obj.gif"); //$NON-NLS-1$ - } - - /** - * @return connect icon - */ - public static ImageIcon getConnectIcon() { - return new ImageIcon("icons/launch_run.gif"); //$NON-NLS-1$ - } - - /** - * @return disconnect icon - */ - public static ImageIcon getDisconnectIcon() { - return new ImageIcon("icons/launch_stop.gif"); //$NON-NLS-1$ - } - - /** - * @return save icon - */ - public static ImageIcon getSaveIcon() { - return new ImageIcon("icons/save_edit.gif"); //$NON-NLS-1$ - } - - /** - * @return add icon - */ - public static ImageIcon getAddNodeIcon() { - return new ImageIcon("icons/new_con.gif"); //$NON-NLS-1$ - } - - /** - * @return delete icon - */ - public static ImageIcon getDeleteNodeIcon() { - return new ImageIcon("icons/trash.gif"); //$NON-NLS-1$ - } - - /** - * @return refresh icon - */ - public static ImageIcon getRefreshIcon() { - return new ImageIcon("icons/refresh.gif"); //$NON-NLS-1$ - } - - /** - * @return information icon - */ - public static ImageIcon getInformationIcon() { - return new ImageIcon("icons/info_obj.gif"); //$NON-NLS-1$ - } - - /** - * @return node viewers icon - */ - public static ImageIcon getChangeNodeViewersIcon() { - return new ImageIcon("icons/edtsrclkup_co.gif"); //$NON-NLS-1$ - } - - /** - * @return up icon - */ - public static ImageIcon getUpIcon() { - return new ImageIcon("icons/search_prev.gif"); //$NON-NLS-1$ - } - - /** - * @return down icon - */ - public static ImageIcon getDownIcon() { - return new ImageIcon("icons/search_next.gif"); //$NON-NLS-1$ - } -} +/** + * 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.inspector.gui; + +import javax.swing.ImageIcon; + +/** + * A class containing static methods for retrieving {@link ImageIcon}s used in + * the application + */ +public class ZooInspectorIconResources { + + /** + * @return file icon + */ + public static ImageIcon getTreeLeafIcon() { + return new ImageIcon("icons/file_obj.gif"); //$NON-NLS-1$ + } + + /** + * @return folder open icon + */ + public static ImageIcon getTreeOpenIcon() { + return new ImageIcon("icons/fldr_obj.gif"); //$NON-NLS-1$ + } + + /** + * @return folder closed icon + */ + public static ImageIcon getTreeClosedIcon() { + return new ImageIcon("icons/fldr_obj.gif"); //$NON-NLS-1$ + } + + /** + * @return connect icon + */ + public static ImageIcon getConnectIcon() { + return new ImageIcon("icons/launch_run.gif"); //$NON-NLS-1$ + } + + /** + * @return disconnect icon + */ + public static ImageIcon getDisconnectIcon() { + return new ImageIcon("icons/launch_stop.gif"); //$NON-NLS-1$ + } + + /** + * @return save icon + */ + public static ImageIcon getSaveIcon() { + return new ImageIcon("icons/save_edit.gif"); //$NON-NLS-1$ + } + + /** + * @return add icon + */ + public static ImageIcon getAddNodeIcon() { + return new ImageIcon("icons/new_con.gif"); //$NON-NLS-1$ + } + + /** + * @return delete icon + */ + public static ImageIcon getDeleteNodeIcon() { + return new ImageIcon("icons/trash.gif"); //$NON-NLS-1$ + } + + /** + * @return refresh icon + */ + public static ImageIcon getRefreshIcon() { + return new ImageIcon("icons/refresh.gif"); //$NON-NLS-1$ + } + + /** + * @return information icon + */ + public static ImageIcon getInformationIcon() { + return new ImageIcon("icons/info_obj.gif"); //$NON-NLS-1$ + } + + /** + * @return node viewers icon + */ + public static ImageIcon getChangeNodeViewersIcon() { + return new ImageIcon("icons/edtsrclkup_co.gif"); //$NON-NLS-1$ + } + + /** + * @return up icon + */ + public static ImageIcon getUpIcon() { + return new ImageIcon("icons/search_prev.gif"); //$NON-NLS-1$ + } + + /** + * @return down icon + */ + public static ImageIcon getDownIcon() { + return new ImageIcon("icons/search_next.gif"); //$NON-NLS-1$ + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java index 66125fd9c20..fd0b00ac0b1 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java @@ -1,605 +1,605 @@ -/** - * 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.inspector.gui; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.FlowLayout; -import java.awt.Frame; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.datatransfer.Transferable; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.swing.DefaultListCellRenderer; -import javax.swing.DefaultListModel; -import javax.swing.DropMode; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.TransferHandler; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; - -import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; - -/** - * A {@link JDialog} for configuring which {@link ZooInspectorNodeViewer}s to - * show in the application - */ -public class ZooInspectorNodeViewersDialog extends JDialog implements - ListSelectionListener { - - private final JButton upButton; - private final JButton downButton; - private final JButton removeButton; - private final JButton addButton; - private final JList viewersList; - private final JButton saveFileButton; - private final JButton loadFileButton; - private final JButton setDefaultsButton; - private final JFileChooser fileChooser = new JFileChooser(new File(".")); - - /** - * @param frame - * - the Frame from which the dialog is displayed - * @param currentViewers - * - the {@link ZooInspectorNodeViewer}s to show - * @param listeners - * - the {@link NodeViewersChangeListener}s which need to be - * notified of changes to the node viewers configuration - * @param manager - * - the {@link ZooInspectorManager} for the application - * - */ - public ZooInspectorNodeViewersDialog(Frame frame, - final List currentViewers, - final Collection listeners, - final ZooInspectorManager manager) { - super(frame); - final List newViewers = new ArrayList( - currentViewers); - this.setLayout(new BorderLayout()); - this.setIconImage(ZooInspectorIconResources.getChangeNodeViewersIcon() - .getImage()); - this.setTitle("About ZooInspector"); - this.setModal(true); - this.setAlwaysOnTop(true); - this.setResizable(true); - final JPanel panel = new JPanel(); - panel.setLayout(new GridBagLayout()); - viewersList = new JList(); - DefaultListModel model = new DefaultListModel(); - for (ZooInspectorNodeViewer viewer : newViewers) { - model.addElement(viewer); - } - viewersList.setModel(model); - viewersList.setCellRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(JList list, - Object value, int index, boolean isSelected, - boolean cellHasFocus) { - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) value; - JLabel label = (JLabel) super.getListCellRendererComponent( - list, value, index, isSelected, cellHasFocus); - label.setText(viewer.getTitle()); - return label; - } - }); - viewersList.setDropMode(DropMode.INSERT); - viewersList.enableInputMethods(true); - viewersList.setDragEnabled(true); - viewersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - viewersList.getSelectionModel().addListSelectionListener(this); - viewersList.setTransferHandler(new TransferHandler() { - - @Override - public boolean canImport(TransferHandler.TransferSupport info) { - // we only import NodeViewers - if (!info - .isDataFlavorSupported(ZooInspectorNodeViewer.nodeViewerDataFlavor)) { - return false; - } - - JList.DropLocation dl = (JList.DropLocation) info - .getDropLocation(); - if (dl.getIndex() == -1) { - return false; - } - return true; - } - - @Override - public boolean importData(TransferHandler.TransferSupport info) { - JList.DropLocation dl = (JList.DropLocation) info - .getDropLocation(); - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - int index = dl.getIndex(); - boolean insert = dl.isInsert(); - // Get the string that is being dropped. - Transferable t = info.getTransferable(); - String data; - try { - data = (String) t - .getTransferData(ZooInspectorNodeViewer.nodeViewerDataFlavor); - } catch (Exception e) { - return false; - } - try { - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class - .forName(data).newInstance(); - if (listModel.contains(viewer)) { - listModel.removeElement(viewer); - } - if (insert) { - listModel.add(index, viewer); - } else { - listModel.set(index, viewer); - } - return true; - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error instantiating class: " + data, e); - return false; - } - - } - - @Override - public int getSourceActions(JComponent c) { - return MOVE; - } - - @Override - protected Transferable createTransferable(JComponent c) { - JList list = (JList) c; - ZooInspectorNodeViewer value = (ZooInspectorNodeViewer) list - .getSelectedValue(); - return value; - } - }); - JScrollPane scroller = new JScrollPane(viewersList); - GridBagConstraints c1 = new GridBagConstraints(); - c1.gridx = 0; - c1.gridy = 0; - c1.gridwidth = 3; - c1.gridheight = 3; - c1.weightx = 0; - c1.weighty = 1; - c1.anchor = GridBagConstraints.CENTER; - c1.fill = GridBagConstraints.BOTH; - c1.insets = new Insets(5, 5, 5, 5); - c1.ipadx = 0; - c1.ipady = 0; - panel.add(scroller, c1); - upButton = new JButton(ZooInspectorIconResources.getUpIcon()); - downButton = new JButton(ZooInspectorIconResources.getDownIcon()); - removeButton = new JButton(ZooInspectorIconResources - .getDeleteNodeIcon()); - addButton = new JButton(ZooInspectorIconResources.getAddNodeIcon()); - upButton.setEnabled(false); - downButton.setEnabled(false); - removeButton.setEnabled(false); - addButton.setEnabled(true); - upButton.setToolTipText("Move currently selected node viewer up"); - downButton.setToolTipText("Move currently selected node viewer down"); - removeButton.setToolTipText("Remove currently selected node viewer"); - addButton.setToolTipText("Add node viewer"); - final JTextField newViewerTextField = new JTextField(); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 3; - c2.gridy = 0; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.NORTH; - c2.fill = GridBagConstraints.HORIZONTAL; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - panel.add(upButton, c2); - GridBagConstraints c3 = new GridBagConstraints(); - c3.gridx = 3; - c3.gridy = 2; - c3.gridwidth = 1; - c3.gridheight = 1; - c3.weightx = 0; - c3.weighty = 0; - c3.anchor = GridBagConstraints.NORTH; - c3.fill = GridBagConstraints.HORIZONTAL; - c3.insets = new Insets(5, 5, 5, 5); - c3.ipadx = 0; - c3.ipady = 0; - panel.add(downButton, c3); - GridBagConstraints c4 = new GridBagConstraints(); - c4.gridx = 3; - c4.gridy = 1; - c4.gridwidth = 1; - c4.gridheight = 1; - c4.weightx = 0; - c4.weighty = 0; - c4.anchor = GridBagConstraints.NORTH; - c4.fill = GridBagConstraints.HORIZONTAL; - c4.insets = new Insets(5, 5, 5, 5); - c4.ipadx = 0; - c4.ipady = 0; - panel.add(removeButton, c4); - GridBagConstraints c5 = new GridBagConstraints(); - c5.gridx = 0; - c5.gridy = 3; - c5.gridwidth = 3; - c5.gridheight = 1; - c5.weightx = 0; - c5.weighty = 0; - c5.anchor = GridBagConstraints.CENTER; - c5.fill = GridBagConstraints.BOTH; - c5.insets = new Insets(5, 5, 5, 5); - c5.ipadx = 0; - c5.ipady = 0; - panel.add(newViewerTextField, c5); - GridBagConstraints c6 = new GridBagConstraints(); - c6.gridx = 3; - c6.gridy = 3; - c6.gridwidth = 1; - c6.gridheight = 1; - c6.weightx = 0; - c6.weighty = 0; - c6.anchor = GridBagConstraints.CENTER; - c6.fill = GridBagConstraints.BOTH; - c6.insets = new Insets(5, 5, 5, 5); - c6.ipadx = 0; - c6.ipady = 0; - panel.add(addButton, c6); - upButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList - .getSelectedValue(); - int index = viewersList.getSelectedIndex(); - if (listModel.contains(viewer)) { - listModel.removeElementAt(index); - listModel.insertElementAt(viewer, index - 1); - viewersList.setSelectedValue(viewer, true); - } - } - }); - downButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList - .getSelectedValue(); - int index = viewersList.getSelectedIndex(); - if (listModel.contains(viewer)) { - listModel.removeElementAt(index); - listModel.insertElementAt(viewer, index + 1); - viewersList.setSelectedValue(viewer, true); - } - } - }); - removeButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList - .getSelectedValue(); - int index = viewersList.getSelectedIndex(); - if (listModel.contains(viewer)) { - listModel.removeElement(viewer); - viewersList - .setSelectedIndex(index == listModel.size() ? index - 1 - : index); - } - } - }); - addButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - String className = newViewerTextField.getText(); - if (className == null || className.length() == 0) { - JOptionPane - .showMessageDialog( - ZooInspectorNodeViewersDialog.this, - "Please enter the full class name for a Node Viewer and click the add button", - "Input Error", JOptionPane.ERROR_MESSAGE); - } else { - try { - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class - .forName(className).newInstance(); - if (listModel.contains(viewer)) { - JOptionPane - .showMessageDialog( - ZooInspectorNodeViewersDialog.this, - "Node viewer already exists. Each node viewer can only be added once.", - "Input Error", - JOptionPane.ERROR_MESSAGE); - } else { - listModel.addElement(viewer); - } - } catch (Exception ex) { - LoggerFactory - .getLogger() - .error( - "An error occurred while instaniating the node viewer. ", - ex); - JOptionPane.showMessageDialog( - ZooInspectorNodeViewersDialog.this, - "An error occurred while instaniating the node viewer: " - + ex.getMessage(), "Error", - JOptionPane.ERROR_MESSAGE); - } - } - } - }); - saveFileButton = new JButton("Save"); - loadFileButton = new JButton("Load"); - setDefaultsButton = new JButton("Set As Defaults"); - saveFileButton - .setToolTipText("Save current node viewer configuration to file"); - loadFileButton - .setToolTipText("Load node viewer configuration frm file"); - setDefaultsButton - .setToolTipText("Set current configuration asd defaults"); - GridBagConstraints c7 = new GridBagConstraints(); - c7.gridx = 0; - c7.gridy = 4; - c7.gridwidth = 1; - c7.gridheight = 1; - c7.weightx = 1; - c7.weighty = 0; - c7.anchor = GridBagConstraints.WEST; - c7.fill = GridBagConstraints.VERTICAL; - c7.insets = new Insets(5, 5, 5, 5); - c7.ipadx = 0; - c7.ipady = 0; - panel.add(saveFileButton, c7); - GridBagConstraints c8 = new GridBagConstraints(); - c8.gridx = 1; - c8.gridy = 4; - c8.gridwidth = 1; - c8.gridheight = 1; - c8.weightx = 0; - c8.weighty = 0; - c8.anchor = GridBagConstraints.WEST; - c8.fill = GridBagConstraints.VERTICAL; - c8.insets = new Insets(5, 5, 5, 5); - c8.ipadx = 0; - c8.ipady = 0; - panel.add(loadFileButton, c8); - GridBagConstraints c9 = new GridBagConstraints(); - c9.gridx = 2; - c9.gridy = 4; - c9.gridwidth = 1; - c9.gridheight = 1; - c9.weightx = 0; - c9.weighty = 0; - c9.anchor = GridBagConstraints.WEST; - c9.fill = GridBagConstraints.VERTICAL; - c9.insets = new Insets(5, 5, 5, 5); - c9.ipadx = 0; - c9.ipady = 0; - panel.add(setDefaultsButton, c9); - saveFileButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - int result = fileChooser - .showSaveDialog(ZooInspectorNodeViewersDialog.this); - if (result == JFileChooser.APPROVE_OPTION) { - File selectedFile = fileChooser.getSelectedFile(); - int answer = JOptionPane.YES_OPTION; - if (selectedFile.exists()) { - answer = JOptionPane - .showConfirmDialog( - ZooInspectorNodeViewersDialog.this, - "The specified file already exists. do you want to overwrite it?", - "Confirm Overwrite", - JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE); - } - if (answer == JOptionPane.YES_OPTION) { - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - List nodeViewersClassNames = new ArrayList(); - Object[] modelContents = listModel.toArray(); - for (Object o : modelContents) { - nodeViewersClassNames - .add(((ZooInspectorNodeViewer) o) - .getClass().getCanonicalName()); - } - try { - manager.saveNodeViewersFile(selectedFile, - nodeViewersClassNames); - } catch (IOException ex) { - LoggerFactory - .getLogger() - .error( - "Error saving node veiwer configuration from file.", - ex); - JOptionPane.showMessageDialog( - ZooInspectorNodeViewersDialog.this, - "Error saving node veiwer configuration from file: " - + ex.getMessage(), "Error", - JOptionPane.ERROR_MESSAGE); - } - } - } - } - }); - loadFileButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - int result = fileChooser - .showOpenDialog(ZooInspectorNodeViewersDialog.this); - if (result == JFileChooser.APPROVE_OPTION) { - try { - List nodeViewersClassNames = manager - .loadNodeViewersFile(fileChooser - .getSelectedFile()); - List nodeViewers = new ArrayList(); - for (String nodeViewersClassName : nodeViewersClassNames) { - ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class - .forName(nodeViewersClassName) - .newInstance(); - nodeViewers.add(viewer); - } - DefaultListModel model = new DefaultListModel(); - for (ZooInspectorNodeViewer viewer : nodeViewers) { - model.addElement(viewer); - } - viewersList.setModel(model); - panel.revalidate(); - panel.repaint(); - } catch (Exception ex) { - LoggerFactory - .getLogger() - .error( - "Error loading node veiwer configuration from file.", - ex); - JOptionPane.showMessageDialog( - ZooInspectorNodeViewersDialog.this, - "Error loading node veiwer configuration from file: " - + ex.getMessage(), "Error", - JOptionPane.ERROR_MESSAGE); - } - } - } - }); - setDefaultsButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - int answer = JOptionPane - .showConfirmDialog( - ZooInspectorNodeViewersDialog.this, - "Are you sure you want to save this configuration as the default?", - "Confirm Set Defaults", - JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE); - if (answer == JOptionPane.YES_OPTION) { - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - List nodeViewersClassNames = new ArrayList(); - Object[] modelContents = listModel.toArray(); - for (Object o : modelContents) { - nodeViewersClassNames.add(((ZooInspectorNodeViewer) o) - .getClass().getCanonicalName()); - } - try { - manager - .setDefaultNodeViewerConfiguration(nodeViewersClassNames); - } catch (IOException ex) { - LoggerFactory - .getLogger() - .error( - "Error setting default node veiwer configuration.", - ex); - JOptionPane.showMessageDialog( - ZooInspectorNodeViewersDialog.this, - "Error setting default node veiwer configuration: " - + ex.getMessage(), "Error", - JOptionPane.ERROR_MESSAGE); - } - } - } - }); - - JPanel buttonsPanel = new JPanel(); - buttonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); - JButton okButton = new JButton("OK"); - okButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ZooInspectorNodeViewersDialog.this.dispose(); - DefaultListModel listModel = (DefaultListModel) viewersList - .getModel(); - newViewers.clear(); - Object[] modelContents = listModel.toArray(); - for (Object o : modelContents) { - newViewers.add((ZooInspectorNodeViewer) o); - } - currentViewers.clear(); - currentViewers.addAll(newViewers); - for (NodeViewersChangeListener listener : listeners) { - listener.nodeViewersChanged(currentViewers); - } - } - }); - buttonsPanel.add(okButton); - JButton cancelButton = new JButton("Cancel"); - cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ZooInspectorNodeViewersDialog.this.dispose(); - } - }); - buttonsPanel.add(cancelButton); - this.add(panel, BorderLayout.CENTER); - this.add(buttonsPanel, BorderLayout.SOUTH); - this.pack(); - } - - /* - * (non-Javadoc) - * - * @see - * javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event - * .ListSelectionEvent) - */ - public void valueChanged(ListSelectionEvent e) { - int index = viewersList.getSelectedIndex(); - if (index == -1) { - removeButton.setEnabled(false); - upButton.setEnabled(false); - downButton.setEnabled(false); - } else { - removeButton.setEnabled(true); - if (index == 0) { - upButton.setEnabled(false); - } else { - upButton.setEnabled(true); - } - if (index == ((DefaultListModel) viewersList.getModel()).getSize()) { - downButton.setEnabled(false); - } else { - downButton.setEnabled(true); - } - } - } -} +/** + * 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.inspector.gui; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListModel; +import javax.swing.DropMode; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.TransferHandler; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.ZooInspectorManager; + +/** + * A {@link JDialog} for configuring which {@link ZooInspectorNodeViewer}s to + * show in the application + */ +public class ZooInspectorNodeViewersDialog extends JDialog implements + ListSelectionListener { + + private final JButton upButton; + private final JButton downButton; + private final JButton removeButton; + private final JButton addButton; + private final JList viewersList; + private final JButton saveFileButton; + private final JButton loadFileButton; + private final JButton setDefaultsButton; + private final JFileChooser fileChooser = new JFileChooser(new File(".")); + + /** + * @param frame + * - the Frame from which the dialog is displayed + * @param currentViewers + * - the {@link ZooInspectorNodeViewer}s to show + * @param listeners + * - the {@link NodeViewersChangeListener}s which need to be + * notified of changes to the node viewers configuration + * @param manager + * - the {@link ZooInspectorManager} for the application + * + */ + public ZooInspectorNodeViewersDialog(Frame frame, + final List currentViewers, + final Collection listeners, + final ZooInspectorManager manager) { + super(frame); + final List newViewers = new ArrayList( + currentViewers); + this.setLayout(new BorderLayout()); + this.setIconImage(ZooInspectorIconResources.getChangeNodeViewersIcon() + .getImage()); + this.setTitle("About ZooInspector"); + this.setModal(true); + this.setAlwaysOnTop(true); + this.setResizable(true); + final JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + viewersList = new JList(); + DefaultListModel model = new DefaultListModel(); + for (ZooInspectorNodeViewer viewer : newViewers) { + model.addElement(viewer); + } + viewersList.setModel(model); + viewersList.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, + Object value, int index, boolean isSelected, + boolean cellHasFocus) { + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) value; + JLabel label = (JLabel) super.getListCellRendererComponent( + list, value, index, isSelected, cellHasFocus); + label.setText(viewer.getTitle()); + return label; + } + }); + viewersList.setDropMode(DropMode.INSERT); + viewersList.enableInputMethods(true); + viewersList.setDragEnabled(true); + viewersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + viewersList.getSelectionModel().addListSelectionListener(this); + viewersList.setTransferHandler(new TransferHandler() { + + @Override + public boolean canImport(TransferHandler.TransferSupport info) { + // we only import NodeViewers + if (!info + .isDataFlavorSupported(ZooInspectorNodeViewer.nodeViewerDataFlavor)) { + return false; + } + + JList.DropLocation dl = (JList.DropLocation) info + .getDropLocation(); + if (dl.getIndex() == -1) { + return false; + } + return true; + } + + @Override + public boolean importData(TransferHandler.TransferSupport info) { + JList.DropLocation dl = (JList.DropLocation) info + .getDropLocation(); + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + int index = dl.getIndex(); + boolean insert = dl.isInsert(); + // Get the string that is being dropped. + Transferable t = info.getTransferable(); + String data; + try { + data = (String) t + .getTransferData(ZooInspectorNodeViewer.nodeViewerDataFlavor); + } catch (Exception e) { + return false; + } + try { + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class + .forName(data).newInstance(); + if (listModel.contains(viewer)) { + listModel.removeElement(viewer); + } + if (insert) { + listModel.add(index, viewer); + } else { + listModel.set(index, viewer); + } + return true; + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error instantiating class: " + data, e); + return false; + } + + } + + @Override + public int getSourceActions(JComponent c) { + return MOVE; + } + + @Override + protected Transferable createTransferable(JComponent c) { + JList list = (JList) c; + ZooInspectorNodeViewer value = (ZooInspectorNodeViewer) list + .getSelectedValue(); + return value; + } + }); + JScrollPane scroller = new JScrollPane(viewersList); + GridBagConstraints c1 = new GridBagConstraints(); + c1.gridx = 0; + c1.gridy = 0; + c1.gridwidth = 3; + c1.gridheight = 3; + c1.weightx = 0; + c1.weighty = 1; + c1.anchor = GridBagConstraints.CENTER; + c1.fill = GridBagConstraints.BOTH; + c1.insets = new Insets(5, 5, 5, 5); + c1.ipadx = 0; + c1.ipady = 0; + panel.add(scroller, c1); + upButton = new JButton(ZooInspectorIconResources.getUpIcon()); + downButton = new JButton(ZooInspectorIconResources.getDownIcon()); + removeButton = new JButton(ZooInspectorIconResources + .getDeleteNodeIcon()); + addButton = new JButton(ZooInspectorIconResources.getAddNodeIcon()); + upButton.setEnabled(false); + downButton.setEnabled(false); + removeButton.setEnabled(false); + addButton.setEnabled(true); + upButton.setToolTipText("Move currently selected node viewer up"); + downButton.setToolTipText("Move currently selected node viewer down"); + removeButton.setToolTipText("Remove currently selected node viewer"); + addButton.setToolTipText("Add node viewer"); + final JTextField newViewerTextField = new JTextField(); + GridBagConstraints c2 = new GridBagConstraints(); + c2.gridx = 3; + c2.gridy = 0; + c2.gridwidth = 1; + c2.gridheight = 1; + c2.weightx = 0; + c2.weighty = 0; + c2.anchor = GridBagConstraints.NORTH; + c2.fill = GridBagConstraints.HORIZONTAL; + c2.insets = new Insets(5, 5, 5, 5); + c2.ipadx = 0; + c2.ipady = 0; + panel.add(upButton, c2); + GridBagConstraints c3 = new GridBagConstraints(); + c3.gridx = 3; + c3.gridy = 2; + c3.gridwidth = 1; + c3.gridheight = 1; + c3.weightx = 0; + c3.weighty = 0; + c3.anchor = GridBagConstraints.NORTH; + c3.fill = GridBagConstraints.HORIZONTAL; + c3.insets = new Insets(5, 5, 5, 5); + c3.ipadx = 0; + c3.ipady = 0; + panel.add(downButton, c3); + GridBagConstraints c4 = new GridBagConstraints(); + c4.gridx = 3; + c4.gridy = 1; + c4.gridwidth = 1; + c4.gridheight = 1; + c4.weightx = 0; + c4.weighty = 0; + c4.anchor = GridBagConstraints.NORTH; + c4.fill = GridBagConstraints.HORIZONTAL; + c4.insets = new Insets(5, 5, 5, 5); + c4.ipadx = 0; + c4.ipady = 0; + panel.add(removeButton, c4); + GridBagConstraints c5 = new GridBagConstraints(); + c5.gridx = 0; + c5.gridy = 3; + c5.gridwidth = 3; + c5.gridheight = 1; + c5.weightx = 0; + c5.weighty = 0; + c5.anchor = GridBagConstraints.CENTER; + c5.fill = GridBagConstraints.BOTH; + c5.insets = new Insets(5, 5, 5, 5); + c5.ipadx = 0; + c5.ipady = 0; + panel.add(newViewerTextField, c5); + GridBagConstraints c6 = new GridBagConstraints(); + c6.gridx = 3; + c6.gridy = 3; + c6.gridwidth = 1; + c6.gridheight = 1; + c6.weightx = 0; + c6.weighty = 0; + c6.anchor = GridBagConstraints.CENTER; + c6.fill = GridBagConstraints.BOTH; + c6.insets = new Insets(5, 5, 5, 5); + c6.ipadx = 0; + c6.ipady = 0; + panel.add(addButton, c6); + upButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList + .getSelectedValue(); + int index = viewersList.getSelectedIndex(); + if (listModel.contains(viewer)) { + listModel.removeElementAt(index); + listModel.insertElementAt(viewer, index - 1); + viewersList.setSelectedValue(viewer, true); + } + } + }); + downButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList + .getSelectedValue(); + int index = viewersList.getSelectedIndex(); + if (listModel.contains(viewer)) { + listModel.removeElementAt(index); + listModel.insertElementAt(viewer, index + 1); + viewersList.setSelectedValue(viewer, true); + } + } + }); + removeButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) viewersList + .getSelectedValue(); + int index = viewersList.getSelectedIndex(); + if (listModel.contains(viewer)) { + listModel.removeElement(viewer); + viewersList + .setSelectedIndex(index == listModel.size() ? index - 1 + : index); + } + } + }); + addButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + String className = newViewerTextField.getText(); + if (className == null || className.length() == 0) { + JOptionPane + .showMessageDialog( + ZooInspectorNodeViewersDialog.this, + "Please enter the full class name for a Node Viewer and click the add button", + "Input Error", JOptionPane.ERROR_MESSAGE); + } else { + try { + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class + .forName(className).newInstance(); + if (listModel.contains(viewer)) { + JOptionPane + .showMessageDialog( + ZooInspectorNodeViewersDialog.this, + "Node viewer already exists. Each node viewer can only be added once.", + "Input Error", + JOptionPane.ERROR_MESSAGE); + } else { + listModel.addElement(viewer); + } + } catch (Exception ex) { + LoggerFactory + .getLogger() + .error( + "An error occurred while instaniating the node viewer. ", + ex); + JOptionPane.showMessageDialog( + ZooInspectorNodeViewersDialog.this, + "An error occurred while instaniating the node viewer: " + + ex.getMessage(), "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + }); + saveFileButton = new JButton("Save"); + loadFileButton = new JButton("Load"); + setDefaultsButton = new JButton("Set As Defaults"); + saveFileButton + .setToolTipText("Save current node viewer configuration to file"); + loadFileButton + .setToolTipText("Load node viewer configuration frm file"); + setDefaultsButton + .setToolTipText("Set current configuration asd defaults"); + GridBagConstraints c7 = new GridBagConstraints(); + c7.gridx = 0; + c7.gridy = 4; + c7.gridwidth = 1; + c7.gridheight = 1; + c7.weightx = 1; + c7.weighty = 0; + c7.anchor = GridBagConstraints.WEST; + c7.fill = GridBagConstraints.VERTICAL; + c7.insets = new Insets(5, 5, 5, 5); + c7.ipadx = 0; + c7.ipady = 0; + panel.add(saveFileButton, c7); + GridBagConstraints c8 = new GridBagConstraints(); + c8.gridx = 1; + c8.gridy = 4; + c8.gridwidth = 1; + c8.gridheight = 1; + c8.weightx = 0; + c8.weighty = 0; + c8.anchor = GridBagConstraints.WEST; + c8.fill = GridBagConstraints.VERTICAL; + c8.insets = new Insets(5, 5, 5, 5); + c8.ipadx = 0; + c8.ipady = 0; + panel.add(loadFileButton, c8); + GridBagConstraints c9 = new GridBagConstraints(); + c9.gridx = 2; + c9.gridy = 4; + c9.gridwidth = 1; + c9.gridheight = 1; + c9.weightx = 0; + c9.weighty = 0; + c9.anchor = GridBagConstraints.WEST; + c9.fill = GridBagConstraints.VERTICAL; + c9.insets = new Insets(5, 5, 5, 5); + c9.ipadx = 0; + c9.ipady = 0; + panel.add(setDefaultsButton, c9); + saveFileButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + int result = fileChooser + .showSaveDialog(ZooInspectorNodeViewersDialog.this); + if (result == JFileChooser.APPROVE_OPTION) { + File selectedFile = fileChooser.getSelectedFile(); + int answer = JOptionPane.YES_OPTION; + if (selectedFile.exists()) { + answer = JOptionPane + .showConfirmDialog( + ZooInspectorNodeViewersDialog.this, + "The specified file already exists. do you want to overwrite it?", + "Confirm Overwrite", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + } + if (answer == JOptionPane.YES_OPTION) { + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + List nodeViewersClassNames = new ArrayList(); + Object[] modelContents = listModel.toArray(); + for (Object o : modelContents) { + nodeViewersClassNames + .add(((ZooInspectorNodeViewer) o) + .getClass().getCanonicalName()); + } + try { + manager.saveNodeViewersFile(selectedFile, + nodeViewersClassNames); + } catch (IOException ex) { + LoggerFactory + .getLogger() + .error( + "Error saving node veiwer configuration from file.", + ex); + JOptionPane.showMessageDialog( + ZooInspectorNodeViewersDialog.this, + "Error saving node veiwer configuration from file: " + + ex.getMessage(), "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + } + }); + loadFileButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + int result = fileChooser + .showOpenDialog(ZooInspectorNodeViewersDialog.this); + if (result == JFileChooser.APPROVE_OPTION) { + try { + List nodeViewersClassNames = manager + .loadNodeViewersFile(fileChooser + .getSelectedFile()); + List nodeViewers = new ArrayList(); + for (String nodeViewersClassName : nodeViewersClassNames) { + ZooInspectorNodeViewer viewer = (ZooInspectorNodeViewer) Class + .forName(nodeViewersClassName) + .newInstance(); + nodeViewers.add(viewer); + } + DefaultListModel model = new DefaultListModel(); + for (ZooInspectorNodeViewer viewer : nodeViewers) { + model.addElement(viewer); + } + viewersList.setModel(model); + panel.revalidate(); + panel.repaint(); + } catch (Exception ex) { + LoggerFactory + .getLogger() + .error( + "Error loading node veiwer configuration from file.", + ex); + JOptionPane.showMessageDialog( + ZooInspectorNodeViewersDialog.this, + "Error loading node veiwer configuration from file: " + + ex.getMessage(), "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + }); + setDefaultsButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + int answer = JOptionPane + .showConfirmDialog( + ZooInspectorNodeViewersDialog.this, + "Are you sure you want to save this configuration as the default?", + "Confirm Set Defaults", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + if (answer == JOptionPane.YES_OPTION) { + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + List nodeViewersClassNames = new ArrayList(); + Object[] modelContents = listModel.toArray(); + for (Object o : modelContents) { + nodeViewersClassNames.add(((ZooInspectorNodeViewer) o) + .getClass().getCanonicalName()); + } + try { + manager + .setDefaultNodeViewerConfiguration(nodeViewersClassNames); + } catch (IOException ex) { + LoggerFactory + .getLogger() + .error( + "Error setting default node veiwer configuration.", + ex); + JOptionPane.showMessageDialog( + ZooInspectorNodeViewersDialog.this, + "Error setting default node veiwer configuration: " + + ex.getMessage(), "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + }); + + JPanel buttonsPanel = new JPanel(); + buttonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); + JButton okButton = new JButton("OK"); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ZooInspectorNodeViewersDialog.this.dispose(); + DefaultListModel listModel = (DefaultListModel) viewersList + .getModel(); + newViewers.clear(); + Object[] modelContents = listModel.toArray(); + for (Object o : modelContents) { + newViewers.add((ZooInspectorNodeViewer) o); + } + currentViewers.clear(); + currentViewers.addAll(newViewers); + for (NodeViewersChangeListener listener : listeners) { + listener.nodeViewersChanged(currentViewers); + } + } + }); + buttonsPanel.add(okButton); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ZooInspectorNodeViewersDialog.this.dispose(); + } + }); + buttonsPanel.add(cancelButton); + this.add(panel, BorderLayout.CENTER); + this.add(buttonsPanel, BorderLayout.SOUTH); + this.pack(); + } + + /* + * (non-Javadoc) + * + * @see + * javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event + * .ListSelectionEvent) + */ + public void valueChanged(ListSelectionEvent e) { + int index = viewersList.getSelectedIndex(); + if (index == -1) { + removeButton.setEnabled(false); + upButton.setEnabled(false); + downButton.setEnabled(false); + } else { + removeButton.setEnabled(true); + if (index == 0) { + upButton.setEnabled(false); + } else { + upButton.setEnabled(true); + } + if (index == ((DefaultListModel) viewersList.getModel()).getSize()) { + downButton.setEnabled(false); + } else { + downButton.setEnabled(true); + } + } + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java index 2dd763ac971..05c256b5a85 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java @@ -1,140 +1,140 @@ -/** - * 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.inspector.gui; - -import java.awt.BorderLayout; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JPanel; -import javax.swing.JTabbedPane; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.TreePath; - -import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * This is the {@link JPanel} which contains the {@link ZooInspectorNodeViewer}s - */ -public class ZooInspectorNodeViewersPanel extends JPanel implements - TreeSelectionListener, ChangeListener { - - private final List nodeVeiwers = new ArrayList(); - private final List needsReload = new ArrayList(); - private final JTabbedPane tabbedPane; - private final List selectedNodes = new ArrayList(); - private final ZooInspectorNodeManager zooInspectorManager; - - /** - * @param zooInspectorManager - * - the {@link ZooInspectorManager} for the application - * @param nodeVeiwers - * - the {@link ZooInspectorNodeViewer}s to show - */ - public ZooInspectorNodeViewersPanel( - ZooInspectorNodeManager zooInspectorManager, - List nodeVeiwers) { - this.zooInspectorManager = zooInspectorManager; - this.setLayout(new BorderLayout()); - tabbedPane = new JTabbedPane(JTabbedPane.TOP, - JTabbedPane.WRAP_TAB_LAYOUT); - setNodeViewers(nodeVeiwers); - tabbedPane.addChangeListener(this); - this.add(tabbedPane, BorderLayout.CENTER); - reloadSelectedViewer(); - } - - /** - * @param nodeViewers - * - the {@link ZooInspectorNodeViewer}s to show - */ - public void setNodeViewers(List nodeViewers) { - this.nodeVeiwers.clear(); - this.nodeVeiwers.addAll(nodeViewers); - needsReload.clear(); - tabbedPane.removeAll(); - for (ZooInspectorNodeViewer nodeViewer : nodeVeiwers) { - nodeViewer.setZooInspectorManager(zooInspectorManager); - needsReload.add(true); - tabbedPane.add(nodeViewer.getTitle(), nodeViewer); - } - this.revalidate(); - this.repaint(); - } - - private void reloadSelectedViewer() { - int index = this.tabbedPane.getSelectedIndex(); - if (index != -1 && this.needsReload.get(index)) { - ZooInspectorNodeViewer viewer = this.nodeVeiwers.get(index); - viewer.nodeSelectionChanged(selectedNodes); - this.needsReload.set(index, false); - } - } - - /* - * (non-Javadoc) - * - * @see - * javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event - * .TreeSelectionEvent) - */ - public void valueChanged(TreeSelectionEvent e) { - TreePath[] paths = e.getPaths(); - selectedNodes.clear(); - for (TreePath path : paths) { - boolean appended = false; - StringBuilder sb = new StringBuilder(); - Object[] pathArray = path.getPath(); - for (Object o : pathArray) { - if (o != null) { - String nodeName = o.toString(); - if (nodeName != null) { - if (nodeName.length() > 0) { - appended = true; - sb.append("/"); //$NON-NLS-1$ - sb.append(o.toString()); - } - } - } - } - if (appended) { - selectedNodes.add(sb.toString()); - } - } - for (int i = 0; i < needsReload.size(); i++) { - this.needsReload.set(i, true); - } - reloadSelectedViewer(); - } - - /* - * (non-Javadoc) - * - * @see - * javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent - * ) - */ - public void stateChanged(ChangeEvent e) { - reloadSelectedViewer(); - } -} +/** + * 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.inspector.gui; + +import java.awt.BorderLayout; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreePath; + +import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; +import org.apache.zookeeper.inspector.manager.ZooInspectorManager; +import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; + +/** + * This is the {@link JPanel} which contains the {@link ZooInspectorNodeViewer}s + */ +public class ZooInspectorNodeViewersPanel extends JPanel implements + TreeSelectionListener, ChangeListener { + + private final List nodeVeiwers = new ArrayList(); + private final List needsReload = new ArrayList(); + private final JTabbedPane tabbedPane; + private final List selectedNodes = new ArrayList(); + private final ZooInspectorNodeManager zooInspectorManager; + + /** + * @param zooInspectorManager + * - the {@link ZooInspectorManager} for the application + * @param nodeVeiwers + * - the {@link ZooInspectorNodeViewer}s to show + */ + public ZooInspectorNodeViewersPanel( + ZooInspectorNodeManager zooInspectorManager, + List nodeVeiwers) { + this.zooInspectorManager = zooInspectorManager; + this.setLayout(new BorderLayout()); + tabbedPane = new JTabbedPane(JTabbedPane.TOP, + JTabbedPane.WRAP_TAB_LAYOUT); + setNodeViewers(nodeVeiwers); + tabbedPane.addChangeListener(this); + this.add(tabbedPane, BorderLayout.CENTER); + reloadSelectedViewer(); + } + + /** + * @param nodeViewers + * - the {@link ZooInspectorNodeViewer}s to show + */ + public void setNodeViewers(List nodeViewers) { + this.nodeVeiwers.clear(); + this.nodeVeiwers.addAll(nodeViewers); + needsReload.clear(); + tabbedPane.removeAll(); + for (ZooInspectorNodeViewer nodeViewer : nodeVeiwers) { + nodeViewer.setZooInspectorManager(zooInspectorManager); + needsReload.add(true); + tabbedPane.add(nodeViewer.getTitle(), nodeViewer); + } + this.revalidate(); + this.repaint(); + } + + private void reloadSelectedViewer() { + int index = this.tabbedPane.getSelectedIndex(); + if (index != -1 && this.needsReload.get(index)) { + ZooInspectorNodeViewer viewer = this.nodeVeiwers.get(index); + viewer.nodeSelectionChanged(selectedNodes); + this.needsReload.set(index, false); + } + } + + /* + * (non-Javadoc) + * + * @see + * javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event + * .TreeSelectionEvent) + */ + public void valueChanged(TreeSelectionEvent e) { + TreePath[] paths = e.getPaths(); + selectedNodes.clear(); + for (TreePath path : paths) { + boolean appended = false; + StringBuilder sb = new StringBuilder(); + Object[] pathArray = path.getPath(); + for (Object o : pathArray) { + if (o != null) { + String nodeName = o.toString(); + if (nodeName != null) { + if (nodeName.length() > 0) { + appended = true; + sb.append("/"); //$NON-NLS-1$ + sb.append(o.toString()); + } + } + } + } + if (appended) { + selectedNodes.add(sb.toString()); + } + } + for (int i = 0; i < needsReload.size(); i++) { + this.needsReload.set(i, true); + } + reloadSelectedViewer(); + } + + /* + * (non-Javadoc) + * + * @see + * javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent + * ) + */ + public void stateChanged(ChangeEvent e) { + reloadSelectedViewer(); + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java index c2d0fac6247..7a4efafa465 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java @@ -1,361 +1,361 @@ -/** - * 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.inspector.gui; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.ExecutionException; - -import javax.swing.JButton; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JToolBar; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; - -/** - * The parent {@link JPanel} for the whole application - */ -public class ZooInspectorPanel extends JPanel implements - NodeViewersChangeListener { - private final JButton refreshButton; - private final JButton disconnectButton; - private final JButton connectButton; - private final ZooInspectorNodeViewersPanel nodeViewersPanel; - private final ZooInspectorTreeViewer treeViewer; - private final ZooInspectorManager zooInspectorManager; - private final JButton addNodeButton; - private final JButton deleteNodeButton; - private final JButton nodeViewersButton; - private final JButton aboutButton; - private final List listeners = new ArrayList(); - { - listeners.add(this); - } - - /** - * @param zooInspectorManager - * - the {@link ZooInspectorManager} for the application - */ - public ZooInspectorPanel(final ZooInspectorManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - final ArrayList nodeViewers = new ArrayList(); - try { - List defaultNodeViewersClassNames = this.zooInspectorManager - .getDefaultNodeViewerConfiguration(); - for (String className : defaultNodeViewersClassNames) { - nodeViewers.add((ZooInspectorNodeViewer) Class.forName( - className).newInstance()); - } - } catch (Exception ex) { - LoggerFactory.getLogger().error( - "Error loading default node viewers.", ex); - JOptionPane.showMessageDialog(ZooInspectorPanel.this, - "Error loading default node viewers: " + ex.getMessage(), - "Error", JOptionPane.ERROR_MESSAGE); - } - nodeViewersPanel = new ZooInspectorNodeViewersPanel( - zooInspectorManager, nodeViewers); - treeViewer = new ZooInspectorTreeViewer(zooInspectorManager, - nodeViewersPanel); - this.setLayout(new BorderLayout()); - JToolBar toolbar = new JToolBar(); - toolbar.setFloatable(false); - connectButton = new JButton(ZooInspectorIconResources.getConnectIcon()); - disconnectButton = new JButton(ZooInspectorIconResources - .getDisconnectIcon()); - refreshButton = new JButton(ZooInspectorIconResources.getRefreshIcon()); - addNodeButton = new JButton(ZooInspectorIconResources.getAddNodeIcon()); - deleteNodeButton = new JButton(ZooInspectorIconResources - .getDeleteNodeIcon()); - nodeViewersButton = new JButton(ZooInspectorIconResources - .getChangeNodeViewersIcon()); - aboutButton = new JButton(ZooInspectorIconResources - .getInformationIcon()); - toolbar.add(connectButton); - toolbar.add(disconnectButton); - toolbar.add(refreshButton); - toolbar.add(addNodeButton); - toolbar.add(deleteNodeButton); - toolbar.add(nodeViewersButton); - toolbar.add(aboutButton); - aboutButton.setEnabled(true); - connectButton.setEnabled(true); - disconnectButton.setEnabled(false); - refreshButton.setEnabled(false); - addNodeButton.setEnabled(false); - deleteNodeButton.setEnabled(false); - nodeViewersButton.setEnabled(true); - nodeViewersButton.setToolTipText("Change Node Viewers"); - aboutButton.setToolTipText("About ZooInspector"); - connectButton.setToolTipText("Connect"); - disconnectButton.setToolTipText("Disconnect"); - refreshButton.setToolTipText("Refresh"); - addNodeButton.setToolTipText("Add Node"); - deleteNodeButton.setToolTipText("Delete Node"); - connectButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ZooInspectorConnectionPropertiesDialog zicpd = new ZooInspectorConnectionPropertiesDialog( - zooInspectorManager.getLastConnectionProps(), - zooInspectorManager.getConnectionPropertiesTemplate(), - ZooInspectorPanel.this); - zicpd.setVisible(true); - } - }); - disconnectButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - disconnect(); - } - }); - refreshButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - treeViewer.refreshView(); - } - }); - addNodeButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - final List selectedNodes = treeViewer - .getSelectedNodes(); - if (selectedNodes.size() == 1) { - final String nodeName = JOptionPane.showInputDialog( - ZooInspectorPanel.this, - "Please Enter a name for the new node", - "Create Node", JOptionPane.INFORMATION_MESSAGE); - if (nodeName != null && nodeName.length() > 0) { - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - return ZooInspectorPanel.this.zooInspectorManager - .createNode(selectedNodes.get(0), - nodeName); - } - - @Override - protected void done() { - treeViewer.refreshView(); - } - }; - worker.execute(); - } - } else { - JOptionPane.showMessageDialog(ZooInspectorPanel.this, - "Please select 1 parent node for the new node."); - } - } - }); - deleteNodeButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - final List selectedNodes = treeViewer - .getSelectedNodes(); - if (selectedNodes.size() == 0) { - JOptionPane.showMessageDialog(ZooInspectorPanel.this, - "Please select at least 1 node to be deleted"); - } else { - int answer = JOptionPane.showConfirmDialog( - ZooInspectorPanel.this, - "Are you sure you want to delete the selected nodes?" - + "(This action cannot be reverted)", - "Confirm Delete", JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE); - if (answer == JOptionPane.YES_OPTION) { - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - for (String nodePath : selectedNodes) { - ZooInspectorPanel.this.zooInspectorManager - .deleteNode(nodePath); - } - return true; - } - - @Override - protected void done() { - treeViewer.refreshView(); - } - }; - worker.execute(); - } - } - } - }); - nodeViewersButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - ZooInspectorNodeViewersDialog nvd = new ZooInspectorNodeViewersDialog( - JOptionPane.getRootFrame(), nodeViewers, listeners, - zooInspectorManager); - nvd.setVisible(true); - } - }); - aboutButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ZooInspectorAboutDialog zicpd = new ZooInspectorAboutDialog( - JOptionPane.getRootFrame()); - zicpd.setVisible(true); - } - }); - JScrollPane treeScroller = new JScrollPane(treeViewer); - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, - treeScroller, nodeViewersPanel); - splitPane.setResizeWeight(0.25); - this.add(splitPane, BorderLayout.CENTER); - this.add(toolbar, BorderLayout.NORTH); - } - - /** - * @param connectionProps - * the {@link Properties} for connecting to the zookeeper - * instance - */ - public void connect(final Properties connectionProps) { - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - zooInspectorManager.setLastConnectionProps(connectionProps); - return zooInspectorManager.connect(connectionProps); - } - - @Override - protected void done() { - try { - if (get()) { - treeViewer.refreshView(); - connectButton.setEnabled(false); - disconnectButton.setEnabled(true); - refreshButton.setEnabled(true); - addNodeButton.setEnabled(true); - deleteNodeButton.setEnabled(true); - } else { - JOptionPane.showMessageDialog(ZooInspectorPanel.this, - "Unable to connect to zookeeper", "Error", - JOptionPane.ERROR_MESSAGE); - } - } catch (InterruptedException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while connecting to ZooKeeper server", - e); - } catch (ExecutionException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while connecting to ZooKeeper server", - e); - } - } - - }; - worker.execute(); - } - - /** - * - */ - public void disconnect() { - disconnect(false); - } - - /** - * @param wait - * - set this to true if the method should only return once the - * application has successfully disconnected - */ - public void disconnect(boolean wait) { - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - return ZooInspectorPanel.this.zooInspectorManager.disconnect(); - } - - @Override - protected void done() { - try { - if (get()) { - treeViewer.clearView(); - connectButton.setEnabled(true); - disconnectButton.setEnabled(false); - refreshButton.setEnabled(false); - addNodeButton.setEnabled(false); - deleteNodeButton.setEnabled(false); - } - } catch (InterruptedException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while disconnecting from ZooKeeper server", - e); - } catch (ExecutionException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while disconnecting from ZooKeeper server", - e); - } - } - - }; - worker.execute(); - if (wait) { - while (!worker.isDone()) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while disconnecting from ZooKeeper server", - e); - } - } - } - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.gui.NodeViewersChangeListener# - * nodeViewersChanged(java.util.List) - */ - public void nodeViewersChanged(List newViewers) { - this.nodeViewersPanel.setNodeViewers(newViewers); - } - - /** - * @param connectionProps - * @throws IOException - */ - public void setdefaultConnectionProps(Properties connectionProps) - throws IOException { - this.zooInspectorManager.saveDefaultConnectionFile(connectionProps); - } -} +/** + * 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.inspector.gui; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.ExecutionException; + +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JToolBar; +import javax.swing.SwingWorker; + +import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.ZooInspectorManager; + +/** + * The parent {@link JPanel} for the whole application + */ +public class ZooInspectorPanel extends JPanel implements + NodeViewersChangeListener { + private final JButton refreshButton; + private final JButton disconnectButton; + private final JButton connectButton; + private final ZooInspectorNodeViewersPanel nodeViewersPanel; + private final ZooInspectorTreeViewer treeViewer; + private final ZooInspectorManager zooInspectorManager; + private final JButton addNodeButton; + private final JButton deleteNodeButton; + private final JButton nodeViewersButton; + private final JButton aboutButton; + private final List listeners = new ArrayList(); + { + listeners.add(this); + } + + /** + * @param zooInspectorManager + * - the {@link ZooInspectorManager} for the application + */ + public ZooInspectorPanel(final ZooInspectorManager zooInspectorManager) { + this.zooInspectorManager = zooInspectorManager; + final ArrayList nodeViewers = new ArrayList(); + try { + List defaultNodeViewersClassNames = this.zooInspectorManager + .getDefaultNodeViewerConfiguration(); + for (String className : defaultNodeViewersClassNames) { + nodeViewers.add((ZooInspectorNodeViewer) Class.forName( + className).newInstance()); + } + } catch (Exception ex) { + LoggerFactory.getLogger().error( + "Error loading default node viewers.", ex); + JOptionPane.showMessageDialog(ZooInspectorPanel.this, + "Error loading default node viewers: " + ex.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + } + nodeViewersPanel = new ZooInspectorNodeViewersPanel( + zooInspectorManager, nodeViewers); + treeViewer = new ZooInspectorTreeViewer(zooInspectorManager, + nodeViewersPanel); + this.setLayout(new BorderLayout()); + JToolBar toolbar = new JToolBar(); + toolbar.setFloatable(false); + connectButton = new JButton(ZooInspectorIconResources.getConnectIcon()); + disconnectButton = new JButton(ZooInspectorIconResources + .getDisconnectIcon()); + refreshButton = new JButton(ZooInspectorIconResources.getRefreshIcon()); + addNodeButton = new JButton(ZooInspectorIconResources.getAddNodeIcon()); + deleteNodeButton = new JButton(ZooInspectorIconResources + .getDeleteNodeIcon()); + nodeViewersButton = new JButton(ZooInspectorIconResources + .getChangeNodeViewersIcon()); + aboutButton = new JButton(ZooInspectorIconResources + .getInformationIcon()); + toolbar.add(connectButton); + toolbar.add(disconnectButton); + toolbar.add(refreshButton); + toolbar.add(addNodeButton); + toolbar.add(deleteNodeButton); + toolbar.add(nodeViewersButton); + toolbar.add(aboutButton); + aboutButton.setEnabled(true); + connectButton.setEnabled(true); + disconnectButton.setEnabled(false); + refreshButton.setEnabled(false); + addNodeButton.setEnabled(false); + deleteNodeButton.setEnabled(false); + nodeViewersButton.setEnabled(true); + nodeViewersButton.setToolTipText("Change Node Viewers"); + aboutButton.setToolTipText("About ZooInspector"); + connectButton.setToolTipText("Connect"); + disconnectButton.setToolTipText("Disconnect"); + refreshButton.setToolTipText("Refresh"); + addNodeButton.setToolTipText("Add Node"); + deleteNodeButton.setToolTipText("Delete Node"); + connectButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ZooInspectorConnectionPropertiesDialog zicpd = new ZooInspectorConnectionPropertiesDialog( + zooInspectorManager.getLastConnectionProps(), + zooInspectorManager.getConnectionPropertiesTemplate(), + ZooInspectorPanel.this); + zicpd.setVisible(true); + } + }); + disconnectButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + disconnect(); + } + }); + refreshButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + treeViewer.refreshView(); + } + }); + addNodeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final List selectedNodes = treeViewer + .getSelectedNodes(); + if (selectedNodes.size() == 1) { + final String nodeName = JOptionPane.showInputDialog( + ZooInspectorPanel.this, + "Please Enter a name for the new node", + "Create Node", JOptionPane.INFORMATION_MESSAGE); + if (nodeName != null && nodeName.length() > 0) { + SwingWorker worker = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + return ZooInspectorPanel.this.zooInspectorManager + .createNode(selectedNodes.get(0), + nodeName); + } + + @Override + protected void done() { + treeViewer.refreshView(); + } + }; + worker.execute(); + } + } else { + JOptionPane.showMessageDialog(ZooInspectorPanel.this, + "Please select 1 parent node for the new node."); + } + } + }); + deleteNodeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final List selectedNodes = treeViewer + .getSelectedNodes(); + if (selectedNodes.size() == 0) { + JOptionPane.showMessageDialog(ZooInspectorPanel.this, + "Please select at least 1 node to be deleted"); + } else { + int answer = JOptionPane.showConfirmDialog( + ZooInspectorPanel.this, + "Are you sure you want to delete the selected nodes?" + + "(This action cannot be reverted)", + "Confirm Delete", JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + if (answer == JOptionPane.YES_OPTION) { + SwingWorker worker = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + for (String nodePath : selectedNodes) { + ZooInspectorPanel.this.zooInspectorManager + .deleteNode(nodePath); + } + return true; + } + + @Override + protected void done() { + treeViewer.refreshView(); + } + }; + worker.execute(); + } + } + } + }); + nodeViewersButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + ZooInspectorNodeViewersDialog nvd = new ZooInspectorNodeViewersDialog( + JOptionPane.getRootFrame(), nodeViewers, listeners, + zooInspectorManager); + nvd.setVisible(true); + } + }); + aboutButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ZooInspectorAboutDialog zicpd = new ZooInspectorAboutDialog( + JOptionPane.getRootFrame()); + zicpd.setVisible(true); + } + }); + JScrollPane treeScroller = new JScrollPane(treeViewer); + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + treeScroller, nodeViewersPanel); + splitPane.setResizeWeight(0.25); + this.add(splitPane, BorderLayout.CENTER); + this.add(toolbar, BorderLayout.NORTH); + } + + /** + * @param connectionProps + * the {@link Properties} for connecting to the zookeeper + * instance + */ + public void connect(final Properties connectionProps) { + SwingWorker worker = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + zooInspectorManager.setLastConnectionProps(connectionProps); + return zooInspectorManager.connect(connectionProps); + } + + @Override + protected void done() { + try { + if (get()) { + treeViewer.refreshView(); + connectButton.setEnabled(false); + disconnectButton.setEnabled(true); + refreshButton.setEnabled(true); + addNodeButton.setEnabled(true); + deleteNodeButton.setEnabled(true); + } else { + JOptionPane.showMessageDialog(ZooInspectorPanel.this, + "Unable to connect to zookeeper", "Error", + JOptionPane.ERROR_MESSAGE); + } + } catch (InterruptedException e) { + LoggerFactory + .getLogger() + .error( + "Error occurred while connecting to ZooKeeper server", + e); + } catch (ExecutionException e) { + LoggerFactory + .getLogger() + .error( + "Error occurred while connecting to ZooKeeper server", + e); + } + } + + }; + worker.execute(); + } + + /** + * + */ + public void disconnect() { + disconnect(false); + } + + /** + * @param wait + * - set this to true if the method should only return once the + * application has successfully disconnected + */ + public void disconnect(boolean wait) { + SwingWorker worker = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + return ZooInspectorPanel.this.zooInspectorManager.disconnect(); + } + + @Override + protected void done() { + try { + if (get()) { + treeViewer.clearView(); + connectButton.setEnabled(true); + disconnectButton.setEnabled(false); + refreshButton.setEnabled(false); + addNodeButton.setEnabled(false); + deleteNodeButton.setEnabled(false); + } + } catch (InterruptedException e) { + LoggerFactory + .getLogger() + .error( + "Error occurred while disconnecting from ZooKeeper server", + e); + } catch (ExecutionException e) { + LoggerFactory + .getLogger() + .error( + "Error occurred while disconnecting from ZooKeeper server", + e); + } + } + + }; + worker.execute(); + if (wait) { + while (!worker.isDone()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + LoggerFactory + .getLogger() + .error( + "Error occurred while disconnecting from ZooKeeper server", + e); + } + } + } + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.gui.NodeViewersChangeListener# + * nodeViewersChanged(java.util.List) + */ + public void nodeViewersChanged(List newViewers) { + this.nodeViewersPanel.setNodeViewers(newViewers); + } + + /** + * @param connectionProps + * @throws IOException + */ + public void setdefaultConnectionProps(Properties connectionProps) + throws IOException { + this.zooInspectorManager.saveDefaultConnectionFile(connectionProps); + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java index b49be2d43f7..128b358ebbb 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java @@ -1,362 +1,362 @@ -/** - * 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.inspector.gui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JTree; -import javax.swing.SwingWorker; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import org.apache.zookeeper.inspector.manager.NodeListener; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; - -import com.nitido.utils.toaster.Toaster; - -/** - * A {@link JPanel} for showing the tree view of all the nodes in the zookeeper - * instance - */ -public class ZooInspectorTreeViewer extends JPanel implements NodeListener { - private final ZooInspectorManager zooInspectorManager; - private final JTree tree; - private final Toaster toasterManager; - - /** - * @param zooInspectorManager - * - the {@link ZooInspectorManager} for the application - * @param listener - * - the {@link TreeSelectionListener} to listen for changes in - * the selected node on the node tree - */ - public ZooInspectorTreeViewer( - final ZooInspectorManager zooInspectorManager, - TreeSelectionListener listener) { - this.zooInspectorManager = zooInspectorManager; - this.setLayout(new BorderLayout()); - final JPopupMenu popupMenu = new JPopupMenu(); - final JMenuItem addNotify = new JMenuItem("Add Change Notification"); - this.toasterManager = new Toaster(); - this.toasterManager.setBorderColor(Color.BLACK); - this.toasterManager.setMessageColor(Color.BLACK); - this.toasterManager.setToasterColor(Color.WHITE); - addNotify.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - List selectedNodes = getSelectedNodes(); - zooInspectorManager.addWatchers(selectedNodes, - ZooInspectorTreeViewer.this); - } - }); - final JMenuItem removeNotify = new JMenuItem( - "Remove Change Notification"); - removeNotify.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - List selectedNodes = getSelectedNodes(); - zooInspectorManager.removeWatchers(selectedNodes); - } - }); - tree = new JTree(new DefaultMutableTreeNode()); - tree.setCellRenderer(new ZooInspectorTreeCellRenderer()); - tree.setEditable(false); - tree.getSelectionModel().addTreeSelectionListener(listener); - tree.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3) { - // TODO only show add if a selected node isn't being - // watched, and only show remove if a selected node is being - // watched - popupMenu.removeAll(); - popupMenu.add(addNotify); - popupMenu.add(removeNotify); - popupMenu.show(ZooInspectorTreeViewer.this, e.getX(), e - .getY()); - } - } - }); - this.add(tree, BorderLayout.CENTER); - } - - /** - * Refresh the tree view - */ - public void refreshView() { - final Set expandedNodes = new LinkedHashSet(); - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - expandedNodes.add(path); - } - } - final TreePath[] selectedNodes = tree.getSelectionPaths(); - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - tree.setModel(new DefaultTreeModel(new ZooInspectorTreeNode( - "/", null))); - return true; - } - - @Override - protected void done() { - for (TreePath path : expandedNodes) { - tree.expandPath(path); - } - tree.getSelectionModel().setSelectionPaths(selectedNodes); - } - }; - worker.execute(); - } - - /** - * clear the tree view of all nodes - */ - public void clearView() { - tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); - } - - /** - * @author Colin - * - */ - private static class ZooInspectorTreeCellRenderer extends - DefaultTreeCellRenderer { - public ZooInspectorTreeCellRenderer() { - setLeafIcon(ZooInspectorIconResources.getTreeLeafIcon()); - setOpenIcon(ZooInspectorIconResources.getTreeOpenIcon()); - setClosedIcon(ZooInspectorIconResources.getTreeClosedIcon()); - } - } - - /** - * @author Colin - * - */ - private class ZooInspectorTreeNode implements TreeNode { - private final String nodePath; - private final String nodeName; - private final ZooInspectorTreeNode parent; - - public ZooInspectorTreeNode(String nodePath, ZooInspectorTreeNode parent) { - this.parent = parent; - this.nodePath = nodePath; - int index = nodePath.lastIndexOf("/"); - if (index == -1) { - throw new IllegalArgumentException("Invalid node path" - + nodePath); - } - this.nodeName = nodePath.substring(index + 1); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#children() - */ - public Enumeration children() { - List children = zooInspectorManager - .getChildren(this.nodePath); - Collections.sort(children); - List returnChildren = new ArrayList(); - for (String child : children) { - returnChildren.add(new ZooInspectorTreeNode((this.nodePath - .equals("/") ? "" : this.nodePath) - + "/" + child, this)); - } - return Collections.enumeration(returnChildren); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getAllowsChildren() - */ - public boolean getAllowsChildren() { - return zooInspectorManager.isAllowsChildren(this.nodePath); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getChildAt(int) - */ - public TreeNode getChildAt(int childIndex) { - String child = zooInspectorManager.getNodeChild(this.nodePath, - childIndex); - if (child != null) { - return new ZooInspectorTreeNode((this.nodePath.equals("/") ? "" - : this.nodePath) - + "/" + child, this); - } - return null; - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getChildCount() - */ - public int getChildCount() { - return zooInspectorManager.getNumChildren(this.nodePath); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode) - */ - public int getIndex(TreeNode node) { - return zooInspectorManager.getNodeIndex(this.nodePath); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getParent() - */ - public TreeNode getParent() { - return this.parent; - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#isLeaf() - */ - public boolean isLeaf() { - return !zooInspectorManager.hasChildren(this.nodePath); - } - - @Override - public String toString() { - return this.nodeName; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result - + ((nodePath == null) ? 0 : nodePath.hashCode()); - result = prime * result - + ((parent == null) ? 0 : parent.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ZooInspectorTreeNode other = (ZooInspectorTreeNode) obj; - if (!getOuterType().equals(other.getOuterType())) - return false; - if (nodePath == null) { - if (other.nodePath != null) - return false; - } else if (!nodePath.equals(other.nodePath)) - return false; - if (parent == null) { - if (other.parent != null) - return false; - } else if (!parent.equals(other.parent)) - return false; - return true; - } - - private ZooInspectorTreeViewer getOuterType() { - return ZooInspectorTreeViewer.this; - } - - } - - /** - * @return {@link List} of the currently selected nodes - */ - public List getSelectedNodes() { - TreePath[] paths = tree.getSelectionPaths(); - List selectedNodes = new ArrayList(); - if (paths != null) { - for (TreePath path : paths) { - StringBuilder sb = new StringBuilder(); - Object[] pathArray = path.getPath(); - for (Object o : pathArray) { - String nodeName = o.toString(); - if (nodeName.length() > 0) { - sb.append("/"); - sb.append(o.toString()); - } - } - selectedNodes.add(sb.toString()); - } - } - return selectedNodes; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.NodeListener#processEvent(java - * .lang.String, java.lang.String, java.util.Map) - */ - public void processEvent(String nodePath, String eventType, - Map eventInfo) { - StringBuilder sb = new StringBuilder(); - sb.append("Node: "); - sb.append(nodePath); - sb.append("\nEvent: "); - sb.append(eventType); - if (eventInfo != null) { - for (Map.Entry entry : eventInfo.entrySet()) { - sb.append("\n"); - sb.append(entry.getKey()); - sb.append(": "); - sb.append(entry.getValue()); - } - } - this.toasterManager.showToaster(ZooInspectorIconResources - .getInformationIcon(), sb.toString()); - } -} +/** + * 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.inspector.gui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.SwingWorker; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import org.apache.zookeeper.inspector.manager.NodeListener; +import org.apache.zookeeper.inspector.manager.ZooInspectorManager; + +import com.nitido.utils.toaster.Toaster; + +/** + * A {@link JPanel} for showing the tree view of all the nodes in the zookeeper + * instance + */ +public class ZooInspectorTreeViewer extends JPanel implements NodeListener { + private final ZooInspectorManager zooInspectorManager; + private final JTree tree; + private final Toaster toasterManager; + + /** + * @param zooInspectorManager + * - the {@link ZooInspectorManager} for the application + * @param listener + * - the {@link TreeSelectionListener} to listen for changes in + * the selected node on the node tree + */ + public ZooInspectorTreeViewer( + final ZooInspectorManager zooInspectorManager, + TreeSelectionListener listener) { + this.zooInspectorManager = zooInspectorManager; + this.setLayout(new BorderLayout()); + final JPopupMenu popupMenu = new JPopupMenu(); + final JMenuItem addNotify = new JMenuItem("Add Change Notification"); + this.toasterManager = new Toaster(); + this.toasterManager.setBorderColor(Color.BLACK); + this.toasterManager.setMessageColor(Color.BLACK); + this.toasterManager.setToasterColor(Color.WHITE); + addNotify.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + List selectedNodes = getSelectedNodes(); + zooInspectorManager.addWatchers(selectedNodes, + ZooInspectorTreeViewer.this); + } + }); + final JMenuItem removeNotify = new JMenuItem( + "Remove Change Notification"); + removeNotify.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + List selectedNodes = getSelectedNodes(); + zooInspectorManager.removeWatchers(selectedNodes); + } + }); + tree = new JTree(new DefaultMutableTreeNode()); + tree.setCellRenderer(new ZooInspectorTreeCellRenderer()); + tree.setEditable(false); + tree.getSelectionModel().addTreeSelectionListener(listener); + tree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3) { + // TODO only show add if a selected node isn't being + // watched, and only show remove if a selected node is being + // watched + popupMenu.removeAll(); + popupMenu.add(addNotify); + popupMenu.add(removeNotify); + popupMenu.show(ZooInspectorTreeViewer.this, e.getX(), e + .getY()); + } + } + }); + this.add(tree, BorderLayout.CENTER); + } + + /** + * Refresh the tree view + */ + public void refreshView() { + final Set expandedNodes = new LinkedHashSet(); + int rowCount = tree.getRowCount(); + for (int i = 0; i < rowCount; i++) { + TreePath path = tree.getPathForRow(i); + if (tree.isExpanded(path)) { + expandedNodes.add(path); + } + } + final TreePath[] selectedNodes = tree.getSelectionPaths(); + SwingWorker worker = new SwingWorker() { + + @Override + protected Boolean doInBackground() throws Exception { + tree.setModel(new DefaultTreeModel(new ZooInspectorTreeNode( + "/", null))); + return true; + } + + @Override + protected void done() { + for (TreePath path : expandedNodes) { + tree.expandPath(path); + } + tree.getSelectionModel().setSelectionPaths(selectedNodes); + } + }; + worker.execute(); + } + + /** + * clear the tree view of all nodes + */ + public void clearView() { + tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); + } + + /** + * @author Colin + * + */ + private static class ZooInspectorTreeCellRenderer extends + DefaultTreeCellRenderer { + public ZooInspectorTreeCellRenderer() { + setLeafIcon(ZooInspectorIconResources.getTreeLeafIcon()); + setOpenIcon(ZooInspectorIconResources.getTreeOpenIcon()); + setClosedIcon(ZooInspectorIconResources.getTreeClosedIcon()); + } + } + + /** + * @author Colin + * + */ + private class ZooInspectorTreeNode implements TreeNode { + private final String nodePath; + private final String nodeName; + private final ZooInspectorTreeNode parent; + + public ZooInspectorTreeNode(String nodePath, ZooInspectorTreeNode parent) { + this.parent = parent; + this.nodePath = nodePath; + int index = nodePath.lastIndexOf("/"); + if (index == -1) { + throw new IllegalArgumentException("Invalid node path" + + nodePath); + } + this.nodeName = nodePath.substring(index + 1); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#children() + */ + public Enumeration children() { + List children = zooInspectorManager + .getChildren(this.nodePath); + Collections.sort(children); + List returnChildren = new ArrayList(); + for (String child : children) { + returnChildren.add(new ZooInspectorTreeNode((this.nodePath + .equals("/") ? "" : this.nodePath) + + "/" + child, this)); + } + return Collections.enumeration(returnChildren); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#getAllowsChildren() + */ + public boolean getAllowsChildren() { + return zooInspectorManager.isAllowsChildren(this.nodePath); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#getChildAt(int) + */ + public TreeNode getChildAt(int childIndex) { + String child = zooInspectorManager.getNodeChild(this.nodePath, + childIndex); + if (child != null) { + return new ZooInspectorTreeNode((this.nodePath.equals("/") ? "" + : this.nodePath) + + "/" + child, this); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#getChildCount() + */ + public int getChildCount() { + return zooInspectorManager.getNumChildren(this.nodePath); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode) + */ + public int getIndex(TreeNode node) { + return zooInspectorManager.getNodeIndex(this.nodePath); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#getParent() + */ + public TreeNode getParent() { + return this.parent; + } + + /* + * (non-Javadoc) + * + * @see javax.swing.tree.TreeNode#isLeaf() + */ + public boolean isLeaf() { + return !zooInspectorManager.hasChildren(this.nodePath); + } + + @Override + public String toString() { + return this.nodeName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + getOuterType().hashCode(); + result = prime * result + + ((nodePath == null) ? 0 : nodePath.hashCode()); + result = prime * result + + ((parent == null) ? 0 : parent.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ZooInspectorTreeNode other = (ZooInspectorTreeNode) obj; + if (!getOuterType().equals(other.getOuterType())) + return false; + if (nodePath == null) { + if (other.nodePath != null) + return false; + } else if (!nodePath.equals(other.nodePath)) + return false; + if (parent == null) { + if (other.parent != null) + return false; + } else if (!parent.equals(other.parent)) + return false; + return true; + } + + private ZooInspectorTreeViewer getOuterType() { + return ZooInspectorTreeViewer.this; + } + + } + + /** + * @return {@link List} of the currently selected nodes + */ + public List getSelectedNodes() { + TreePath[] paths = tree.getSelectionPaths(); + List selectedNodes = new ArrayList(); + if (paths != null) { + for (TreePath path : paths) { + StringBuilder sb = new StringBuilder(); + Object[] pathArray = path.getPath(); + for (Object o : pathArray) { + String nodeName = o.toString(); + if (nodeName.length() > 0) { + sb.append("/"); + sb.append(o.toString()); + } + } + selectedNodes.add(sb.toString()); + } + } + return selectedNodes; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.NodeListener#processEvent(java + * .lang.String, java.lang.String, java.util.Map) + */ + public void processEvent(String nodePath, String eventType, + Map eventInfo) { + StringBuilder sb = new StringBuilder(); + sb.append("Node: "); + sb.append(nodePath); + sb.append("\nEvent: "); + sb.append(eventType); + if (eventInfo != null) { + for (Map.Entry entry : eventInfo.entrySet()) { + sb.append("\n"); + sb.append(entry.getKey()); + sb.append(": "); + sb.append(entry.getValue()); + } + } + this.toasterManager.showToaster(ZooInspectorIconResources + .getInformationIcon(), sb.toString()); + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html index 2bd9d389fc8..17fb3dca20b 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html @@ -1,21 +1,21 @@ - - - - -ZooInspector v0.1 - - -

    ZooInspector was developed by Colin Goodheart-Smithe and is -available under the Apache Software Licence v2.0.

    -

    The Icons used were sourced from the Eclipse project (http://www.eclipse.org) and licensed -under the Eclipse Public Licence v1.0. [http://www.eclipse.org/org/documents/epl-v10.php] -

    -

    ZooKeeper is available from http://zookeeper.apache.org/ -and is licensed under an Apache Software Licence v2.0

    -

    The ApacheSoftware Licence v2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0

    - - + + + + +ZooInspector v0.1 + + +

    ZooInspector was developed by Colin Goodheart-Smithe and is +available under the Apache Software Licence v2.0.

    +

    The Icons used were sourced from the Eclipse project (http://www.eclipse.org) and licensed +under the Eclipse Public Licence v1.0. [http://www.eclipse.org/org/documents/epl-v10.php] +

    +

    ZooKeeper is available from http://zookeeper.apache.org/ +and is licensed under an Apache Software Licence v2.0

    +

    The ApacheSoftware Licence v2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0

    + + diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java index cc2a4bd2e93..5ac203c28c8 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java @@ -1,187 +1,187 @@ -/** - * 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.inspector.gui.nodeviewer; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import javax.swing.BorderFactory; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A node viewer for displaying the ACLs currently applied to the selected node - */ -public class NodeViewerACL extends ZooInspectorNodeViewer { - private ZooInspectorNodeManager zooInspectorManager; - private final JPanel aclDataPanel; - private String selectedNode; - - /** - * - */ - public NodeViewerACL() { - this.setLayout(new BorderLayout()); - this.aclDataPanel = new JPanel(); - this.aclDataPanel.setBackground(Color.WHITE); - JScrollPane scroller = new JScrollPane(this.aclDataPanel); - this.add(scroller, BorderLayout.CENTER); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * getTitle() - */ - @Override - public String getTitle() { - return "Node ACLs"; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * nodeSelectionChanged(java.util.Set) - */ - @Override - public void nodeSelectionChanged(List selectedNodes) { - this.aclDataPanel.removeAll(); - if (selectedNodes.size() > 0) { - this.selectedNode = selectedNodes.get(0); - SwingWorker>, Void> worker = new SwingWorker>, Void>() { - - @Override - protected List> doInBackground() - throws Exception { - return NodeViewerACL.this.zooInspectorManager - .getACLs(NodeViewerACL.this.selectedNode); - } - - @Override - protected void done() { - List> acls = null; - try { - acls = get(); - } catch (InterruptedException e) { - acls = new ArrayList>(); - LoggerFactory.getLogger().error( - "Error retrieving ACL Information for node: " - + NodeViewerACL.this.selectedNode, e); - } catch (ExecutionException e) { - acls = new ArrayList>(); - LoggerFactory.getLogger().error( - "Error retrieving ACL Information for node: " - + NodeViewerACL.this.selectedNode, e); - } - aclDataPanel.setLayout(new GridBagLayout()); - int j = 0; - for (Map data : acls) { - int rowPos = 2 * j + 1; - JPanel aclPanel = new JPanel(); - aclPanel.setBorder(BorderFactory - .createLineBorder(Color.BLACK)); - aclPanel.setBackground(Color.WHITE); - aclPanel.setLayout(new GridBagLayout()); - int i = 0; - for (Map.Entry entry : data.entrySet()) { - int rowPosACL = 2 * i + 1; - JLabel label = new JLabel(entry.getKey()); - JTextField text = new JTextField(entry.getValue()); - text.setEditable(false); - GridBagConstraints c1 = new GridBagConstraints(); - c1.gridx = 1; - c1.gridy = rowPosACL; - c1.gridwidth = 1; - c1.gridheight = 1; - c1.weightx = 0; - c1.weighty = 0; - c1.anchor = GridBagConstraints.NORTHWEST; - c1.fill = GridBagConstraints.BOTH; - c1.insets = new Insets(5, 5, 5, 5); - c1.ipadx = 0; - c1.ipady = 0; - aclPanel.add(label, c1); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 3; - c2.gridy = rowPosACL; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.NORTHWEST; - c2.fill = GridBagConstraints.BOTH; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - aclPanel.add(text, c2); - i++; - } - GridBagConstraints c = new GridBagConstraints(); - c.gridx = 1; - c.gridy = rowPos; - c.gridwidth = 1; - c.gridheight = 1; - c.weightx = 1; - c.weighty = 1; - c.anchor = GridBagConstraints.NORTHWEST; - c.fill = GridBagConstraints.NONE; - c.insets = new Insets(5, 5, 5, 5); - c.ipadx = 0; - c.ipady = 0; - aclDataPanel.add(aclPanel, c); - } - NodeViewerACL.this.aclDataPanel.revalidate(); - NodeViewerACL.this.aclDataPanel.repaint(); - } - }; - worker.execute(); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * setZooInspectorManager - * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) - */ - @Override - public void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - } - -} +/** + * 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.inspector.gui.nodeviewer; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.SwingWorker; + +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; + +/** + * A node viewer for displaying the ACLs currently applied to the selected node + */ +public class NodeViewerACL extends ZooInspectorNodeViewer { + private ZooInspectorNodeManager zooInspectorManager; + private final JPanel aclDataPanel; + private String selectedNode; + + /** + * + */ + public NodeViewerACL() { + this.setLayout(new BorderLayout()); + this.aclDataPanel = new JPanel(); + this.aclDataPanel.setBackground(Color.WHITE); + JScrollPane scroller = new JScrollPane(this.aclDataPanel); + this.add(scroller, BorderLayout.CENTER); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * getTitle() + */ + @Override + public String getTitle() { + return "Node ACLs"; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * nodeSelectionChanged(java.util.Set) + */ + @Override + public void nodeSelectionChanged(List selectedNodes) { + this.aclDataPanel.removeAll(); + if (selectedNodes.size() > 0) { + this.selectedNode = selectedNodes.get(0); + SwingWorker>, Void> worker = new SwingWorker>, Void>() { + + @Override + protected List> doInBackground() + throws Exception { + return NodeViewerACL.this.zooInspectorManager + .getACLs(NodeViewerACL.this.selectedNode); + } + + @Override + protected void done() { + List> acls = null; + try { + acls = get(); + } catch (InterruptedException e) { + acls = new ArrayList>(); + LoggerFactory.getLogger().error( + "Error retrieving ACL Information for node: " + + NodeViewerACL.this.selectedNode, e); + } catch (ExecutionException e) { + acls = new ArrayList>(); + LoggerFactory.getLogger().error( + "Error retrieving ACL Information for node: " + + NodeViewerACL.this.selectedNode, e); + } + aclDataPanel.setLayout(new GridBagLayout()); + int j = 0; + for (Map data : acls) { + int rowPos = 2 * j + 1; + JPanel aclPanel = new JPanel(); + aclPanel.setBorder(BorderFactory + .createLineBorder(Color.BLACK)); + aclPanel.setBackground(Color.WHITE); + aclPanel.setLayout(new GridBagLayout()); + int i = 0; + for (Map.Entry entry : data.entrySet()) { + int rowPosACL = 2 * i + 1; + JLabel label = new JLabel(entry.getKey()); + JTextField text = new JTextField(entry.getValue()); + text.setEditable(false); + GridBagConstraints c1 = new GridBagConstraints(); + c1.gridx = 1; + c1.gridy = rowPosACL; + c1.gridwidth = 1; + c1.gridheight = 1; + c1.weightx = 0; + c1.weighty = 0; + c1.anchor = GridBagConstraints.NORTHWEST; + c1.fill = GridBagConstraints.BOTH; + c1.insets = new Insets(5, 5, 5, 5); + c1.ipadx = 0; + c1.ipady = 0; + aclPanel.add(label, c1); + GridBagConstraints c2 = new GridBagConstraints(); + c2.gridx = 3; + c2.gridy = rowPosACL; + c2.gridwidth = 1; + c2.gridheight = 1; + c2.weightx = 0; + c2.weighty = 0; + c2.anchor = GridBagConstraints.NORTHWEST; + c2.fill = GridBagConstraints.BOTH; + c2.insets = new Insets(5, 5, 5, 5); + c2.ipadx = 0; + c2.ipady = 0; + aclPanel.add(text, c2); + i++; + } + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 1; + c.gridy = rowPos; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 1; + c.weighty = 1; + c.anchor = GridBagConstraints.NORTHWEST; + c.fill = GridBagConstraints.NONE; + c.insets = new Insets(5, 5, 5, 5); + c.ipadx = 0; + c.ipady = 0; + aclDataPanel.add(aclPanel, c); + } + NodeViewerACL.this.aclDataPanel.revalidate(); + NodeViewerACL.this.aclDataPanel.repaint(); + } + }; + worker.execute(); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * setZooInspectorManager + * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) + */ + @Override + public void setZooInspectorManager( + ZooInspectorNodeManager zooInspectorManager) { + this.zooInspectorManager = zooInspectorManager; + } + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java index 9dd0f38f3b6..001cb7b21a9 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java @@ -1,146 +1,146 @@ -/** - * 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.inspector.gui.nodeviewer; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.concurrent.ExecutionException; - -import javax.swing.JButton; -import javax.swing.JOptionPane; -import javax.swing.JScrollPane; -import javax.swing.JTextPane; -import javax.swing.JToolBar; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.inspector.gui.ZooInspectorIconResources; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A node viewer for displaying the data for the currently selected node - */ -public class NodeViewerData extends ZooInspectorNodeViewer { - private ZooInspectorNodeManager zooInspectorManager; - private final JTextPane dataArea; - private final JToolBar toolbar; - private String selectedNode; - - /** - * - */ - public NodeViewerData() { - this.setLayout(new BorderLayout()); - this.dataArea = new JTextPane(); - this.toolbar = new JToolBar(); - this.toolbar.setFloatable(false); - JScrollPane scroller = new JScrollPane(this.dataArea); - scroller - .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - this.add(scroller, BorderLayout.CENTER); - this.add(this.toolbar, BorderLayout.NORTH); - JButton saveButton = new JButton(ZooInspectorIconResources - .getSaveIcon()); - saveButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - if (selectedNode != null) { - if (JOptionPane.showConfirmDialog(NodeViewerData.this, - "Are you sure you want to save this node?" - + " (this action cannot be reverted)", - "Confirm Save", JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - zooInspectorManager.setData(selectedNode, dataArea - .getText()); - } - } - } - }); - this.toolbar.add(saveButton); - - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * getTitle() - */ - @Override - public String getTitle() { - return "Node Data"; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * nodeSelectionChanged(java.util.Set) - */ - @Override - public void nodeSelectionChanged(List selectedNodes) { - if (selectedNodes.size() > 0) { - this.selectedNode = selectedNodes.get(0); - SwingWorker worker = new SwingWorker() { - - @Override - protected String doInBackground() throws Exception { - return NodeViewerData.this.zooInspectorManager - .getData(NodeViewerData.this.selectedNode); - } - - @Override - protected void done() { - String data = ""; - try { - data = get(); - } catch (InterruptedException e) { - LoggerFactory.getLogger().error( - "Error retrieving data for node: " - + NodeViewerData.this.selectedNode, e); - } catch (ExecutionException e) { - LoggerFactory.getLogger().error( - "Error retrieving data for node: " - + NodeViewerData.this.selectedNode, e); - } - NodeViewerData.this.dataArea.setText(data); - } - }; - worker.execute(); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * setZooInspectorManager - * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) - */ - @Override - public void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - } - -} +/** + * 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.inspector.gui.nodeviewer; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; +import javax.swing.JToolBar; +import javax.swing.SwingWorker; + +import org.apache.zookeeper.inspector.gui.ZooInspectorIconResources; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; + +/** + * A node viewer for displaying the data for the currently selected node + */ +public class NodeViewerData extends ZooInspectorNodeViewer { + private ZooInspectorNodeManager zooInspectorManager; + private final JTextPane dataArea; + private final JToolBar toolbar; + private String selectedNode; + + /** + * + */ + public NodeViewerData() { + this.setLayout(new BorderLayout()); + this.dataArea = new JTextPane(); + this.toolbar = new JToolBar(); + this.toolbar.setFloatable(false); + JScrollPane scroller = new JScrollPane(this.dataArea); + scroller + .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + this.add(scroller, BorderLayout.CENTER); + this.add(this.toolbar, BorderLayout.NORTH); + JButton saveButton = new JButton(ZooInspectorIconResources + .getSaveIcon()); + saveButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (selectedNode != null) { + if (JOptionPane.showConfirmDialog(NodeViewerData.this, + "Are you sure you want to save this node?" + + " (this action cannot be reverted)", + "Confirm Save", JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { + zooInspectorManager.setData(selectedNode, dataArea + .getText()); + } + } + } + }); + this.toolbar.add(saveButton); + + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * getTitle() + */ + @Override + public String getTitle() { + return "Node Data"; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * nodeSelectionChanged(java.util.Set) + */ + @Override + public void nodeSelectionChanged(List selectedNodes) { + if (selectedNodes.size() > 0) { + this.selectedNode = selectedNodes.get(0); + SwingWorker worker = new SwingWorker() { + + @Override + protected String doInBackground() throws Exception { + return NodeViewerData.this.zooInspectorManager + .getData(NodeViewerData.this.selectedNode); + } + + @Override + protected void done() { + String data = ""; + try { + data = get(); + } catch (InterruptedException e) { + LoggerFactory.getLogger().error( + "Error retrieving data for node: " + + NodeViewerData.this.selectedNode, e); + } catch (ExecutionException e) { + LoggerFactory.getLogger().error( + "Error retrieving data for node: " + + NodeViewerData.this.selectedNode, e); + } + NodeViewerData.this.dataArea.setText(data); + } + }; + worker.execute(); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * setZooInspectorManager + * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) + */ + @Override + public void setZooInspectorManager( + ZooInspectorNodeManager zooInspectorManager) { + this.zooInspectorManager = zooInspectorManager; + } + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java index d7e2b43258f..5c2df8d88c5 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java @@ -1,186 +1,186 @@ -/** - * 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.inspector.gui.nodeviewer; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A node viewer for displaying the meta data for the currently selected node. - * The meta data is essentially the information from the {@link Stat} for the - * node - */ -public class NodeViewerMetaData extends ZooInspectorNodeViewer { - private ZooInspectorNodeManager zooInspectorManager; - private final JPanel metaDataPanel; - private String selectedNode; - - /** - * - */ - public NodeViewerMetaData() { - this.setLayout(new BorderLayout()); - this.metaDataPanel = new JPanel(); - this.metaDataPanel.setBackground(Color.WHITE); - JScrollPane scroller = new JScrollPane(this.metaDataPanel); - this.add(scroller, BorderLayout.CENTER); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * getTitle() - */ - @Override - public String getTitle() { - return "Node Metadata"; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * nodeSelectionChanged(java.util.Set) - */ - @Override - public void nodeSelectionChanged(List selectedNodes) { - this.metaDataPanel.removeAll(); - if (selectedNodes.size() > 0) { - this.selectedNode = selectedNodes.get(0); - SwingWorker, Void> worker = new SwingWorker, Void>() { - - @Override - protected Map doInBackground() throws Exception { - return NodeViewerMetaData.this.zooInspectorManager - .getNodeMeta(NodeViewerMetaData.this.selectedNode); - } - - @Override - protected void done() { - Map data = null; - try { - data = get(); - } catch (InterruptedException e) { - data = new HashMap(); - LoggerFactory.getLogger().error( - "Error retrieving meta data for node: " - + NodeViewerMetaData.this.selectedNode, - e); - } catch (ExecutionException e) { - data = new HashMap(); - LoggerFactory.getLogger().error( - "Error retrieving meta data for node: " - + NodeViewerMetaData.this.selectedNode, - e); - } - NodeViewerMetaData.this.metaDataPanel - .setLayout(new GridBagLayout()); - JPanel infoPanel = new JPanel(); - infoPanel.setBackground(Color.WHITE); - infoPanel.setLayout(new GridBagLayout()); - int i = 0; - int rowPos = 0; - for (Map.Entry entry : data.entrySet()) { - rowPos = 2 * i + 1; - JLabel label = new JLabel(entry.getKey()); - JTextField text = new JTextField(entry.getValue()); - text.setEditable(false); - GridBagConstraints c1 = new GridBagConstraints(); - c1.gridx = 0; - c1.gridy = rowPos; - c1.gridwidth = 1; - c1.gridheight = 1; - c1.weightx = 0; - c1.weighty = 0; - c1.anchor = GridBagConstraints.WEST; - c1.fill = GridBagConstraints.HORIZONTAL; - c1.insets = new Insets(5, 5, 5, 5); - c1.ipadx = 0; - c1.ipady = 0; - infoPanel.add(label, c1); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 2; - c2.gridy = rowPos; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.WEST; - c2.fill = GridBagConstraints.HORIZONTAL; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - infoPanel.add(text, c2); - i++; - } - GridBagConstraints c = new GridBagConstraints(); - c.gridx = 1; - c.gridy = rowPos; - c.gridwidth = 1; - c.gridheight = 1; - c.weightx = 1; - c.weighty = 1; - c.anchor = GridBagConstraints.NORTHWEST; - c.fill = GridBagConstraints.NONE; - c.insets = new Insets(5, 5, 5, 5); - c.ipadx = 0; - c.ipady = 0; - NodeViewerMetaData.this.metaDataPanel.add(infoPanel, c); - NodeViewerMetaData.this.metaDataPanel.revalidate(); - NodeViewerMetaData.this.metaDataPanel.repaint(); - } - }; - worker.execute(); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * setZooInspectorManager - * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) - */ - @Override - public void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - } - -} +/** + * 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.inspector.gui.nodeviewer; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.SwingWorker; + +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; + +/** + * A node viewer for displaying the meta data for the currently selected node. + * The meta data is essentially the information from the {@link Stat} for the + * node + */ +public class NodeViewerMetaData extends ZooInspectorNodeViewer { + private ZooInspectorNodeManager zooInspectorManager; + private final JPanel metaDataPanel; + private String selectedNode; + + /** + * + */ + public NodeViewerMetaData() { + this.setLayout(new BorderLayout()); + this.metaDataPanel = new JPanel(); + this.metaDataPanel.setBackground(Color.WHITE); + JScrollPane scroller = new JScrollPane(this.metaDataPanel); + this.add(scroller, BorderLayout.CENTER); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * getTitle() + */ + @Override + public String getTitle() { + return "Node Metadata"; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * nodeSelectionChanged(java.util.Set) + */ + @Override + public void nodeSelectionChanged(List selectedNodes) { + this.metaDataPanel.removeAll(); + if (selectedNodes.size() > 0) { + this.selectedNode = selectedNodes.get(0); + SwingWorker, Void> worker = new SwingWorker, Void>() { + + @Override + protected Map doInBackground() throws Exception { + return NodeViewerMetaData.this.zooInspectorManager + .getNodeMeta(NodeViewerMetaData.this.selectedNode); + } + + @Override + protected void done() { + Map data = null; + try { + data = get(); + } catch (InterruptedException e) { + data = new HashMap(); + LoggerFactory.getLogger().error( + "Error retrieving meta data for node: " + + NodeViewerMetaData.this.selectedNode, + e); + } catch (ExecutionException e) { + data = new HashMap(); + LoggerFactory.getLogger().error( + "Error retrieving meta data for node: " + + NodeViewerMetaData.this.selectedNode, + e); + } + NodeViewerMetaData.this.metaDataPanel + .setLayout(new GridBagLayout()); + JPanel infoPanel = new JPanel(); + infoPanel.setBackground(Color.WHITE); + infoPanel.setLayout(new GridBagLayout()); + int i = 0; + int rowPos = 0; + for (Map.Entry entry : data.entrySet()) { + rowPos = 2 * i + 1; + JLabel label = new JLabel(entry.getKey()); + JTextField text = new JTextField(entry.getValue()); + text.setEditable(false); + GridBagConstraints c1 = new GridBagConstraints(); + c1.gridx = 0; + c1.gridy = rowPos; + c1.gridwidth = 1; + c1.gridheight = 1; + c1.weightx = 0; + c1.weighty = 0; + c1.anchor = GridBagConstraints.WEST; + c1.fill = GridBagConstraints.HORIZONTAL; + c1.insets = new Insets(5, 5, 5, 5); + c1.ipadx = 0; + c1.ipady = 0; + infoPanel.add(label, c1); + GridBagConstraints c2 = new GridBagConstraints(); + c2.gridx = 2; + c2.gridy = rowPos; + c2.gridwidth = 1; + c2.gridheight = 1; + c2.weightx = 0; + c2.weighty = 0; + c2.anchor = GridBagConstraints.WEST; + c2.fill = GridBagConstraints.HORIZONTAL; + c2.insets = new Insets(5, 5, 5, 5); + c2.ipadx = 0; + c2.ipady = 0; + infoPanel.add(text, c2); + i++; + } + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 1; + c.gridy = rowPos; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 1; + c.weighty = 1; + c.anchor = GridBagConstraints.NORTHWEST; + c.fill = GridBagConstraints.NONE; + c.insets = new Insets(5, 5, 5, 5); + c.ipadx = 0; + c.ipady = 0; + NodeViewerMetaData.this.metaDataPanel.add(infoPanel, c); + NodeViewerMetaData.this.metaDataPanel.revalidate(); + NodeViewerMetaData.this.metaDataPanel.repaint(); + } + }; + worker.execute(); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# + * setZooInspectorManager + * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) + */ + @Override + public void setZooInspectorManager( + ZooInspectorNodeManager zooInspectorManager) { + this.zooInspectorManager = zooInspectorManager; + } + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java index 2185440ec4b..32119a85c30 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java @@ -1,138 +1,138 @@ -/** - * 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.inspector.gui.nodeviewer; - -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.io.IOException; -import java.util.List; - -import javax.swing.JPanel; - -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A {@link JPanel} for displaying information about the currently selected - * node(s) - */ -public abstract class ZooInspectorNodeViewer extends JPanel implements - Transferable { - /** - * The {@link DataFlavor} used for DnD in the node viewer configuration - * dialog - */ - public static final DataFlavor nodeViewerDataFlavor = new DataFlavor( - ZooInspectorNodeViewer.class, "nodeviewer"); - - /** - * @param zooInspectorManager - */ - public abstract void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager); - - /** - * Called whenever the selected nodes in the tree view changes. - * - * @param selectedNodes - * - the nodes currently selected in the tree view - * - */ - public abstract void nodeSelectionChanged(List selectedNodes); - - /** - * @return the title of the node viewer. this will be shown on the tab for - * this node viewer. - */ - public abstract String getTitle(); - - /* - * (non-Javadoc) - * - * @see - * java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer - * .DataFlavor) - */ - public Object getTransferData(DataFlavor flavor) - throws UnsupportedFlavorException, IOException { - if (flavor.equals(nodeViewerDataFlavor)) { - return this.getClass().getCanonicalName(); - } else { - return null; - } - } - - /* - * (non-Javadoc) - * - * @see java.awt.datatransfer.Transferable#getTransferDataFlavors() - */ - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { nodeViewerDataFlavor }; - } - - /* - * (non-Javadoc) - * - * @seejava.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt. - * datatransfer.DataFlavor) - */ - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(nodeViewerDataFlavor); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((getTitle() == null) ? 0 : getTitle().hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ZooInspectorNodeViewer other = (ZooInspectorNodeViewer) obj; - if (getClass().getCanonicalName() != other.getClass() - .getCanonicalName()) { - return false; - } - if (getTitle() == null) { - if (other.getTitle() != null) - return false; - } else if (!getTitle().equals(other.getTitle())) - return false; - return true; - } -} +/** + * 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.inspector.gui.nodeviewer; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; +import java.util.List; + +import javax.swing.JPanel; + +import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; + +/** + * A {@link JPanel} for displaying information about the currently selected + * node(s) + */ +public abstract class ZooInspectorNodeViewer extends JPanel implements + Transferable { + /** + * The {@link DataFlavor} used for DnD in the node viewer configuration + * dialog + */ + public static final DataFlavor nodeViewerDataFlavor = new DataFlavor( + ZooInspectorNodeViewer.class, "nodeviewer"); + + /** + * @param zooInspectorManager + */ + public abstract void setZooInspectorManager( + ZooInspectorNodeManager zooInspectorManager); + + /** + * Called whenever the selected nodes in the tree view changes. + * + * @param selectedNodes + * - the nodes currently selected in the tree view + * + */ + public abstract void nodeSelectionChanged(List selectedNodes); + + /** + * @return the title of the node viewer. this will be shown on the tab for + * this node viewer. + */ + public abstract String getTitle(); + + /* + * (non-Javadoc) + * + * @see + * java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer + * .DataFlavor) + */ + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + if (flavor.equals(nodeViewerDataFlavor)) { + return this.getClass().getCanonicalName(); + } else { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see java.awt.datatransfer.Transferable#getTransferDataFlavors() + */ + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { nodeViewerDataFlavor }; + } + + /* + * (non-Javadoc) + * + * @seejava.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt. + * datatransfer.DataFlavor) + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + return flavor.equals(nodeViewerDataFlavor); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((getTitle() == null) ? 0 : getTitle().hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ZooInspectorNodeViewer other = (ZooInspectorNodeViewer) obj; + if (getClass().getCanonicalName() != other.getClass() + .getCanonicalName()) { + return false; + } + if (getTitle() == null) { + if (other.getTitle() != null) + return false; + } else if (!getTitle().equals(other.getTitle())) + return false; + return true; + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java index a8b5fd9aa60..e4fae41698b 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java @@ -1,36 +1,36 @@ -/** - * 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.inspector.logger; - -/** - * Provides a {@link Logger} for use across the entire application - * - */ -public class LoggerFactory -{ - private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$ - - /** - * @return {@link Logger} for ZooInspector - */ - public static org.slf4j.Logger getLogger() - { - return logger; - } - -} +/** + * 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.inspector.logger; + +/** + * Provides a {@link Logger} for use across the entire application + * + */ +public class LoggerFactory +{ + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$ + + /** + * @return {@link Logger} for ZooInspector + */ + public static org.slf4j.Logger getLogger() + { + return logger; + } + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java index f174f8a29c2..fe55a45b85c 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java @@ -1,37 +1,37 @@ -/** - * 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.inspector.manager; - -import java.util.Map; - -/** - * A Listener for Events on zookeeper nodes - */ -public interface NodeListener { - /** - * @param nodePath - * - the path of the node - * @param eventType - * - the event type - * @param eventInfo - * - a {@link Map} containing any other information about this - * event - */ - public void processEvent(String nodePath, String eventType, - Map eventInfo); -} +/** + * 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.inspector.manager; + +import java.util.Map; + +/** + * A Listener for Events on zookeeper nodes + */ +public interface NodeListener { + /** + * @param nodePath + * - the path of the node + * @param eventType + * - the event type + * @param eventInfo + * - a {@link Map} containing any other information about this + * event + */ + public void processEvent(String nodePath, String eventType, + Map eventInfo); +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java index 9ebbd952041..b72950c9567 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java @@ -1,120 +1,120 @@ -/** - * 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.inspector.manager; - -/** - * A utility class for storing a pair of objects - * - * @param - * @param - */ -public class Pair { - private K key; - private V value; - - /** - * @param key - * @param value - */ - public Pair(K key, V value) { - this.key = key; - this.value = value; - } - - /** - * - */ - public Pair() { - // Do Nothing - } - - /** - * @return key - */ - public K getKey() { - return key; - } - - /** - * @param key - */ - public void setKey(K key) { - this.key = key; - } - - /** - * @return value - */ - public V getValue() { - return value; - } - - /** - * @param value - */ - public void setValue(V value) { - this.value = value; - } - - @Override - public String toString() { - return "Pair [" + key + ", " + value + "]"; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Pair other = (Pair) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (value == null) { - if (other.value != null) - return false; - } else if (!value.equals(other.value)) - return false; - return true; - } - -} +/** + * 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.inspector.manager; + +/** + * A utility class for storing a pair of objects + * + * @param + * @param + */ +public class Pair { + private K key; + private V value; + + /** + * @param key + * @param value + */ + public Pair(K key, V value) { + this.key = key; + this.value = value; + } + + /** + * + */ + public Pair() { + // Do Nothing + } + + /** + * @return key + */ + public K getKey() { + return key; + } + + /** + * @param key + */ + public void setKey(K key) { + this.key = key; + } + + /** + * @return value + */ + public V getValue() { + return value; + } + + /** + * @param value + */ + public void setValue(V value) { + this.value = value; + } + + @Override + public String toString() { + return "Pair [" + key + ", " + value + "]"; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Pair other = (Pair) obj; + if (key == null) { + if (other.key != null) + return false; + } else if (!key.equals(other.key)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + return true; + } + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java index e9c7c1d99db..74c3cb20e88 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java @@ -1,139 +1,139 @@ -/** - * 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.inspector.manager; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import javax.swing.JComboBox; -import javax.swing.JTextField; - -/** - * A Manager for all interactions between the application and the Zookeeper - * instance - */ -public interface ZooInspectorManager extends ZooInspectorNodeManager, - ZooInspectorNodeTreeManager { - - /** - * @param connectionProps - * @return true if successfully connected - */ - public boolean connect(Properties connectionProps); - - /** - * @return true if successfully disconnected - */ - public boolean disconnect(); - - /** - * @return a {@link Pair} containing the following: - *
      - *
    • a {@link Map} of property keys to list of possible values. If - * the list size is 1 the value is taken to be the default value for - * a {@link JTextField}. If the list size is greater than 1, the - * values are taken to be the possible options to show in a - * {@link JComboBox} with the first selected as default.
    • - *
    • a {@link Map} of property keys to the label to show on the UI - *
    • - *
        - * - */ - public Pair>, Map> getConnectionPropertiesTemplate(); - - /** - * @param selectedNodes - * - the nodes to add the watcher to - * @param nodeListener - * - the node listener for this watcher - */ - public void addWatchers(Collection selectedNodes, - NodeListener nodeListener); - - /** - * @param selectedNodes - * - the nodes to remove the watchers from - */ - public void removeWatchers(Collection selectedNodes); - - /** - * @param selectedFile - * - the file to load which contains the node viewers - * configuration - * @return nodeViewers - the class names of the node viewers from the - * configuration - * @throws IOException - * - if the configuration file cannot be loaded - */ - public List loadNodeViewersFile(File selectedFile) - throws IOException; - - /** - * @param selectedFile - * - the file to save the configuration to - * @param nodeViewersClassNames - * - the class names of the node viewers - * @throws IOException - * - if the configuration file cannot be saved - */ - public void saveNodeViewersFile(File selectedFile, - List nodeViewersClassNames) throws IOException; - - /** - * @param nodeViewersClassNames - * - the class names of the node viewers - * @throws IOException - * - if the default configuration file cannot be loaded - */ - public void setDefaultNodeViewerConfiguration( - List nodeViewersClassNames) throws IOException; - - /** - * @return nodeViewers - the class names of the node viewers from the - * configuration - * @throws IOException - * - if the default configuration file cannot be loaded - */ - List getDefaultNodeViewerConfiguration() throws IOException; - - /** - * @param connectionProps - * - the connection properties last used to connect to the - * zookeeeper instance - */ - public void setLastConnectionProps(Properties connectionProps); - - /** - * @return last connection Properties - the connection properties last used - * to connect to the zookeeeper instance - */ - public Properties getLastConnectionProps(); - - /** - * @param props - * - the properties to use as the default connection settings - * @throws IOException - * - if the default configuration file cannot be saved - */ - public void saveDefaultConnectionFile(Properties props) throws IOException; - -} +/** + * 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.inspector.manager; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.swing.JComboBox; +import javax.swing.JTextField; + +/** + * A Manager for all interactions between the application and the Zookeeper + * instance + */ +public interface ZooInspectorManager extends ZooInspectorNodeManager, + ZooInspectorNodeTreeManager { + + /** + * @param connectionProps + * @return true if successfully connected + */ + public boolean connect(Properties connectionProps); + + /** + * @return true if successfully disconnected + */ + public boolean disconnect(); + + /** + * @return a {@link Pair} containing the following: + *
          + *
        • a {@link Map} of property keys to list of possible values. If + * the list size is 1 the value is taken to be the default value for + * a {@link JTextField}. If the list size is greater than 1, the + * values are taken to be the possible options to show in a + * {@link JComboBox} with the first selected as default.
        • + *
        • a {@link Map} of property keys to the label to show on the UI + *
        • + *
            + * + */ + public Pair>, Map> getConnectionPropertiesTemplate(); + + /** + * @param selectedNodes + * - the nodes to add the watcher to + * @param nodeListener + * - the node listener for this watcher + */ + public void addWatchers(Collection selectedNodes, + NodeListener nodeListener); + + /** + * @param selectedNodes + * - the nodes to remove the watchers from + */ + public void removeWatchers(Collection selectedNodes); + + /** + * @param selectedFile + * - the file to load which contains the node viewers + * configuration + * @return nodeViewers - the class names of the node viewers from the + * configuration + * @throws IOException + * - if the configuration file cannot be loaded + */ + public List loadNodeViewersFile(File selectedFile) + throws IOException; + + /** + * @param selectedFile + * - the file to save the configuration to + * @param nodeViewersClassNames + * - the class names of the node viewers + * @throws IOException + * - if the configuration file cannot be saved + */ + public void saveNodeViewersFile(File selectedFile, + List nodeViewersClassNames) throws IOException; + + /** + * @param nodeViewersClassNames + * - the class names of the node viewers + * @throws IOException + * - if the default configuration file cannot be loaded + */ + public void setDefaultNodeViewerConfiguration( + List nodeViewersClassNames) throws IOException; + + /** + * @return nodeViewers - the class names of the node viewers from the + * configuration + * @throws IOException + * - if the default configuration file cannot be loaded + */ + List getDefaultNodeViewerConfiguration() throws IOException; + + /** + * @param connectionProps + * - the connection properties last used to connect to the + * zookeeeper instance + */ + public void setLastConnectionProps(Properties connectionProps); + + /** + * @return last connection Properties - the connection properties last used + * to connect to the zookeeeper instance + */ + public Properties getLastConnectionProps(); + + /** + * @param props + * - the properties to use as the default connection settings + * @throws IOException + * - if the default configuration file cannot be saved + */ + public void saveDefaultConnectionFile(Properties props) throws IOException; + +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java index 1937d260f5e..63d82c1fb6f 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java @@ -1,884 +1,884 @@ -/** - * 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.inspector.manager; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.Watcher.Event.EventType; -import org.apache.zookeeper.Watcher.Event.KeeperState; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.ZooDefs.Perms; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager; -import org.apache.zookeeper.inspector.encryption.DataEncryptionManager; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.retry.ZooKeeperRetry; - -/** - * A default implementation of {@link ZooInspectorManager} for connecting to - * zookeeper instances - */ -public class ZooInspectorManagerImpl implements ZooInspectorManager { - private static final String A_VERSION = "ACL Version"; - private static final String C_TIME = "Creation Time"; - private static final String C_VERSION = "Children Version"; - private static final String CZXID = "Creation ID"; - private static final String DATA_LENGTH = "Data Length"; - private static final String EPHEMERAL_OWNER = "Ephemeral Owner"; - private static final String M_TIME = "Last Modified Time"; - private static final String MZXID = "Modified ID"; - private static final String NUM_CHILDREN = "Number of Children"; - private static final String PZXID = "Node ID"; - private static final String VERSION = "Data Version"; - private static final String ACL_PERMS = "Permissions"; - private static final String ACL_SCHEME = "Scheme"; - private static final String ACL_ID = "Id"; - private static final String SESSION_STATE = "Session State"; - private static final String SESSION_ID = "Session ID"; - /** - * The key used for the connect string in the connection properties file - */ - public static final String CONNECT_STRING = "hosts"; - /** - * The key used for the session timeout in the connection properties file - */ - public static final String SESSION_TIMEOUT = "timeout"; - /** - * The key used for the data encryption manager in the connection properties - * file - */ - public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager"; - /** - * The key used for the authentication scheme in the connection properties file - */ - public static final String AUTH_SCHEME_KEY = "authScheme"; - /** - * The key used for the authentication data in the connection properties file - */ - public static final String AUTH_DATA_KEY = "authData"; - - private static final File defaultNodeViewersFile = new File( - "./config/defaultNodeVeiwers.cfg"); - private static final File defaultConnectionFile = new File( - "./config/defaultConnectionSettings.cfg"); - - private DataEncryptionManager encryptionManager; - private String connectString; - private int sessionTimeout; - private ZooKeeper zooKeeper; - private final Map watchers = new HashMap(); - protected boolean connected = true; - private Properties lastConnectionProps; - private String defaultEncryptionManager; - private String defaultTimeout; - private String defaultHosts; - private String defaultAuthScheme; - private String defaultAuthValue; - - /** - * @throws IOException - * - thrown if the default connection settings cannot be loaded - * - */ - public ZooInspectorManagerImpl() throws IOException { - loadDefaultConnectionFile(); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorManager#connect(java - * .util.Properties) - */ - public boolean connect(Properties connectionProps) { - try { - if (this.zooKeeper == null) { - String connectString = connectionProps - .getProperty(CONNECT_STRING); - String sessionTimeout = connectionProps - .getProperty(SESSION_TIMEOUT); - String encryptionManager = connectionProps - .getProperty(DATA_ENCRYPTION_MANAGER); - String authScheme = connectionProps - .getProperty(AUTH_SCHEME_KEY); - String authData = connectionProps - .getProperty(AUTH_DATA_KEY); - - if (connectString == null || sessionTimeout == null) { - throw new IllegalArgumentException( - "Both connect string and session timeout are required."); - } - if (encryptionManager == null) { - this.encryptionManager = new BasicDataEncryptionManager(); - } else { - Class clazz = Class.forName(encryptionManager); - - if (Arrays.asList(clazz.getInterfaces()).contains( - DataEncryptionManager.class)) { - this.encryptionManager = (DataEncryptionManager) Class - .forName(encryptionManager).newInstance(); - } else { - throw new IllegalArgumentException( - "Data encryption manager must implement DataEncryptionManager interface"); - } - } - this.connectString = connectString; - this.sessionTimeout = Integer.valueOf(sessionTimeout); - this.zooKeeper = new ZooKeeperRetry(connectString, Integer - .valueOf(sessionTimeout), new Watcher() { - - public void process(WatchedEvent event) { - if (event.getState() == KeeperState.Expired) { - connected = false; - } - } - }); - if (authData != null && authData.length() > 0){ - this.zooKeeper.addAuthInfo(authScheme, authData.getBytes()); - } - ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10); - connected = ((ZooKeeperRetry) this.zooKeeper).testConnection(); - } - } catch (Exception e) { - connected = false; - e.printStackTrace(); - } - if (!connected){ - disconnect(); - } - return connected; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorManager#disconnect() - */ - public boolean disconnect() { - try { - if (this.zooKeeper != null) { - this.zooKeeper.close(); - this.zooKeeper = null; - connected = false; - removeWatchers(this.watchers.keySet()); - return true; - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred while disconnecting from ZooKeeper server", - e); - } - return false; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getChildren(java.lang.String) - */ - public List getChildren(String nodePath) { - if (connected) { - try { - - return zooKeeper.getChildren(nodePath, false); - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving children of node: " - + nodePath, e); - } - } - return null; - - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getData - * (java.lang.String) - */ - public String getData(String nodePath) { - if (connected) { - try { - if (nodePath.length() == 0) { - nodePath = "/"; - } - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - return this.encryptionManager.decryptData(zooKeeper - .getData(nodePath, false, s)); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred getting data for node: " + nodePath, e); - } - } - return null; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getNodeChild(java.lang.String, int) - */ - public String getNodeChild(String nodePath, int childIndex) { - if (connected) { - try { - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - return this.zooKeeper.getChildren(nodePath, false).get( - childIndex); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving child " + childIndex - + " of node: " + nodePath, e); - } - } - return null; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getNodeIndex(java.lang.String) - */ - public int getNodeIndex(String nodePath) { - if (connected) { - int index = nodePath.lastIndexOf("/"); - if (index == -1 - || (!nodePath.equals("/") && nodePath.charAt(nodePath - .length() - 1) == '/')) { - throw new IllegalArgumentException("Invalid node path: " - + nodePath); - } - String parentPath = nodePath.substring(0, index); - String child = nodePath.substring(index + 1); - if (parentPath != null && parentPath.length() > 0) { - List children = this.getChildren(parentPath); - if (children != null) { - return children.indexOf(child); - } - } - } - return -1; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getACLs - * (java.lang.String) - */ - public List> getACLs(String nodePath) { - List> returnACLs = new ArrayList>(); - if (connected) { - try { - if (nodePath.length() == 0) { - nodePath = "/"; - } - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - List acls = zooKeeper.getACL(nodePath, s); - for (ACL acl : acls) { - Map aclMap = new LinkedHashMap(); - aclMap.put(ACL_SCHEME, acl.getId().getScheme()); - aclMap.put(ACL_ID, acl.getId().getId()); - StringBuilder sb = new StringBuilder(); - int perms = acl.getPerms(); - boolean addedPerm = false; - if ((perms & Perms.READ) == Perms.READ) { - sb.append("Read"); - addedPerm = true; - } - if (addedPerm) { - sb.append(", "); - } - if ((perms & Perms.WRITE) == Perms.WRITE) { - sb.append("Write"); - addedPerm = true; - } - if (addedPerm) { - sb.append(", "); - } - if ((perms & Perms.CREATE) == Perms.CREATE) { - sb.append("Create"); - addedPerm = true; - } - if (addedPerm) { - sb.append(", "); - } - if ((perms & Perms.DELETE) == Perms.DELETE) { - sb.append("Delete"); - addedPerm = true; - } - if (addedPerm) { - sb.append(", "); - } - if ((perms & Perms.ADMIN) == Perms.ADMIN) { - sb.append("Admin"); - addedPerm = true; - } - aclMap.put(ACL_PERMS, sb.toString()); - returnACLs.add(aclMap); - } - } - } catch (InterruptedException e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving ACLs of node: " + nodePath, - e); - } catch (KeeperException e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving ACLs of node: " + nodePath, - e); - } - } - return returnACLs; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getNodeMeta(java.lang.String) - */ - public Map getNodeMeta(String nodePath) { - Map nodeMeta = new LinkedHashMap(); - if (connected) { - try { - if (nodePath.length() == 0) { - nodePath = "/"; - } - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - nodeMeta.put(A_VERSION, String.valueOf(s.getAversion())); - nodeMeta.put(C_TIME, String.valueOf(s.getCtime())); - nodeMeta.put(C_VERSION, String.valueOf(s.getCversion())); - nodeMeta.put(CZXID, String.valueOf(s.getCzxid())); - nodeMeta - .put(DATA_LENGTH, String.valueOf(s.getDataLength())); - nodeMeta.put(EPHEMERAL_OWNER, String.valueOf(s - .getEphemeralOwner())); - nodeMeta.put(M_TIME, String.valueOf(s.getMtime())); - nodeMeta.put(MZXID, String.valueOf(s.getMzxid())); - nodeMeta.put(NUM_CHILDREN, String.valueOf(s - .getNumChildren())); - nodeMeta.put(PZXID, String.valueOf(s.getPzxid())); - nodeMeta.put(VERSION, String.valueOf(s.getVersion())); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving meta data for node: " - + nodePath, e); - } - } - return nodeMeta; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getNumChildren(java.lang.String) - */ - public int getNumChildren(String nodePath) { - if (connected) { - try { - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - return s.getNumChildren(); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred getting the number of children of node: " - + nodePath, e); - } - } - return -1; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * hasChildren(java.lang.String) - */ - public boolean hasChildren(String nodePath) { - return getNumChildren(nodePath) > 0; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * isAllowsChildren(java.lang.String) - */ - public boolean isAllowsChildren(String nodePath) { - if (connected) { - try { - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - return s.getEphemeralOwner() == 0; - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred determining whether node is allowed children: " - + nodePath, e); - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getSessionMeta() - */ - public Map getSessionMeta() { - Map sessionMeta = new LinkedHashMap(); - try { - if (zooKeeper != null) { - - sessionMeta.put(SESSION_ID, String.valueOf(zooKeeper - .getSessionId())); - sessionMeta.put(SESSION_STATE, String.valueOf(zooKeeper - .getState().toString())); - sessionMeta.put(CONNECT_STRING, this.connectString); - sessionMeta.put(SESSION_TIMEOUT, String - .valueOf(this.sessionTimeout)); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving session meta data.", e); - } - return sessionMeta; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#createNode - * (java.lang.String, java.lang.String) - */ - public boolean createNode(String parent, String nodeName) { - if (connected) { - try { - String[] nodeElements = nodeName.split("/"); - for (String nodeElement : nodeElements) { - String node = parent + "/" + nodeElement; - Stat s = zooKeeper.exists(node, false); - if (s == null) { - zooKeeper.create(node, this.encryptionManager - .encryptData(null), Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - parent = node; - } - } - return true; - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred creating node: " + parent + "/" - + nodeName, e); - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#deleteNode - * (java.lang.String) - */ - public boolean deleteNode(String nodePath) { - if (connected) { - try { - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - List children = zooKeeper.getChildren(nodePath, - false); - for (String child : children) { - String node = nodePath + "/" + child; - deleteNode(node); - } - zooKeeper.delete(nodePath, -1); - } - return true; - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred deleting node: " + nodePath, e); - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager#setData - * (java.lang.String, java.lang.String) - */ - public boolean setData(String nodePath, String data) { - if (connected) { - try { - zooKeeper.setData(nodePath, this.encryptionManager - .encryptData(data), -1); - return true; - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred setting data for node: " + nodePath, e); - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * getConnectionPropertiesTemplate() - */ - public Pair>, Map> getConnectionPropertiesTemplate() { - Map> template = new LinkedHashMap>(); - template.put(CONNECT_STRING, Arrays - .asList(new String[] { defaultHosts })); - template.put(SESSION_TIMEOUT, Arrays - .asList(new String[] { defaultTimeout })); - template.put(DATA_ENCRYPTION_MANAGER, Arrays - .asList(new String[] { defaultEncryptionManager })); - template.put(AUTH_SCHEME_KEY, Arrays - .asList(new String[] { defaultAuthScheme })); - template.put(AUTH_DATA_KEY, Arrays - .asList(new String[] { defaultAuthValue })); - Map labels = new LinkedHashMap(); - labels.put(CONNECT_STRING, "Connect String"); - labels.put(SESSION_TIMEOUT, "Session Timeout"); - labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager"); - labels.put(AUTH_SCHEME_KEY, "Authentication Scheme"); - labels.put(AUTH_DATA_KEY, "Authentication Data"); - return new Pair>, Map>( - template, labels); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorManager#addWatchers - * (java.util.Collection, - * org.apache.zookeeper.inspector.manager.NodeListener) - */ - public void addWatchers(Collection selectedNodes, - NodeListener nodeListener) { - // add watcher for each node and add node to collection of - // watched nodes - if (connected) { - for (String node : selectedNodes) { - if (!watchers.containsKey(node)) { - try { - watchers.put(node, new NodeWatcher(node, nodeListener, - zooKeeper)); - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occured adding node watcher for node: " - + node, e); - } - } - } - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.ZooInspectorManager#removeWatchers - * (java.util.Collection) - */ - public void removeWatchers(Collection selectedNodes) { - // remove watcher for each node and remove node from - // collection of watched nodes - if (connected) { - for (String node : selectedNodes) { - if (watchers.containsKey(node)) { - NodeWatcher watcher = watchers.remove(node); - if (watcher != null) { - watcher.stop(); - } - } - } - } - } - - /** - * A Watcher which will re-add itself every time an event is fired - * - */ - public class NodeWatcher implements Watcher { - - private final String nodePath; - private final NodeListener nodeListener; - private final ZooKeeper zookeeper; - private boolean closed = false; - - /** - * @param nodePath - * - the path to the node to watch - * @param nodeListener - * the {@link NodeListener} for this node - * @param zookeeper - * - a {@link ZooKeeper} to use to access zookeeper - * @throws InterruptedException - * @throws KeeperException - */ - public NodeWatcher(String nodePath, NodeListener nodeListener, - ZooKeeper zookeeper) throws KeeperException, - InterruptedException { - this.nodePath = nodePath; - this.nodeListener = nodeListener; - this.zookeeper = zookeeper; - Stat s = zooKeeper.exists(nodePath, this); - if (s != null) { - zookeeper.getChildren(nodePath, this); - } - } - - public void process(WatchedEvent event) { - if (!closed) { - try { - if (event.getType() != EventType.NodeDeleted) { - - Stat s = zooKeeper.exists(nodePath, this); - if (s != null) { - zookeeper.getChildren(nodePath, this); - } - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occured re-adding node watcherfor node " - + nodePath, e); - } - nodeListener.processEvent(event.getPath(), event.getType() - .name(), null); - } - } - - /** - * - */ - public void stop() { - this.closed = true; - } - - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * loadNodeViewersFile(java.io.File) - */ - public List loadNodeViewersFile(File selectedFile) - throws IOException { - List result = new ArrayList(); - if (defaultNodeViewersFile.exists()) { - FileReader reader = new FileReader(selectedFile); - try { - BufferedReader buff = new BufferedReader(reader); - try { - while (buff.ready()) { - String line = buff.readLine(); - if (line != null && line.length() > 0 && !line.startsWith("#")) { - result.add(line); - } - } - } finally { - buff.close(); - } - } finally { - reader.close(); - } - } - return result; - } - - private void loadDefaultConnectionFile() throws IOException { - if (defaultConnectionFile.exists()) { - Properties props = new Properties(); - - FileReader reader = new FileReader(defaultConnectionFile); - try { - props.load(reader); - } finally { - reader.close(); - } - defaultEncryptionManager = props - .getProperty(DATA_ENCRYPTION_MANAGER) == null ? "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager" - : props.getProperty(DATA_ENCRYPTION_MANAGER); - defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ? "5000" - : props.getProperty(SESSION_TIMEOUT); - defaultHosts = props.getProperty(CONNECT_STRING) == null ? "localhost:2181" - : props.getProperty(CONNECT_STRING); - defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ? "" - : props.getProperty(AUTH_SCHEME_KEY); - defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ? "" - : props.getProperty(AUTH_DATA_KEY); - } else { - defaultEncryptionManager = "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager"; - defaultTimeout = "5000"; - defaultHosts = "localhost:2181"; - defaultAuthScheme = ""; - defaultAuthValue = ""; - } - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * saveDefaultConnectionFile(java.util.Properties) - */ - public void saveDefaultConnectionFile(Properties props) throws IOException { - File defaultDir = defaultConnectionFile.getParentFile(); - if (!defaultDir.exists()) { - if (!defaultDir.mkdirs()) { - throw new IOException( - "Failed to create configuration directory: " - + defaultDir.getAbsolutePath()); - } - } - if (!defaultConnectionFile.exists()) { - if (!defaultConnectionFile.createNewFile()) { - throw new IOException( - "Failed to create default connection file: " - + defaultConnectionFile.getAbsolutePath()); - } - } - FileWriter writer = new FileWriter(defaultConnectionFile); - try { - props.store(writer, "Default connection for ZooInspector"); - } finally { - writer.close(); - } - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * saveNodeViewersFile(java.io.File, java.util.List) - */ - public void saveNodeViewersFile(File selectedFile, - List nodeViewersClassNames) throws IOException { - if (!selectedFile.exists()) { - if (!selectedFile.createNewFile()) { - throw new IOException( - "Failed to create node viewers configuration file: " - + selectedFile.getAbsolutePath()); - } - } - FileWriter writer = new FileWriter(selectedFile); - try { - BufferedWriter buff = new BufferedWriter(writer); - try { - for (String nodeViewersClassName : nodeViewersClassNames) { - buff.append(nodeViewersClassName); - buff.append("\n"); - } - } finally { - buff.flush(); - buff.close(); - } - } finally { - writer.close(); - } - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * setDefaultNodeViewerConfiguration(java.io.File, java.util.List) - */ - public void setDefaultNodeViewerConfiguration( - List nodeViewersClassNames) throws IOException { - File defaultDir = defaultNodeViewersFile.getParentFile(); - if (!defaultDir.exists()) { - if (!defaultDir.mkdirs()) { - throw new IOException( - "Failed to create configuration directory: " - + defaultDir.getAbsolutePath()); - } - } - saveNodeViewersFile(defaultNodeViewersFile, nodeViewersClassNames); - } - - public List getDefaultNodeViewerConfiguration() throws IOException { - return loadNodeViewersFile(defaultNodeViewersFile); - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * getLastConnectionProps() - */ - public Properties getLastConnectionProps() { - return this.lastConnectionProps; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# - * setLastConnectionProps(java.util.Properties) - */ - public void setLastConnectionProps(Properties connectionProps) { - this.lastConnectionProps = connectionProps; - } -} +/** + * 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.inspector.manager; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.Watcher.Event.EventType; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooDefs.Perms; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager; +import org.apache.zookeeper.inspector.encryption.DataEncryptionManager; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.retry.ZooKeeperRetry; + +/** + * A default implementation of {@link ZooInspectorManager} for connecting to + * zookeeper instances + */ +public class ZooInspectorManagerImpl implements ZooInspectorManager { + private static final String A_VERSION = "ACL Version"; + private static final String C_TIME = "Creation Time"; + private static final String C_VERSION = "Children Version"; + private static final String CZXID = "Creation ID"; + private static final String DATA_LENGTH = "Data Length"; + private static final String EPHEMERAL_OWNER = "Ephemeral Owner"; + private static final String M_TIME = "Last Modified Time"; + private static final String MZXID = "Modified ID"; + private static final String NUM_CHILDREN = "Number of Children"; + private static final String PZXID = "Node ID"; + private static final String VERSION = "Data Version"; + private static final String ACL_PERMS = "Permissions"; + private static final String ACL_SCHEME = "Scheme"; + private static final String ACL_ID = "Id"; + private static final String SESSION_STATE = "Session State"; + private static final String SESSION_ID = "Session ID"; + /** + * The key used for the connect string in the connection properties file + */ + public static final String CONNECT_STRING = "hosts"; + /** + * The key used for the session timeout in the connection properties file + */ + public static final String SESSION_TIMEOUT = "timeout"; + /** + * The key used for the data encryption manager in the connection properties + * file + */ + public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager"; + /** + * The key used for the authentication scheme in the connection properties file + */ + public static final String AUTH_SCHEME_KEY = "authScheme"; + /** + * The key used for the authentication data in the connection properties file + */ + public static final String AUTH_DATA_KEY = "authData"; + + private static final File defaultNodeViewersFile = new File( + "./config/defaultNodeVeiwers.cfg"); + private static final File defaultConnectionFile = new File( + "./config/defaultConnectionSettings.cfg"); + + private DataEncryptionManager encryptionManager; + private String connectString; + private int sessionTimeout; + private ZooKeeper zooKeeper; + private final Map watchers = new HashMap(); + protected boolean connected = true; + private Properties lastConnectionProps; + private String defaultEncryptionManager; + private String defaultTimeout; + private String defaultHosts; + private String defaultAuthScheme; + private String defaultAuthValue; + + /** + * @throws IOException + * - thrown if the default connection settings cannot be loaded + * + */ + public ZooInspectorManagerImpl() throws IOException { + loadDefaultConnectionFile(); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorManager#connect(java + * .util.Properties) + */ + public boolean connect(Properties connectionProps) { + try { + if (this.zooKeeper == null) { + String connectString = connectionProps + .getProperty(CONNECT_STRING); + String sessionTimeout = connectionProps + .getProperty(SESSION_TIMEOUT); + String encryptionManager = connectionProps + .getProperty(DATA_ENCRYPTION_MANAGER); + String authScheme = connectionProps + .getProperty(AUTH_SCHEME_KEY); + String authData = connectionProps + .getProperty(AUTH_DATA_KEY); + + if (connectString == null || sessionTimeout == null) { + throw new IllegalArgumentException( + "Both connect string and session timeout are required."); + } + if (encryptionManager == null) { + this.encryptionManager = new BasicDataEncryptionManager(); + } else { + Class clazz = Class.forName(encryptionManager); + + if (Arrays.asList(clazz.getInterfaces()).contains( + DataEncryptionManager.class)) { + this.encryptionManager = (DataEncryptionManager) Class + .forName(encryptionManager).newInstance(); + } else { + throw new IllegalArgumentException( + "Data encryption manager must implement DataEncryptionManager interface"); + } + } + this.connectString = connectString; + this.sessionTimeout = Integer.valueOf(sessionTimeout); + this.zooKeeper = new ZooKeeperRetry(connectString, Integer + .valueOf(sessionTimeout), new Watcher() { + + public void process(WatchedEvent event) { + if (event.getState() == KeeperState.Expired) { + connected = false; + } + } + }); + if (authData != null && authData.length() > 0){ + this.zooKeeper.addAuthInfo(authScheme, authData.getBytes()); + } + ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10); + connected = ((ZooKeeperRetry) this.zooKeeper).testConnection(); + } + } catch (Exception e) { + connected = false; + e.printStackTrace(); + } + if (!connected){ + disconnect(); + } + return connected; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorManager#disconnect() + */ + public boolean disconnect() { + try { + if (this.zooKeeper != null) { + this.zooKeeper.close(); + this.zooKeeper = null; + connected = false; + removeWatchers(this.watchers.keySet()); + return true; + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred while disconnecting from ZooKeeper server", + e); + } + return false; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * getChildren(java.lang.String) + */ + public List getChildren(String nodePath) { + if (connected) { + try { + + return zooKeeper.getChildren(nodePath, false); + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred retrieving children of node: " + + nodePath, e); + } + } + return null; + + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getData + * (java.lang.String) + */ + public String getData(String nodePath) { + if (connected) { + try { + if (nodePath.length() == 0) { + nodePath = "/"; + } + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + return this.encryptionManager.decryptData(zooKeeper + .getData(nodePath, false, s)); + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred getting data for node: " + nodePath, e); + } + } + return null; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * getNodeChild(java.lang.String, int) + */ + public String getNodeChild(String nodePath, int childIndex) { + if (connected) { + try { + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + return this.zooKeeper.getChildren(nodePath, false).get( + childIndex); + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred retrieving child " + childIndex + + " of node: " + nodePath, e); + } + } + return null; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * getNodeIndex(java.lang.String) + */ + public int getNodeIndex(String nodePath) { + if (connected) { + int index = nodePath.lastIndexOf("/"); + if (index == -1 + || (!nodePath.equals("/") && nodePath.charAt(nodePath + .length() - 1) == '/')) { + throw new IllegalArgumentException("Invalid node path: " + + nodePath); + } + String parentPath = nodePath.substring(0, index); + String child = nodePath.substring(index + 1); + if (parentPath != null && parentPath.length() > 0) { + List children = this.getChildren(parentPath); + if (children != null) { + return children.indexOf(child); + } + } + } + return -1; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getACLs + * (java.lang.String) + */ + public List> getACLs(String nodePath) { + List> returnACLs = new ArrayList>(); + if (connected) { + try { + if (nodePath.length() == 0) { + nodePath = "/"; + } + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + List acls = zooKeeper.getACL(nodePath, s); + for (ACL acl : acls) { + Map aclMap = new LinkedHashMap(); + aclMap.put(ACL_SCHEME, acl.getId().getScheme()); + aclMap.put(ACL_ID, acl.getId().getId()); + StringBuilder sb = new StringBuilder(); + int perms = acl.getPerms(); + boolean addedPerm = false; + if ((perms & Perms.READ) == Perms.READ) { + sb.append("Read"); + addedPerm = true; + } + if (addedPerm) { + sb.append(", "); + } + if ((perms & Perms.WRITE) == Perms.WRITE) { + sb.append("Write"); + addedPerm = true; + } + if (addedPerm) { + sb.append(", "); + } + if ((perms & Perms.CREATE) == Perms.CREATE) { + sb.append("Create"); + addedPerm = true; + } + if (addedPerm) { + sb.append(", "); + } + if ((perms & Perms.DELETE) == Perms.DELETE) { + sb.append("Delete"); + addedPerm = true; + } + if (addedPerm) { + sb.append(", "); + } + if ((perms & Perms.ADMIN) == Perms.ADMIN) { + sb.append("Admin"); + addedPerm = true; + } + aclMap.put(ACL_PERMS, sb.toString()); + returnACLs.add(aclMap); + } + } + } catch (InterruptedException e) { + LoggerFactory.getLogger().error( + "Error occurred retrieving ACLs of node: " + nodePath, + e); + } catch (KeeperException e) { + LoggerFactory.getLogger().error( + "Error occurred retrieving ACLs of node: " + nodePath, + e); + } + } + return returnACLs; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * getNodeMeta(java.lang.String) + */ + public Map getNodeMeta(String nodePath) { + Map nodeMeta = new LinkedHashMap(); + if (connected) { + try { + if (nodePath.length() == 0) { + nodePath = "/"; + } + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + nodeMeta.put(A_VERSION, String.valueOf(s.getAversion())); + nodeMeta.put(C_TIME, String.valueOf(s.getCtime())); + nodeMeta.put(C_VERSION, String.valueOf(s.getCversion())); + nodeMeta.put(CZXID, String.valueOf(s.getCzxid())); + nodeMeta + .put(DATA_LENGTH, String.valueOf(s.getDataLength())); + nodeMeta.put(EPHEMERAL_OWNER, String.valueOf(s + .getEphemeralOwner())); + nodeMeta.put(M_TIME, String.valueOf(s.getMtime())); + nodeMeta.put(MZXID, String.valueOf(s.getMzxid())); + nodeMeta.put(NUM_CHILDREN, String.valueOf(s + .getNumChildren())); + nodeMeta.put(PZXID, String.valueOf(s.getPzxid())); + nodeMeta.put(VERSION, String.valueOf(s.getVersion())); + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred retrieving meta data for node: " + + nodePath, e); + } + } + return nodeMeta; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * getNumChildren(java.lang.String) + */ + public int getNumChildren(String nodePath) { + if (connected) { + try { + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + return s.getNumChildren(); + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred getting the number of children of node: " + + nodePath, e); + } + } + return -1; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * hasChildren(java.lang.String) + */ + public boolean hasChildren(String nodePath) { + return getNumChildren(nodePath) > 0; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * isAllowsChildren(java.lang.String) + */ + public boolean isAllowsChildren(String nodePath) { + if (connected) { + try { + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + return s.getEphemeralOwner() == 0; + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred determining whether node is allowed children: " + + nodePath, e); + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * getSessionMeta() + */ + public Map getSessionMeta() { + Map sessionMeta = new LinkedHashMap(); + try { + if (zooKeeper != null) { + + sessionMeta.put(SESSION_ID, String.valueOf(zooKeeper + .getSessionId())); + sessionMeta.put(SESSION_STATE, String.valueOf(zooKeeper + .getState().toString())); + sessionMeta.put(CONNECT_STRING, this.connectString); + sessionMeta.put(SESSION_TIMEOUT, String + .valueOf(this.sessionTimeout)); + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred retrieving session meta data.", e); + } + return sessionMeta; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#createNode + * (java.lang.String, java.lang.String) + */ + public boolean createNode(String parent, String nodeName) { + if (connected) { + try { + String[] nodeElements = nodeName.split("/"); + for (String nodeElement : nodeElements) { + String node = parent + "/" + nodeElement; + Stat s = zooKeeper.exists(node, false); + if (s == null) { + zooKeeper.create(node, this.encryptionManager + .encryptData(null), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + parent = node; + } + } + return true; + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred creating node: " + parent + "/" + + nodeName, e); + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#deleteNode + * (java.lang.String) + */ + public boolean deleteNode(String nodePath) { + if (connected) { + try { + Stat s = zooKeeper.exists(nodePath, false); + if (s != null) { + List children = zooKeeper.getChildren(nodePath, + false); + for (String child : children) { + String node = nodePath + "/" + child; + deleteNode(node); + } + zooKeeper.delete(nodePath, -1); + } + return true; + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred deleting node: " + nodePath, e); + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager#setData + * (java.lang.String, java.lang.String) + */ + public boolean setData(String nodePath, String data) { + if (connected) { + try { + zooKeeper.setData(nodePath, this.encryptionManager + .encryptData(data), -1); + return true; + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occurred setting data for node: " + nodePath, e); + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * getConnectionPropertiesTemplate() + */ + public Pair>, Map> getConnectionPropertiesTemplate() { + Map> template = new LinkedHashMap>(); + template.put(CONNECT_STRING, Arrays + .asList(new String[] { defaultHosts })); + template.put(SESSION_TIMEOUT, Arrays + .asList(new String[] { defaultTimeout })); + template.put(DATA_ENCRYPTION_MANAGER, Arrays + .asList(new String[] { defaultEncryptionManager })); + template.put(AUTH_SCHEME_KEY, Arrays + .asList(new String[] { defaultAuthScheme })); + template.put(AUTH_DATA_KEY, Arrays + .asList(new String[] { defaultAuthValue })); + Map labels = new LinkedHashMap(); + labels.put(CONNECT_STRING, "Connect String"); + labels.put(SESSION_TIMEOUT, "Session Timeout"); + labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager"); + labels.put(AUTH_SCHEME_KEY, "Authentication Scheme"); + labels.put(AUTH_DATA_KEY, "Authentication Data"); + return new Pair>, Map>( + template, labels); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorManager#addWatchers + * (java.util.Collection, + * org.apache.zookeeper.inspector.manager.NodeListener) + */ + public void addWatchers(Collection selectedNodes, + NodeListener nodeListener) { + // add watcher for each node and add node to collection of + // watched nodes + if (connected) { + for (String node : selectedNodes) { + if (!watchers.containsKey(node)) { + try { + watchers.put(node, new NodeWatcher(node, nodeListener, + zooKeeper)); + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occured adding node watcher for node: " + + node, e); + } + } + } + } + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.zookeeper.inspector.manager.ZooInspectorManager#removeWatchers + * (java.util.Collection) + */ + public void removeWatchers(Collection selectedNodes) { + // remove watcher for each node and remove node from + // collection of watched nodes + if (connected) { + for (String node : selectedNodes) { + if (watchers.containsKey(node)) { + NodeWatcher watcher = watchers.remove(node); + if (watcher != null) { + watcher.stop(); + } + } + } + } + } + + /** + * A Watcher which will re-add itself every time an event is fired + * + */ + public class NodeWatcher implements Watcher { + + private final String nodePath; + private final NodeListener nodeListener; + private final ZooKeeper zookeeper; + private boolean closed = false; + + /** + * @param nodePath + * - the path to the node to watch + * @param nodeListener + * the {@link NodeListener} for this node + * @param zookeeper + * - a {@link ZooKeeper} to use to access zookeeper + * @throws InterruptedException + * @throws KeeperException + */ + public NodeWatcher(String nodePath, NodeListener nodeListener, + ZooKeeper zookeeper) throws KeeperException, + InterruptedException { + this.nodePath = nodePath; + this.nodeListener = nodeListener; + this.zookeeper = zookeeper; + Stat s = zooKeeper.exists(nodePath, this); + if (s != null) { + zookeeper.getChildren(nodePath, this); + } + } + + public void process(WatchedEvent event) { + if (!closed) { + try { + if (event.getType() != EventType.NodeDeleted) { + + Stat s = zooKeeper.exists(nodePath, this); + if (s != null) { + zookeeper.getChildren(nodePath, this); + } + } + } catch (Exception e) { + LoggerFactory.getLogger().error( + "Error occured re-adding node watcherfor node " + + nodePath, e); + } + nodeListener.processEvent(event.getPath(), event.getType() + .name(), null); + } + } + + /** + * + */ + public void stop() { + this.closed = true; + } + + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * loadNodeViewersFile(java.io.File) + */ + public List loadNodeViewersFile(File selectedFile) + throws IOException { + List result = new ArrayList(); + if (defaultNodeViewersFile.exists()) { + FileReader reader = new FileReader(selectedFile); + try { + BufferedReader buff = new BufferedReader(reader); + try { + while (buff.ready()) { + String line = buff.readLine(); + if (line != null && line.length() > 0 && !line.startsWith("#")) { + result.add(line); + } + } + } finally { + buff.close(); + } + } finally { + reader.close(); + } + } + return result; + } + + private void loadDefaultConnectionFile() throws IOException { + if (defaultConnectionFile.exists()) { + Properties props = new Properties(); + + FileReader reader = new FileReader(defaultConnectionFile); + try { + props.load(reader); + } finally { + reader.close(); + } + defaultEncryptionManager = props + .getProperty(DATA_ENCRYPTION_MANAGER) == null ? "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager" + : props.getProperty(DATA_ENCRYPTION_MANAGER); + defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ? "5000" + : props.getProperty(SESSION_TIMEOUT); + defaultHosts = props.getProperty(CONNECT_STRING) == null ? "localhost:2181" + : props.getProperty(CONNECT_STRING); + defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ? "" + : props.getProperty(AUTH_SCHEME_KEY); + defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ? "" + : props.getProperty(AUTH_DATA_KEY); + } else { + defaultEncryptionManager = "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager"; + defaultTimeout = "5000"; + defaultHosts = "localhost:2181"; + defaultAuthScheme = ""; + defaultAuthValue = ""; + } + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * saveDefaultConnectionFile(java.util.Properties) + */ + public void saveDefaultConnectionFile(Properties props) throws IOException { + File defaultDir = defaultConnectionFile.getParentFile(); + if (!defaultDir.exists()) { + if (!defaultDir.mkdirs()) { + throw new IOException( + "Failed to create configuration directory: " + + defaultDir.getAbsolutePath()); + } + } + if (!defaultConnectionFile.exists()) { + if (!defaultConnectionFile.createNewFile()) { + throw new IOException( + "Failed to create default connection file: " + + defaultConnectionFile.getAbsolutePath()); + } + } + FileWriter writer = new FileWriter(defaultConnectionFile); + try { + props.store(writer, "Default connection for ZooInspector"); + } finally { + writer.close(); + } + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * saveNodeViewersFile(java.io.File, java.util.List) + */ + public void saveNodeViewersFile(File selectedFile, + List nodeViewersClassNames) throws IOException { + if (!selectedFile.exists()) { + if (!selectedFile.createNewFile()) { + throw new IOException( + "Failed to create node viewers configuration file: " + + selectedFile.getAbsolutePath()); + } + } + FileWriter writer = new FileWriter(selectedFile); + try { + BufferedWriter buff = new BufferedWriter(writer); + try { + for (String nodeViewersClassName : nodeViewersClassNames) { + buff.append(nodeViewersClassName); + buff.append("\n"); + } + } finally { + buff.flush(); + buff.close(); + } + } finally { + writer.close(); + } + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * setDefaultNodeViewerConfiguration(java.io.File, java.util.List) + */ + public void setDefaultNodeViewerConfiguration( + List nodeViewersClassNames) throws IOException { + File defaultDir = defaultNodeViewersFile.getParentFile(); + if (!defaultDir.exists()) { + if (!defaultDir.mkdirs()) { + throw new IOException( + "Failed to create configuration directory: " + + defaultDir.getAbsolutePath()); + } + } + saveNodeViewersFile(defaultNodeViewersFile, nodeViewersClassNames); + } + + public List getDefaultNodeViewerConfiguration() throws IOException { + return loadNodeViewersFile(defaultNodeViewersFile); + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * getLastConnectionProps() + */ + public Properties getLastConnectionProps() { + return this.lastConnectionProps; + } + + /* + * (non-Javadoc) + * + * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * setLastConnectionProps(java.util.Properties) + */ + public void setLastConnectionProps(Properties connectionProps) { + this.lastConnectionProps = connectionProps; + } +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java index c81fa78b833..0c62547d0bb 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java @@ -1,33 +1,33 @@ -/** - * 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.inspector.manager; - -/** - * A Manager for all interactions between the application and the nodes in a - * Zookeeper instance - * */ -public interface ZooInspectorNodeManager extends ZooInspectorReadOnlyManager { - /** - * @param nodePath - * - the path to the node on which to set the data - * @param data - * - the data to set on the this node - * @return true if the data for the node was successfully updated - */ - public boolean setData(String nodePath, String data); -} +/** + * 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.inspector.manager; + +/** + * A Manager for all interactions between the application and the nodes in a + * Zookeeper instance + * */ +public interface ZooInspectorNodeManager extends ZooInspectorReadOnlyManager { + /** + * @param nodePath + * - the path to the node on which to set the data + * @param data + * - the data to set on the this node + * @return true if the data for the node was successfully updated + */ + public boolean setData(String nodePath, String data); +} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java index c5cf4458294..feb4301245f 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java @@ -1,288 +1,288 @@ -/** - * 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.retry; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.inspector.logger.LoggerFactory; - -/** - * A Class which extends {@link ZooKeeper} and will automatically retry calls to - * zookeeper if a {@link KeeperException.ConnectionLossException} occurs - */ -public class ZooKeeperRetry extends ZooKeeper { - - private boolean closed = false; - private final Watcher watcher; - private int limit = -1; - - /** - * @param connectString - * @param sessionTimeout - * @param watcher - * @throws IOException - */ - public ZooKeeperRetry(String connectString, int sessionTimeout, - Watcher watcher) throws IOException { - super(connectString, sessionTimeout, watcher); - this.watcher = watcher; - } - - /** - * @param connectString - * @param sessionTimeout - * @param watcher - * @param sessionId - * @param sessionPasswd - * @throws IOException - */ - public ZooKeeperRetry(String connectString, int sessionTimeout, - Watcher watcher, long sessionId, byte[] sessionPasswd) - throws IOException { - super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd); - this.watcher = watcher; - } - - @Override - public synchronized void close() throws InterruptedException { - this.closed = true; - super.close(); - } - - @Override - public String create(String path, byte[] data, List acl, - CreateMode createMode) throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.create(path, data, acl, createMode); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - if (exists(path, false) != null) { - return path; - } - } catch (KeeperException.NodeExistsException e) { - return path; - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public void delete(String path, int version) throws InterruptedException, - KeeperException { - int count = 0; - do { - try { - super.delete(path, version); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - if (exists(path, false) == null) { - return; - } - } catch (KeeperException.NoNodeException e) { - break; - } - } while (!closed && (limit == -1 || count++ < limit)); - } - - @Override - public Stat exists(String path, boolean watch) throws KeeperException, - InterruptedException { - int count = 0; - do { - try { - return super.exists(path, watch ? watcher : null); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public Stat exists(String path, Watcher watcher) throws KeeperException, - InterruptedException { - int count = 0; - do { - try { - return super.exists(path, watcher); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public List getACL(String path, Stat stat) throws KeeperException, - InterruptedException { - int count = 0; - do { - try { - return super.getACL(path, stat); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public List getChildren(String path, boolean watch) - throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.getChildren(path, watch ? watcher : null); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return new ArrayList(); - } - - @Override - public List getChildren(String path, Watcher watcher) - throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.getChildren(path, watcher); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return new ArrayList(); - } - - @Override - public byte[] getData(String path, boolean watch, Stat stat) - throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.getData(path, watch ? watcher : null, stat); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public byte[] getData(String path, Watcher watcher, Stat stat) - throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.getData(path, watcher, stat); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public Stat setACL(String path, List acl, int version) - throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.setACL(path, acl, version); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - Stat s = exists(path, false); - if (s != null) { - if (getACL(path, s).equals(acl)) { - return s; - } - } else { - return null; - } - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - @Override - public Stat setData(String path, byte[] data, int version) - throws KeeperException, InterruptedException { - int count = 0; - do { - try { - return super.setData(path, data, version); - } catch (KeeperException.ConnectionLossException e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - Stat s = exists(path, false); - if (s != null) { - if (getData(path, false, s) == data) { - return s; - } - } else { - return null; - } - } - } while (!closed && (limit == -1 || count++ < limit)); - return null; - } - - /** - * @param limit - */ - public void setRetryLimit(int limit) { - this.limit = limit; - } - - /** - * @return true if successfully connected to zookeeper - */ - public boolean testConnection() { - int count = 0; - do { - try { - return super.exists("/", null) != null; - } catch (Exception e) { - LoggerFactory.getLogger().warn( - "ZooKeeper connection lost. Trying to reconnect."); - } - } while (count++ < 5); - return false; - } - -} +/** + * 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.retry; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.inspector.logger.LoggerFactory; + +/** + * A Class which extends {@link ZooKeeper} and will automatically retry calls to + * zookeeper if a {@link KeeperException.ConnectionLossException} occurs + */ +public class ZooKeeperRetry extends ZooKeeper { + + private boolean closed = false; + private final Watcher watcher; + private int limit = -1; + + /** + * @param connectString + * @param sessionTimeout + * @param watcher + * @throws IOException + */ + public ZooKeeperRetry(String connectString, int sessionTimeout, + Watcher watcher) throws IOException { + super(connectString, sessionTimeout, watcher); + this.watcher = watcher; + } + + /** + * @param connectString + * @param sessionTimeout + * @param watcher + * @param sessionId + * @param sessionPasswd + * @throws IOException + */ + public ZooKeeperRetry(String connectString, int sessionTimeout, + Watcher watcher, long sessionId, byte[] sessionPasswd) + throws IOException { + super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd); + this.watcher = watcher; + } + + @Override + public synchronized void close() throws InterruptedException { + this.closed = true; + super.close(); + } + + @Override + public String create(String path, byte[] data, List acl, + CreateMode createMode) throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.create(path, data, acl, createMode); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + if (exists(path, false) != null) { + return path; + } + } catch (KeeperException.NodeExistsException e) { + return path; + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public void delete(String path, int version) throws InterruptedException, + KeeperException { + int count = 0; + do { + try { + super.delete(path, version); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + if (exists(path, false) == null) { + return; + } + } catch (KeeperException.NoNodeException e) { + break; + } + } while (!closed && (limit == -1 || count++ < limit)); + } + + @Override + public Stat exists(String path, boolean watch) throws KeeperException, + InterruptedException { + int count = 0; + do { + try { + return super.exists(path, watch ? watcher : null); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public Stat exists(String path, Watcher watcher) throws KeeperException, + InterruptedException { + int count = 0; + do { + try { + return super.exists(path, watcher); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public List getACL(String path, Stat stat) throws KeeperException, + InterruptedException { + int count = 0; + do { + try { + return super.getACL(path, stat); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public List getChildren(String path, boolean watch) + throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.getChildren(path, watch ? watcher : null); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return new ArrayList(); + } + + @Override + public List getChildren(String path, Watcher watcher) + throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.getChildren(path, watcher); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return new ArrayList(); + } + + @Override + public byte[] getData(String path, boolean watch, Stat stat) + throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.getData(path, watch ? watcher : null, stat); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public byte[] getData(String path, Watcher watcher, Stat stat) + throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.getData(path, watcher, stat); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public Stat setACL(String path, List acl, int version) + throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.setACL(path, acl, version); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + Stat s = exists(path, false); + if (s != null) { + if (getACL(path, s).equals(acl)) { + return s; + } + } else { + return null; + } + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + @Override + public Stat setData(String path, byte[] data, int version) + throws KeeperException, InterruptedException { + int count = 0; + do { + try { + return super.setData(path, data, version); + } catch (KeeperException.ConnectionLossException e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + Stat s = exists(path, false); + if (s != null) { + if (getData(path, false, s) == data) { + return s; + } + } else { + return null; + } + } + } while (!closed && (limit == -1 || count++ < limit)); + return null; + } + + /** + * @param limit + */ + public void setRetryLimit(int limit) { + this.limit = limit; + } + + /** + * @return true if successfully connected to zookeeper + */ + public boolean testConnection() { + int count = 0; + do { + try { + return super.exists("/", null) != null; + } catch (Exception e) { + LoggerFactory.getLogger().warn( + "ZooKeeper connection lost. Trying to reconnect."); + } + } while (count++ < 5); + return false; + } + +} diff --git a/src/contrib/zooinspector/zooInspector-dev.sh b/src/contrib/zooinspector/zooInspector-dev.sh index a57b0beba8e..50472040ad1 100755 --- a/src/contrib/zooinspector/zooInspector-dev.sh +++ b/src/contrib/zooinspector/zooInspector-dev.sh @@ -1,18 +1,18 @@ -#!/bin/sh - -# 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. - +#!/bin/sh + +# 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. + java -cp ../../../build/contrib/ZooInspector/zookeeper-dev-ZooInspector.jar:../../../build/lib/log4j-1.2.15.jar:lib/zookeeper-3.3.0.jar:lib/jtoaster-1.0.4.jar:lib org.apache.zookeeper.inspector.ZooInspector \ No newline at end of file diff --git a/src/contrib/zooinspector/zooInspector.cmd b/src/contrib/zooinspector/zooInspector.cmd index 0b298a21f8c..4fa3ab23374 100644 --- a/src/contrib/zooinspector/zooInspector.cmd +++ b/src/contrib/zooinspector/zooInspector.cmd @@ -1,18 +1,18 @@ -#!/bin/sh - -# 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. - +#!/bin/sh + +# 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. + java -cp zookeeper-dev-ZooInspector.jar;lib/log4j-1.2.15.jar;lib/zookeeper-3.3.0.jar;lib/jToaster-1.0.4.jar;lib org.apache.zookeeper.inspector.ZooInspector \ No newline at end of file diff --git a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java index 0ecab73d051..f94c54ddffd 100644 --- a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java +++ b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java @@ -1,128 +1,128 @@ -/** - * 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.server; - -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.zip.Adler32; -import java.util.zip.CheckedInputStream; - -import org.apache.jute.BinaryInputArchive; -import org.apache.jute.InputArchive; -import org.apache.zookeeper.data.StatPersisted; -import org.apache.zookeeper.server.persistence.FileSnap; - -/** - * Dump a snapshot file to stdout. - */ -public class SnapshotFormatter { - - /** - * USAGE: SnapshotFormatter snapshot_file - */ - public static void main(String[] args) throws Exception { - if (args.length != 1) { - System.err.println("USAGE: SnapshotFormatter snapshot_file"); - System.exit(2); - } - - new SnapshotFormatter().run(args[0]); - } - - public void run(String snapshotFileName) throws IOException { - InputStream is = new CheckedInputStream( - new BufferedInputStream(new FileInputStream(snapshotFileName)), - new Adler32()); - InputArchive ia = BinaryInputArchive.getArchive(is); - - FileSnap fileSnap = new FileSnap(null); - - DataTree dataTree = new DataTree(); - Map sessions = new HashMap(); - - fileSnap.deserialize(dataTree, sessions, ia); - - printDetails(dataTree, sessions); - } - - private void printDetails(DataTree dataTree, Map sessions) { - printZnodeDetails(dataTree); - printSessionDetails(dataTree, sessions); - } - - private void printZnodeDetails(DataTree dataTree) { - System.out.println(String.format("ZNode Details (count=%d):", - dataTree.getNodeCount())); - - printZnode(dataTree, "/"); - System.out.println("----"); - } - - private void printZnode(DataTree dataTree, String name) { - System.out.println("----"); - DataNode n = dataTree.getNode(name); - Set children; - synchronized(n) { // keep findbugs happy - System.out.println(name); - printStat(n.stat); - if (n.data != null) { - System.out.println(" dataLength = " + n.data.length); - } else { - System.out.println(" no data"); - } - children = n.getChildren(); - } - if (children != null) { - for (String child : children) { - printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child); - } - } - } - - private void printSessionDetails(DataTree dataTree, Map sessions) { - System.out.println("Session Details (sid, timeout, ephemeralCount):"); - for (Map.Entry e : sessions.entrySet()) { - long sid = e.getKey(); - System.out.println(String.format("%#016x, %d, %d", - sid, e.getValue(), dataTree.getEphemerals(sid).size())); - } - } - - private void printStat(StatPersisted stat) { - printHex("cZxid", stat.getCzxid()); - System.out.println(" ctime = " + new Date(stat.getCtime()).toString()); - printHex("mZxid", stat.getMzxid()); - System.out.println(" mtime = " + new Date(stat.getMtime()).toString()); - printHex("pZxid", stat.getPzxid()); - System.out.println(" cversion = " + stat.getCversion()); - System.out.println(" dataVersion = " + stat.getVersion()); - System.out.println(" aclVersion = " + stat.getAversion()); - printHex("ephemeralOwner", stat.getEphemeralOwner()); - } - - private void printHex(String prefix, long value) { - System.out.println(String.format(" %s = %#016x", prefix, value)); - } -} +/** + * 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.server; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.zip.Adler32; +import java.util.zip.CheckedInputStream; + +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.InputArchive; +import org.apache.zookeeper.data.StatPersisted; +import org.apache.zookeeper.server.persistence.FileSnap; + +/** + * Dump a snapshot file to stdout. + */ +public class SnapshotFormatter { + + /** + * USAGE: SnapshotFormatter snapshot_file + */ + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("USAGE: SnapshotFormatter snapshot_file"); + System.exit(2); + } + + new SnapshotFormatter().run(args[0]); + } + + public void run(String snapshotFileName) throws IOException { + InputStream is = new CheckedInputStream( + new BufferedInputStream(new FileInputStream(snapshotFileName)), + new Adler32()); + InputArchive ia = BinaryInputArchive.getArchive(is); + + FileSnap fileSnap = new FileSnap(null); + + DataTree dataTree = new DataTree(); + Map sessions = new HashMap(); + + fileSnap.deserialize(dataTree, sessions, ia); + + printDetails(dataTree, sessions); + } + + private void printDetails(DataTree dataTree, Map sessions) { + printZnodeDetails(dataTree); + printSessionDetails(dataTree, sessions); + } + + private void printZnodeDetails(DataTree dataTree) { + System.out.println(String.format("ZNode Details (count=%d):", + dataTree.getNodeCount())); + + printZnode(dataTree, "/"); + System.out.println("----"); + } + + private void printZnode(DataTree dataTree, String name) { + System.out.println("----"); + DataNode n = dataTree.getNode(name); + Set children; + synchronized(n) { // keep findbugs happy + System.out.println(name); + printStat(n.stat); + if (n.data != null) { + System.out.println(" dataLength = " + n.data.length); + } else { + System.out.println(" no data"); + } + children = n.getChildren(); + } + if (children != null) { + for (String child : children) { + printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child); + } + } + } + + private void printSessionDetails(DataTree dataTree, Map sessions) { + System.out.println("Session Details (sid, timeout, ephemeralCount):"); + for (Map.Entry e : sessions.entrySet()) { + long sid = e.getKey(); + System.out.println(String.format("%#016x, %d, %d", + sid, e.getValue(), dataTree.getEphemerals(sid).size())); + } + } + + private void printStat(StatPersisted stat) { + printHex("cZxid", stat.getCzxid()); + System.out.println(" ctime = " + new Date(stat.getCtime()).toString()); + printHex("mZxid", stat.getMzxid()); + System.out.println(" mtime = " + new Date(stat.getMtime()).toString()); + printHex("pZxid", stat.getPzxid()); + System.out.println(" cversion = " + stat.getCversion()); + System.out.println(" dataVersion = " + stat.getVersion()); + System.out.println(" aclVersion = " + stat.getAversion()); + printHex("ephemeralOwner", stat.getEphemeralOwner()); + } + + private void printHex(String prefix, long value) { + System.out.println(String.format(" %s = %#016x", prefix, value)); + } +} diff --git a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java index d5769cb8903..67b9fc92c89 100644 --- a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java +++ b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java @@ -1,131 +1,131 @@ -/** - * 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.server; - -import static org.junit.Assert.*; - -import java.io.File; -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.util.concurrent.CountDownLatch; - -import org.apache.zookeeper.PortAssignment; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.KeeperException.SessionExpiredException; -import org.apache.zookeeper.KeeperException.SessionMovedException; -import org.apache.zookeeper.ZooDefs.OpCode; -import org.apache.zookeeper.server.PrepRequestProcessor; -import org.apache.zookeeper.server.Request; -import org.apache.zookeeper.server.RequestProcessor; -import org.apache.zookeeper.server.ServerCnxnFactory; -import org.apache.zookeeper.server.SyncRequestProcessor; -import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.test.ClientBase; -import org.apache.zookeeper.txn.ErrorTxn; -import org.junit.Assert; -import org.junit.Test; - -public class PrepRequestProcessorTest extends ClientBase { - private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); - private static final int CONNECTION_TIMEOUT = 3000; - private final CountDownLatch testEnd = new CountDownLatch(1); - - @Test - public void testPRequest() throws Exception { - File tmpDir = ClientBase.createTmpDir(); - ClientBase.setupTestEnv(); - ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); - SyncRequestProcessor.setSnapCount(100); - final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); - ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); - f.startup(zks); - Assert.assertTrue("waiting for server being up ", - ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); - zks.sessionTracker = new MySessionTracker(); - PrepRequestProcessor processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); - Request foo = new Request(null, 1l, 1, OpCode.create, ByteBuffer.allocate(3), null); - processor.pRequest(foo); - testEnd.await(5, java.util.concurrent.TimeUnit.SECONDS); - f.shutdown(); - zks.shutdown(); - } - - - private class MyRequestProcessor implements RequestProcessor { - @Override - public void processRequest(Request request) { - Assert.assertEquals("Request should have marshalling error", new ErrorTxn(Code.MARSHALLINGERROR.intValue()), request.txn); - testEnd.countDown(); - } - @Override - public void shutdown() { - // TODO Auto-generated method stub - - } - } - - private class MySessionTracker implements SessionTracker { - @Override - public void addSession(long id, int to) { - // TODO Auto-generated method stub - - } - @Override - public void checkSession(long sessionId, Object owner) - throws SessionExpiredException, SessionMovedException { - // TODO Auto-generated method stub - - } - @Override - public long createSession(int sessionTimeout) { - // TODO Auto-generated method stub - return 0; - } - @Override - public void dumpSessions(PrintWriter pwriter) { - // TODO Auto-generated method stub - - } - @Override - public void removeSession(long sessionId) { - // TODO Auto-generated method stub - - } - @Override - public void setOwner(long id, Object owner) - throws SessionExpiredException { - // TODO Auto-generated method stub - - } - @Override - public void shutdown() { - // TODO Auto-generated method stub - - } - @Override - public boolean touchSession(long sessionId, int sessionTimeout) { - // TODO Auto-generated method stub - return false; - } - @Override - public void setSessionClosing(long sessionId) { - // TODO Auto-generated method stub - } - } -} +/** + * 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.server; + +import static org.junit.Assert.*; + +import java.io.File; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; + +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.KeeperException.Code; +import org.apache.zookeeper.KeeperException.SessionExpiredException; +import org.apache.zookeeper.KeeperException.SessionMovedException; +import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.server.PrepRequestProcessor; +import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.RequestProcessor; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.SyncRequestProcessor; +import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.txn.ErrorTxn; +import org.junit.Assert; +import org.junit.Test; + +public class PrepRequestProcessorTest extends ClientBase { + private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); + private static final int CONNECTION_TIMEOUT = 3000; + private final CountDownLatch testEnd = new CountDownLatch(1); + + @Test + public void testPRequest() throws Exception { + File tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(100); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); + zks.sessionTracker = new MySessionTracker(); + PrepRequestProcessor processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); + Request foo = new Request(null, 1l, 1, OpCode.create, ByteBuffer.allocate(3), null); + processor.pRequest(foo); + testEnd.await(5, java.util.concurrent.TimeUnit.SECONDS); + f.shutdown(); + zks.shutdown(); + } + + + private class MyRequestProcessor implements RequestProcessor { + @Override + public void processRequest(Request request) { + Assert.assertEquals("Request should have marshalling error", new ErrorTxn(Code.MARSHALLINGERROR.intValue()), request.txn); + testEnd.countDown(); + } + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + } + + private class MySessionTracker implements SessionTracker { + @Override + public void addSession(long id, int to) { + // TODO Auto-generated method stub + + } + @Override + public void checkSession(long sessionId, Object owner) + throws SessionExpiredException, SessionMovedException { + // TODO Auto-generated method stub + + } + @Override + public long createSession(int sessionTimeout) { + // TODO Auto-generated method stub + return 0; + } + @Override + public void dumpSessions(PrintWriter pwriter) { + // TODO Auto-generated method stub + + } + @Override + public void removeSession(long sessionId) { + // TODO Auto-generated method stub + + } + @Override + public void setOwner(long id, Object owner) + throws SessionExpiredException { + // TODO Auto-generated method stub + + } + @Override + public void shutdown() { + // TODO Auto-generated method stub + + } + @Override + public boolean touchSession(long sessionId, int sessionTimeout) { + // TODO Auto-generated method stub + return false; + } + @Override + public void setSessionClosing(long sessionId) { + // TODO Auto-generated method stub + } + } +} diff --git a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java index ed22cca4193..1712da2add4 100644 --- a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java @@ -1,105 +1,105 @@ -/** - * 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.test; - -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.HashMap; - -import org.apache.zookeeper.server.quorum.FastLeaderElection; -import org.apache.zookeeper.server.quorum.QuorumCnxManager; -import org.apache.zookeeper.server.quorum.QuorumPeer; -import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; -import org.apache.zookeeper.PortAssignment; -import org.apache.zookeeper.ZKTestCase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.junit.Assert; -import org.junit.Test; - -public class FLEPredicateTest extends ZKTestCase { - - protected static final Logger LOG = LoggerFactory.getLogger(FLEPredicateTest.class); - - class MockFLE extends FastLeaderElection { - MockFLE(QuorumPeer peer){ - super(peer, new QuorumCnxManager(peer)); - } - - boolean predicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch){ - return this.totalOrderPredicate(newId, newZxid, newEpoch, curId, curZxid, curEpoch); - } - } - - - HashMap peers; - - @Test - public void testPredicate() throws IOException { - - peers = new HashMap(3); - - /* - * Creates list of peers. - */ - for(int i = 0; i < 3; i++) { - peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); - } - - /* - * Creating peer. - */ - try{ - File tmpDir = ClientBase.createTmpDir(); - QuorumPeer peer = new QuorumPeer(peers, tmpDir, tmpDir, - PortAssignment.unique(), 3, 0, 1000, 2, 2); - - MockFLE mock = new MockFLE(peer); - - /* - * Lower epoch must return false - */ - - Assert.assertFalse (mock.predicate(4L, 0L, 0L, 3L, 0L, 2L)); - - /* - * Later epoch - */ - Assert.assertTrue (mock.predicate(0L, 0L, 1L, 1L, 0L, 0L)); - - /* - * Higher zxid - */ - Assert.assertTrue(mock.predicate(0L, 1L, 0L, 1L, 0L, 0L)); - - /* - * Higher id - */ - Assert.assertTrue(mock.predicate(1L, 1L, 0L, 0L, 1L, 0L)); - } catch (IOException e) { - LOG.error("Exception while creating quorum peer", e); - Assert.fail("Exception while creating quorum peer"); - } - } -} +/** + * 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.test; + +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.HashMap; + +import org.apache.zookeeper.server.quorum.FastLeaderElection; +import org.apache.zookeeper.server.quorum.QuorumCnxManager; +import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKTestCase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.junit.Assert; +import org.junit.Test; + +public class FLEPredicateTest extends ZKTestCase { + + protected static final Logger LOG = LoggerFactory.getLogger(FLEPredicateTest.class); + + class MockFLE extends FastLeaderElection { + MockFLE(QuorumPeer peer){ + super(peer, new QuorumCnxManager(peer)); + } + + boolean predicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch){ + return this.totalOrderPredicate(newId, newZxid, newEpoch, curId, curZxid, curEpoch); + } + } + + + HashMap peers; + + @Test + public void testPredicate() throws IOException { + + peers = new HashMap(3); + + /* + * Creates list of peers. + */ + for(int i = 0; i < 3; i++) { + peers.put(Long.valueOf(i), + new QuorumServer(i, + new InetSocketAddress(PortAssignment.unique()), + new InetSocketAddress(PortAssignment.unique()))); + } + + /* + * Creating peer. + */ + try{ + File tmpDir = ClientBase.createTmpDir(); + QuorumPeer peer = new QuorumPeer(peers, tmpDir, tmpDir, + PortAssignment.unique(), 3, 0, 1000, 2, 2); + + MockFLE mock = new MockFLE(peer); + + /* + * Lower epoch must return false + */ + + Assert.assertFalse (mock.predicate(4L, 0L, 0L, 3L, 0L, 2L)); + + /* + * Later epoch + */ + Assert.assertTrue (mock.predicate(0L, 0L, 1L, 1L, 0L, 0L)); + + /* + * Higher zxid + */ + Assert.assertTrue(mock.predicate(0L, 1L, 0L, 1L, 0L, 0L)); + + /* + * Higher id + */ + Assert.assertTrue(mock.predicate(1L, 1L, 0L, 0L, 1L, 0L)); + } catch (IOException e) { + LOG.error("Exception while creating quorum peer", e); + Assert.fail("Exception while creating quorum peer"); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/test/ObserverLETest.java b/src/java/test/org/apache/zookeeper/test/ObserverLETest.java index ef80cca914e..123ba0be0bd 100644 --- a/src/java/test/org/apache/zookeeper/test/ObserverLETest.java +++ b/src/java/test/org/apache/zookeeper/test/ObserverLETest.java @@ -1,76 +1,76 @@ -/** - * 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.test; - -import static org.junit.Assert.*; - -import java.util.Arrays; - -import org.apache.zookeeper.ZKTestCase; -import org.apache.zookeeper.server.quorum.QuorumPeer; -import org.apache.zookeeper.server.quorum.QuorumStats; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class ObserverLETest extends ZKTestCase { - final QuorumBase qb = new QuorumBase(); - final ClientTest ct = new ClientTest(); - - @Before - public void establishThreeParticipantOneObserverEnsemble() throws Exception { - qb.setUp(true); - ct.hostPort = qb.hostPort; - ct.setUpAll(); - qb.s5.shutdown(); - } - - @After - public void shutdownQuorum() throws Exception { - ct.tearDownAll(); - qb.tearDown(); - } - - /** - * See ZOOKEEPER-1294. Confirms that an observer will not support the quorum - * of a leader by forming a 5-node, 2-observer ensemble (so quorum size is 2). - * When all but the leader and one observer are shut down, the leader should - * enter the 'looking' state, not stay in the 'leading' state. - */ - @Test - public void testLEWithObserver() throws Exception { - QuorumPeer leader = null; - for (QuorumPeer server : Arrays.asList(qb.s1, qb.s2, qb.s3)) { - if (server.getServerState().equals( - QuorumStats.Provider.FOLLOWING_STATE)) { - server.shutdown(); - assertTrue("Waiting for server down", ClientBase - .waitForServerDown("127.0.0.1:" - + server.getClientPort(), - ClientBase.CONNECTION_TIMEOUT)); - } else { - assertNull("More than one leader found", leader); - leader = server; - } - } - assertTrue("Leader is not in Looking state", ClientBase - .waitForServerState(leader, ClientBase.CONNECTION_TIMEOUT, - QuorumStats.Provider.LOOKING_STATE)); - } - -} +/** + * 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.test; + +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.quorum.QuorumStats; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ObserverLETest extends ZKTestCase { + final QuorumBase qb = new QuorumBase(); + final ClientTest ct = new ClientTest(); + + @Before + public void establishThreeParticipantOneObserverEnsemble() throws Exception { + qb.setUp(true); + ct.hostPort = qb.hostPort; + ct.setUpAll(); + qb.s5.shutdown(); + } + + @After + public void shutdownQuorum() throws Exception { + ct.tearDownAll(); + qb.tearDown(); + } + + /** + * See ZOOKEEPER-1294. Confirms that an observer will not support the quorum + * of a leader by forming a 5-node, 2-observer ensemble (so quorum size is 2). + * When all but the leader and one observer are shut down, the leader should + * enter the 'looking' state, not stay in the 'leading' state. + */ + @Test + public void testLEWithObserver() throws Exception { + QuorumPeer leader = null; + for (QuorumPeer server : Arrays.asList(qb.s1, qb.s2, qb.s3)) { + if (server.getServerState().equals( + QuorumStats.Provider.FOLLOWING_STATE)) { + server.shutdown(); + assertTrue("Waiting for server down", ClientBase + .waitForServerDown("127.0.0.1:" + + server.getClientPort(), + ClientBase.CONNECTION_TIMEOUT)); + } else { + assertNull("More than one leader found", leader); + leader = server; + } + } + assertTrue("Leader is not in Looking state", ClientBase + .waitForServerState(leader, ClientBase.CONNECTION_TIMEOUT, + QuorumStats.Provider.LOOKING_STATE)); + } + +} diff --git a/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java b/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java index 104ec77fdda..3a0b2ee6018 100644 --- a/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java +++ b/src/java/test/org/apache/zookeeper/test/SessionInvalidationTest.java @@ -1,105 +1,105 @@ -/** - * 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.test; - -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; - -import junit.framework.Assert; - -import org.apache.jute.BinaryOutputArchive; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.ZooDefs.OpCode; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.proto.ConnectRequest; -import org.apache.zookeeper.proto.CreateRequest; -import org.apache.zookeeper.proto.RequestHeader; -import org.junit.Test; - -public class SessionInvalidationTest extends ClientBase { - /** - * Test solution for ZOOKEEPER-1208. Verify that operations are not - * accepted after a close session. - * - * We're using our own marshalling here in order to force an operation - * after the session is closed (ZooKeeper.class will not allow this). Also - * by filling the pipe with operations it increases the likelyhood that - * the server will process the create before FinalRequestProcessor - * removes the session from the tracker. - */ - @Test - public void testCreateAfterCloseShouldFail() throws Exception { - for (int i = 0; i < 10; i++) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); - - // open a connection - boa.writeInt(44, "len"); - ConnectRequest conReq = new ConnectRequest(0, 0, 30000, 0, new byte[16]); - conReq.serialize(boa, "connect"); - - // close connection - boa.writeInt(8, "len"); - RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.closeSession); - h.serialize(boa, "header"); - - // create ephemeral znode - boa.writeInt(52, "len"); // We'll fill this in later - RequestHeader header = new RequestHeader(2, OpCode.create); - header.serialize(boa, "header"); - CreateRequest createReq = new CreateRequest("/foo" + i, new byte[0], - Ids.OPEN_ACL_UNSAFE, 1); - createReq.serialize(boa, "request"); - baos.close(); - - System.out.println("Length:" + baos.toByteArray().length); - - String hp[] = hostPort.split(":"); - Socket sock = new Socket(hp[0], Integer.parseInt(hp[1])); - InputStream resultStream = null; - try { - OutputStream outstream = sock.getOutputStream(); - byte[] data = baos.toByteArray(); - outstream.write(data); - outstream.flush(); - - resultStream = sock.getInputStream(); - byte[] b = new byte[10000]; - int len; - while ((len = resultStream.read(b)) >= 0) { - // got results - System.out.println("gotlen:" + len); - } - } finally { - if (resultStream != null) { - resultStream.close(); - } - sock.close(); - } - } - - ZooKeeper zk = createClient(); - Assert.assertEquals(1, zk.getChildren("/", false).size()); - - zk.close(); - } -} +/** + * 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.test; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import junit.framework.Assert; + +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.proto.ConnectRequest; +import org.apache.zookeeper.proto.CreateRequest; +import org.apache.zookeeper.proto.RequestHeader; +import org.junit.Test; + +public class SessionInvalidationTest extends ClientBase { + /** + * Test solution for ZOOKEEPER-1208. Verify that operations are not + * accepted after a close session. + * + * We're using our own marshalling here in order to force an operation + * after the session is closed (ZooKeeper.class will not allow this). Also + * by filling the pipe with operations it increases the likelyhood that + * the server will process the create before FinalRequestProcessor + * removes the session from the tracker. + */ + @Test + public void testCreateAfterCloseShouldFail() throws Exception { + for (int i = 0; i < 10; i++) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); + + // open a connection + boa.writeInt(44, "len"); + ConnectRequest conReq = new ConnectRequest(0, 0, 30000, 0, new byte[16]); + conReq.serialize(boa, "connect"); + + // close connection + boa.writeInt(8, "len"); + RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.closeSession); + h.serialize(boa, "header"); + + // create ephemeral znode + boa.writeInt(52, "len"); // We'll fill this in later + RequestHeader header = new RequestHeader(2, OpCode.create); + header.serialize(boa, "header"); + CreateRequest createReq = new CreateRequest("/foo" + i, new byte[0], + Ids.OPEN_ACL_UNSAFE, 1); + createReq.serialize(boa, "request"); + baos.close(); + + System.out.println("Length:" + baos.toByteArray().length); + + String hp[] = hostPort.split(":"); + Socket sock = new Socket(hp[0], Integer.parseInt(hp[1])); + InputStream resultStream = null; + try { + OutputStream outstream = sock.getOutputStream(); + byte[] data = baos.toByteArray(); + outstream.write(data); + outstream.flush(); + + resultStream = sock.getInputStream(); + byte[] b = new byte[10000]; + int len; + while ((len = resultStream.read(b)) >= 0) { + // got results + System.out.println("gotlen:" + len); + } + } finally { + if (resultStream != null) { + resultStream.close(); + } + sock.close(); + } + } + + ZooKeeper zk = createClient(); + Assert.assertEquals(1, zk.getChildren("/", false).size()); + + zk.close(); + } +} From 0a9604eb598e22f9d672445317480fd79346dc79 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 4 Apr 2014 01:34:58 +0000 Subject: [PATCH 248/444] ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for source and text files (Raja Aluri via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1584502 13f79535-47bb-0310-9956-ffa450edef68 --- .gitattributes | 19 +++++++++++++++++++ CHANGES.txt | 5 +++++ 2 files changed, 24 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..af960483d22 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +# Auto detect text files and perform LF normalization +* text=auto + +*.cs text diff=csharp +*.java text diff=java +*.html text diff=html +*.py text diff=python +*.pl text diff=perl +*.pm text diff=perl +*.css text +*.js text +*.sql text + +*.sh text eol=lf + +*.bat text eol=crlf +*.cmd text eol=crlf +*.csproj text merge=union eol=crlf +*.sln text merge=union eol=crlf diff --git a/CHANGES.txt b/CHANGES.txt index 7c33299bfbe..a5defe2dff6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,11 @@ BUGFIXES: ZOOKEEPER-1897. ZK Shell/Cli not processing commands (stack via michim) +IMPROVEMENTS: + + ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for + source and text files (Raja Aluri via michim) + Release 3.4.6 - 2014-03-10 From f50c899f129c30cc77df97408b98196ce309abb4 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 18 Apr 2014 22:33:46 +0000 Subject: [PATCH 249/444] ZOOKEEPER-1913. Invalid manifest files due to bogus revision property value (Raul Gutierrez Segales via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1588585 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index a5defe2dff6..43963c2c3bc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,9 @@ BUGFIXES: ZOOKEEPER-1897. ZK Shell/Cli not processing commands (stack via michim) + ZOOKEEPER-1913. Invalid manifest files due to bogus revision property value + (Raul Gutierrez Segales via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/build.xml b/build.xml index 98fec4fd34a..eedc168a08a 100644 --- a/build.xml +++ b/build.xml @@ -531,7 +531,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + outputproperty="revision" errorproperty="revision.error" failonerror="true"> From b688e8b3a9cdff1852c44f955a8c8d0703030905 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 25 Apr 2014 08:11:17 +0000 Subject: [PATCH 250/444] ZOOKEEPER-1911. REST contrib module does not include all required files when packaged (Sean Mackrory via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1589950 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/rest/build.xml | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 43963c2c3bc..b92ce12977c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,6 +19,9 @@ BUGFIXES: ZOOKEEPER-1913. Invalid manifest files due to bogus revision property value (Raul Gutierrez Segales via michim) + ZOOKEEPER-1911. REST contrib module does not include all required files when + packaged (Sean Mackrory via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/contrib/rest/build.xml b/src/contrib/rest/build.xml index 649dff75295..fb628bcd416 100644 --- a/src/contrib/rest/build.xml +++ b/src/contrib/rest/build.xml @@ -48,6 +48,22 @@ + + + + + + + + + + + + + + + + From c3d709bf61369a08f9da74a2467f223662ce9b2f Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 9 May 2014 21:39:08 +0000 Subject: [PATCH 251/444] ZOOKEEPER-1926. Unit tests should only use build/test/data for data (Enis Soztutar via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1593625 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/test/system/BaseSysTest.java | 4 ++- .../test/system/QuorumPeerInstance.java | 5 +++- .../zookeeper/server/quorum/LearnerTest.java | 5 +++- .../zookeeper/server/quorum/Zab1_0Test.java | 25 +++++++++++-------- 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b92ce12977c..6c468f23c79 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,6 +22,9 @@ BUGFIXES: ZOOKEEPER-1911. REST contrib module does not include all required files when packaged (Sean Mackrory via michim) + ZOOKEEPER-1926. Unit tests should only use build/test/data for data (Enis + Soztutar via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java b/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java index 575a743dce2..d033a46d7d6 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java +++ b/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java @@ -37,6 +37,8 @@ @Ignore("No tests in this class.") public class BaseSysTest extends TestCase { + private static final File testData = new File( + System.getProperty("test.data.dir", "build/test/data")); private static int fakeBasePort = 33222; private static String zkHostPort; protected String prefix = "/sysTest"; @@ -149,7 +151,7 @@ private void fakeConfigureServers(int count) throws IOException { } StringBuilder sb = new StringBuilder(); for(int i = 0; i < count; i++) { - qpsDirs[i] = File.createTempFile("sysTest", ".tmp"); + qpsDirs[i] = File.createTempFile("sysTest", ".tmp", testData); qpsDirs[i].delete(); qpsDirs[i].mkdir(); int port = fakeBasePort+10+i; diff --git a/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java b/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java index 99d928f958b..5fe308aa6f5 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java +++ b/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java @@ -36,6 +36,9 @@ class QuorumPeerInstance implements Instance { final private static Logger LOG = LoggerFactory.getLogger(QuorumPeerInstance.class); + private static final File testData = new File( + System.getProperty("test.data.dir", "build/test/data")); + private static final int syncLimit = 3; private static final int initLimit = 3; private static final int tickTime = 2000; @@ -55,7 +58,7 @@ public void setReporter(Reporter r) { public QuorumPeerInstance() { try { - File tmpFile = File.createTempFile("test", ".dir"); + File tmpFile = File.createTempFile("test", ".dir", testData); File tmpDir = tmpFile.getParentFile(); tmpFile.delete(); File zkDirs = new File(tmpDir, "zktmp.cfg"); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java index 63b15e3a392..2ae57ce6a5b 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java @@ -47,6 +47,9 @@ import org.junit.Test; public class LearnerTest extends ZKTestCase { + private static final File testData = new File( + System.getProperty("test.data.dir", "build/test/data")); + class SimpleLearnerZooKeeperServer extends LearnerZooKeeperServer { boolean startupCalled; @@ -84,7 +87,7 @@ static private void recursiveDelete(File dir) { } @Test public void syncTest() throws Exception { - File tmpFile = File.createTempFile("test", ".dir"); + File tmpFile = File.createTempFile("test", ".dir", testData); tmpFile.delete(); try { FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpFile, tmpFile); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 41fcce97e52..6b5d410d104 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -80,6 +80,9 @@ public class Zab1_0Test { private static final Logger LOG = LoggerFactory.getLogger(Zab1_0Test.class); + private static final File testData = new File( + System.getProperty("test.data.dir", "build/test/data")); + private static final class LeadThread extends Thread { private final Leader leader; @@ -149,7 +152,7 @@ public void run() { } @Test public void testLeaderInConnectingFollowers() throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Leader leader = null; @@ -203,7 +206,7 @@ public void testLeaderInConnectingFollowers() throws Exception { @Test public void testLastAcceptedEpoch() throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Leader leader = null; @@ -241,7 +244,7 @@ public void testLastAcceptedEpoch() throws Exception { @Test public void testLeaderInElectingFollowers() throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Leader leader = null; @@ -348,7 +351,7 @@ public void testLeaderConversation(LeaderConversation conversation) throws Excep Socket pair[] = getSocketPair(); Socket leaderSocket = pair[0]; Socket followerSocket = pair[1]; - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); LeadThread leadThread = null; @@ -390,7 +393,7 @@ public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversa Socket pair[] = getSocketPair(); Socket leaderSocket = pair[0]; Socket followerSocket = pair[1]; - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); LeadThread leadThread = null; @@ -457,7 +460,7 @@ public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversa public void testFollowerConversation(FollowerConversation conversation) throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Thread followerThread = null; @@ -509,7 +512,7 @@ public void run() { } public void testObserverConversation(ObserverConversation conversation) throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Thread observerThread = null; @@ -629,7 +632,7 @@ public void testNormalFollowerRun() throws Exception { @Override public void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile(); @@ -757,7 +760,7 @@ public void testNormalFollowerRunWithDiff() throws Exception { @Override public void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile(); @@ -995,7 +998,7 @@ public void testNormalObserverRun() throws Exception { @Override public void converseWithObserver(InputArchive ia, OutputArchive oa, Observer o) throws Exception { - File tmpDir = File.createTempFile("test", "dir"); + File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = o.zk.getTxnLogFactory().getDataDir().getParentFile(); @@ -1413,7 +1416,7 @@ private String readContentsOfFile(File f) throws IOException { @Test public void testInitialAcceptedCurrent() throws Exception { - File tmpDir = File.createTempFile("test", ".dir"); + File tmpDir = File.createTempFile("test", ".dir", testData); tmpDir.delete(); tmpDir.mkdir(); try { From 50c872c86659eee9e32a2e8bbd8dca921d5ed115 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Thu, 15 May 2014 01:37:13 +0000 Subject: [PATCH 252/444] ZOOKEEPER-657. Cut down the running time of ZKDatabase corruption (Michi Mutsuzaki via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1594756 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../zookeeper/test/ZkDatabaseCorruptionTest.java | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6c468f23c79..e94155c5d48 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,6 +30,8 @@ IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for source and text files (Raja Aluri via michim) + ZOOKEEPER-657. Cut down the running time of ZKDatabase corruption + (Michi Mutsuzaki via rakeshr) Release 3.4.6 - 2014-03-10 diff --git a/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java b/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java index 5fdbd4dd8f3..c213b2a26f4 100644 --- a/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java +++ b/src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java @@ -23,6 +23,7 @@ import java.io.RandomAccessFile; import java.util.Arrays; +import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; @@ -72,6 +73,13 @@ private void corruptAllSnapshots(File snapDir) throws IOException { } } + private class NoopStringCallback implements AsyncCallback.StringCallback { + @Override + public void processResult(int rc, String path, Object ctx, + String name) { + } + } + @Test public void testCorruption() throws Exception { ClientBase.waitForServerUp(qb.hostPort, 10000); @@ -81,7 +89,8 @@ public void process(WatchedEvent event) { }}); SyncRequestProcessor.setSnapCount(100); for (int i = 0; i < 2000; i++) { - zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT, new NoopStringCallback(), null); } zk.close(); @@ -136,7 +145,8 @@ public void process(WatchedEvent event) { zk = qb.createClient(); SyncRequestProcessor.setSnapCount(100); for (int i = 2000; i < 4000; i++) { - zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT, new NoopStringCallback(), null); } zk.close(); From cea57ef610966c61ad4f3f0e9490672a0621f37f Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 16 May 2014 17:57:17 +0000 Subject: [PATCH 253/444] ZOOKEEPER-1895. update all notice files, copyright, etc... with the new year - 2014 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1595275 13f79535-47bb-0310-9956-ffa450edef68 --- NOTICE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTICE.txt b/NOTICE.txt index 35add968965..5689ab64643 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2012 The Apache Software Foundation +Copyright 2009-2014 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). From 084803d257c58c908108d2c30e1095af8a0e632a Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 16 May 2014 22:32:21 +0000 Subject: [PATCH 254/444] ZOOKEEPER-1062. Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait (Botond Hejj via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1595375 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/zkperl/ZooKeeper.xs | 19 +++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e94155c5d48..f0a07f58264 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -25,6 +25,9 @@ BUGFIXES: ZOOKEEPER-1926. Unit tests should only use build/test/data for data (Enis Soztutar via michim) + ZOOKEEPER-1062. Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait + (Botond Hejj via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/contrib/zkperl/ZooKeeper.xs b/src/contrib/zkperl/ZooKeeper.xs index 2b475e1e5dc..f65e076b2a3 100644 --- a/src/contrib/zkperl/ZooKeeper.xs +++ b/src/contrib/zkperl/ZooKeeper.xs @@ -2604,6 +2604,7 @@ zkw_wait(zkwh, ...) unsigned int timeout; struct timeval end_timeval; int i, done; + struct timespec wait_timespec; PPCODE: watch = _zkw_get_handle_outer(aTHX_ zkwh, NULL); @@ -2630,25 +2631,19 @@ zkw_wait(zkwh, ...) end_timeval.tv_sec += timeout / 1000; end_timeval.tv_usec += (timeout % 1000) * 1000; + wait_timespec.tv_sec = end_timeval.tv_sec; + wait_timespec.tv_nsec = end_timeval.tv_usec * 1000; + pthread_mutex_lock(&watch->mutex); while (!watch->done) { struct timeval curr_timeval; - struct timespec wait_timespec; gettimeofday(&curr_timeval, NULL); - wait_timespec.tv_sec = end_timeval.tv_sec - curr_timeval.tv_sec; - wait_timespec.tv_nsec = - (end_timeval.tv_usec - curr_timeval.tv_usec) * 1000; - - if (wait_timespec.tv_nsec < 0) { - --wait_timespec.tv_sec; - wait_timespec.tv_nsec += 1000000000; - } - - if (wait_timespec.tv_sec < 0 || - (wait_timespec.tv_sec == 0 && wait_timespec.tv_nsec <= 0)) { + if (end_timeval.tv_sec < curr_timeval.tv_sec || + (end_timeval.tv_sec == curr_timeval.tv_sec && + end_timeval.tv_usec <= curr_timeval.tv_usec)) { break; } From 2ac31e78184ff27f3e166ac52b8b88462c129145 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sun, 18 May 2014 01:04:08 +0000 Subject: [PATCH 255/444] ZOOKEEPER-1797. PurgeTxnLog may delete data logs during roll (Rakesh R via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1595553 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../apache/zookeeper/server/PurgeTxnLog.java | 49 ++- .../apache/zookeeper/server/PurgeTxnTest.java | 371 ++++++++++++++++++ .../apache/zookeeper/test/PurgeTxnTest.java | 88 ----- 4 files changed, 402 insertions(+), 109 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java diff --git a/CHANGES.txt b/CHANGES.txt index f0a07f58264..480b3a1d3bd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -28,6 +28,9 @@ BUGFIXES: ZOOKEEPER-1062. Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait (Botond Hejj via michim) + ZOOKEEPER-1797. PurgeTxnLog may delete data logs during roll (Rakesh R via + michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java index 185c1e123cf..86c78ba75bb 100644 --- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java @@ -24,9 +24,7 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,10 +49,16 @@ static void printUsage(){ System.out.println("\tcount -- the number of old snaps/logs you want to keep"); System.exit(1); } - + + private static final String PREFIX_SNAPSHOT = "snapshot"; + private static final String PREFIX_LOG = "log"; + /** - * purges the snapshot and logs keeping the last num snapshots - * and the corresponding logs. + * Purges the snapshot and logs keeping the last num snapshots and the + * corresponding logs. If logs are rolling or a new snapshot is created + * during this process, these newest N snapshots or any data logs will be + * excluded from current purging cycle. + * * @param dataDir the dir that has the logs * @param snapDir the dir that has the snapshots * @param num the number of snapshots to keep @@ -66,38 +70,41 @@ public static void purge(File dataDir, File snapDir, int num) throws IOException } FileTxnSnapLog txnLog = new FileTxnSnapLog(dataDir, snapDir); - - // found any valid recent snapshots? - - // files to exclude from deletion - Set exc=new HashSet(); + List snaps = txnLog.findNRecentSnapshots(num); - if (snaps.size() == 0) + retainNRecentSnapshots(txnLog, snaps); + } + + // VisibleForTesting + static void retainNRecentSnapshots(FileTxnSnapLog txnLog, List snaps) { + // found any valid recent snapshots? + if (snaps.size() == 0) return; File snapShot = snaps.get(snaps.size() -1); - for (File f: snaps) { - exc.add(f); - } - long zxid = Util.getZxidFromName(snapShot.getName(),"snapshot"); - exc.addAll(Arrays.asList(txnLog.getSnapshotLogs(zxid))); + final long leastZxidToBeRetain = Util.getZxidFromName( + snapShot.getName(), PREFIX_SNAPSHOT); - final Set exclude=exc; class MyFileFilter implements FileFilter{ private final String prefix; MyFileFilter(String prefix){ this.prefix=prefix; } public boolean accept(File f){ - if(!f.getName().startsWith(prefix) || exclude.contains(f)) + if(!f.getName().startsWith(prefix + ".")) + return false; + long fZxid = Util.getZxidFromName(f.getName(), prefix); + if (fZxid >= leastZxidToBeRetain) { return false; + } return true; } } // add all non-excluded log files - List files=new ArrayList( - Arrays.asList(txnLog.getDataDir().listFiles(new MyFileFilter("log.")))); + List files = new ArrayList(Arrays.asList(txnLog + .getDataDir().listFiles(new MyFileFilter(PREFIX_LOG)))); // add all non-excluded snapshot files to the deletion list - files.addAll(Arrays.asList(txnLog.getSnapDir().listFiles(new MyFileFilter("snapshot.")))); + files.addAll(Arrays.asList(txnLog.getSnapDir().listFiles( + new MyFileFilter(PREFIX_SNAPSHOT)))); // remove the old files for(File f: files) { diff --git a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java new file mode 100644 index 00000000000..4a685ac93b0 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java @@ -0,0 +1,371 @@ +/** + * 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.server; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.PurgeTxnLog; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.SyncRequestProcessor; +import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.test.ClientBase; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PurgeTxnTest extends ZKTestCase implements Watcher { + private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnTest.class); + private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); + private static final int CONNECTION_TIMEOUT = 3000; + private static final long OP_TIMEOUT_IN_MILLIS = 90000; + private File tmpDir; + + @After + public void teardown() { + if (null != tmpDir) { + ClientBase.recursiveDelete(tmpDir); + } + } + + /** + * test the purge + * @throws Exception an exception might be thrown here + */ + @Test + public void testPurge() throws Exception { + tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(100); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); + ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); + try { + for (int i = 0; i< 2000; i++) { + zk.create("/invalidsnap-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + } finally { + zk.close(); + } + f.shutdown(); + zks.getTxnLogFactory().close(); + Assert.assertTrue("waiting for server to shutdown", + ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); + // now corrupt the snapshot + PurgeTxnLog.purge(tmpDir, tmpDir, 3); + FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpDir, tmpDir); + List listLogs = snaplog.findNRecentSnapshots(4); + int numSnaps = 0; + for (File ff: listLogs) { + if (ff.getName().startsWith("snapshot")) { + numSnaps++; + } + } + Assert.assertTrue("exactly 3 snapshots ", (numSnaps == 3)); + snaplog.close(); + zks.shutdown(); + } + + /** + * Tests purge when logs are rolling or a new snapshot is created, then + * these newer files should alse be excluded in the current cycle. + * + * For frequent snapshotting, configured SnapCount to 30. There are three + * threads which will create 1000 znodes each and simultaneously do purge + * call + */ + @Test + public void testPurgeWhenLogRollingInProgress() throws Exception { + tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + SyncRequestProcessor.setSnapCount(30); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); + final ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); + final CountDownLatch doPurge = new CountDownLatch(1); + final CountDownLatch purgeFinished = new CountDownLatch(1); + final AtomicBoolean opFailed = new AtomicBoolean(false); + new Thread() { + public void run() { + try { + doPurge.await(OP_TIMEOUT_IN_MILLIS / 2, + TimeUnit.MILLISECONDS); + PurgeTxnLog.purge(tmpDir, tmpDir, 3); + } catch (IOException ioe) { + LOG.error("Exception when purge", ioe); + opFailed.set(true); + } catch (InterruptedException ie) { + LOG.error("Exception when purge", ie); + opFailed.set(true); + } finally { + purgeFinished.countDown(); + } + }; + }.start(); + final int thCount = 3; + List znodes = manyClientOps(zk, doPurge, thCount, + "/invalidsnap"); + Assert.assertTrue("Purging is not finished!", purgeFinished.await( + OP_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS)); + Assert.assertFalse("Purging failed!", opFailed.get()); + for (String znode : znodes) { + try { + zk.getData(znode, false, null); + } catch (Exception ke) { + LOG.error("Unexpected exception when visiting znode!", ke); + Assert.fail("Unexpected exception when visiting znode!"); + } + } + zk.close(); + f.shutdown(); + zks.shutdown(); + zks.getTxnLogFactory().close(); + } + + /** + * Tests finding n recent snapshots from set of snapshots and data logs + */ + @Test + public void testFindNRecentSnapshots() throws Exception { + int nRecentSnap = 4; // n recent snap shots + int nRecentCount = 30; + int offset = 0; + tmpDir = ClientBase.createTmpDir(); + File version2 = new File(tmpDir.toString(), "version-2"); + Assert.assertTrue("Failed to create version_2 dir:" + version2.toString(), + version2.mkdir()); + List expectedNRecentSnapFiles = new ArrayList(); + int counter = offset + (2 * nRecentCount); + for (int i = 0; i < nRecentCount; i++) { + // simulate log file + File logFile = new File(version2 + "/log." + Long.toHexString(--counter)); + Assert.assertTrue("Failed to create log File:" + logFile.toString(), + logFile.createNewFile()); + // simulate snapshot file + File snapFile = new File(version2 + "/snapshot." + + Long.toHexString(--counter)); + Assert.assertTrue("Failed to create snap File:" + snapFile.toString(), + snapFile.createNewFile()); + // add the n recent snap files for assertion + if(i < nRecentSnap){ + expectedNRecentSnapFiles.add(snapFile); + } + } + + FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); + List nRecentSnapFiles = txnLog.findNRecentSnapshots(nRecentSnap); + txnLog.close(); + Assert.assertEquals("exactly 4 snapshots ", 4, + nRecentSnapFiles.size()); + expectedNRecentSnapFiles.removeAll(nRecentSnapFiles); + Assert.assertEquals("Didn't get the recent snap files", 0, + expectedNRecentSnapFiles.size()); + } + + /** + * Tests purge where the data directory contains old snapshots and data + * logs, newest snapshots and data logs, (newest + n) snapshots and data + * logs + */ + @Test + public void testSnapFilesGreaterThanToRetain() throws Exception { + int nRecentCount = 4; + int fileAboveRecentCount = 4; + int fileToPurgeCount = 2; + AtomicInteger offset = new AtomicInteger(0); + tmpDir = ClientBase.createTmpDir(); + File version2 = new File(tmpDir.toString(), "version-2"); + Assert.assertTrue("Failed to create version_2 dir:" + version2.toString(), + version2.mkdir()); + List snapsToPurge = new ArrayList(); + List logsToPurge = new ArrayList(); + List snaps = new ArrayList(); + List logs = new ArrayList(); + List snapsAboveRecentFiles = new ArrayList(); + List logsAboveRecentFiles = new ArrayList(); + createDataDirFiles(offset, fileToPurgeCount, version2, snapsToPurge, + logsToPurge); + createDataDirFiles(offset, nRecentCount, version2, snaps, logs); + createDataDirFiles(offset, fileAboveRecentCount, version2, + snapsAboveRecentFiles, logsAboveRecentFiles); + + FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); + PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps); + txnLog.close(); + verifyFilesAfterPurge(snapsToPurge, false); + verifyFilesAfterPurge(logsToPurge, false); + verifyFilesAfterPurge(snaps, true); + verifyFilesAfterPurge(logs, true); + verifyFilesAfterPurge(snapsAboveRecentFiles, true); + verifyFilesAfterPurge(logsAboveRecentFiles, true); + } + + /** + * Tests purge where the data directory contains snap files equals to the + * number of files to be retained + */ + @Test + public void testSnapFilesEqualsToRetain() throws Exception { + int nRecentCount = 3; + AtomicInteger offset = new AtomicInteger(0); + tmpDir = ClientBase.createTmpDir(); + File version2 = new File(tmpDir.toString(), "version-2"); + Assert.assertTrue("Failed to create version_2 dir:" + version2.toString(), + version2.mkdir()); + List snaps = new ArrayList(); + List logs = new ArrayList(); + createDataDirFiles(offset, nRecentCount, version2, snaps, logs); + + FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); + PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps); + txnLog.close(); + verifyFilesAfterPurge(snaps, true); + verifyFilesAfterPurge(logs, true); + } + + /** + * Tests purge where the data directory contains old snapshots and data + * logs, newest snapshots and data logs + */ + @Test + public void testSnapFilesLessThanToRetain() throws Exception { + int nRecentCount = 4; + int fileToPurgeCount = 2; + AtomicInteger offset = new AtomicInteger(0); + tmpDir = ClientBase.createTmpDir(); + File version2 = new File(tmpDir.toString(), "version-2"); + Assert.assertTrue("Failed to create version_2 dir:" + version2.toString(), + version2.mkdir()); + List snapsToPurge = new ArrayList(); + List logsToPurge = new ArrayList(); + List snaps = new ArrayList(); + List logs = new ArrayList(); + createDataDirFiles(offset, fileToPurgeCount, version2, snapsToPurge, + logsToPurge); + createDataDirFiles(offset, nRecentCount, version2, snaps, logs); + + FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); + PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps); + txnLog.close(); + verifyFilesAfterPurge(snapsToPurge, false); + verifyFilesAfterPurge(logsToPurge, false); + verifyFilesAfterPurge(snaps, true); + verifyFilesAfterPurge(logs, true); + } + + private void createDataDirFiles(AtomicInteger offset, int limit, + File version_2, List snaps, List logs) + throws IOException { + int counter = offset.get() + (2 * limit); + offset.set(counter); + for (int i = 0; i < limit; i++) { + // simulate log file + File logFile = new File(version_2 + "/log." + Long.toHexString(--counter)); + Assert.assertTrue("Failed to create log File:" + logFile.toString(), + logFile.createNewFile()); + logs.add(logFile); + // simulate snapshot file + File snapFile = new File(version_2 + "/snapshot." + + Long.toHexString(--counter)); + Assert.assertTrue("Failed to create snap File:" + snapFile.toString(), + snapFile.createNewFile()); + snaps.add(snapFile); + } + } + + private void verifyFilesAfterPurge(List logs, boolean exists) { + for (File file : logs) { + Assert.assertEquals("After purging, file " + file, exists, + file.exists()); + } + } + + private List manyClientOps(final ZooKeeper zk, + final CountDownLatch doPurge, int thCount, final String prefix) { + Thread[] ths = new Thread[thCount]; + final List znodes = Collections + .synchronizedList(new ArrayList()); + final CountDownLatch finished = new CountDownLatch(thCount); + for (int indx = 0; indx < thCount; indx++) { + final String myprefix = prefix + "-" + indx; + Thread th = new Thread() { + public void run() { + for (int i = 0; i < 1000; i++) { + try { + String mynode = myprefix + "-" + i; + znodes.add(mynode); + zk.create(mynode, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } catch (Exception e) { + LOG.error("Unexpected exception occured!", e); + } + if (i == 200) { + doPurge.countDown(); + } + } + finished.countDown(); + }; + }; + ths[indx] = th; + } + + for (Thread thread : ths) { + thread.start(); + } + try { + Assert.assertTrue("ZkClient ops is not finished!", + finished.await(OP_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS)); + } catch (InterruptedException ie) { + LOG.error("Unexpected exception occured!", ie); + Assert.fail("Unexpected exception occured!"); + } + return znodes; + } + + public void process(WatchedEvent event) { + // do nothing + } + +} diff --git a/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java deleted file mode 100644 index 2f8fec54f05..00000000000 --- a/src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * 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.test; - -import java.io.File; -import java.util.List; - -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.PortAssignment; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZKTestCase; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.server.PurgeTxnLog; -import org.apache.zookeeper.server.ServerCnxnFactory; -import org.apache.zookeeper.server.SyncRequestProcessor; -import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.server.persistence.FileTxnSnapLog; -import org.junit.Assert; -import org.junit.Test; - -public class PurgeTxnTest extends ZKTestCase implements Watcher { - //private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnTest.class); - private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); - private static final int CONNECTION_TIMEOUT = 3000; - /** - * test the purge - * @throws Exception an exception might be thrown here - */ - @Test - public void testPurge() throws Exception { - File tmpDir = ClientBase.createTmpDir(); - ClientBase.setupTestEnv(); - ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); - SyncRequestProcessor.setSnapCount(100); - final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); - ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); - f.startup(zks); - Assert.assertTrue("waiting for server being up ", - ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); - ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - try { - for (int i = 0; i< 2000; i++) { - zk.create("/invalidsnap-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - } - } finally { - zk.close(); - } - f.shutdown(); - Assert.assertTrue("waiting for server to shutdown", - ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); - // now corrupt the snapshot - PurgeTxnLog.purge(tmpDir, tmpDir, 3); - FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpDir, tmpDir); - List listLogs = snaplog.findNRecentSnapshots(4); - int numSnaps = 0; - for (File ff: listLogs) { - if (ff.getName().startsWith("snapshot")) { - numSnaps++; - } - } - Assert.assertTrue("exactly 3 snapshots ", (numSnaps == 3)); - zks.shutdown(); - } - - public void process(WatchedEvent event) { - // do nothing - } - -} From 86ebdc9a903034252f909b0b891cdc6b0e654b72 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 23 Jun 2014 00:01:23 +0000 Subject: [PATCH 256/444] 'src/contrib/zooinspector/NOTICE.txt' isn't complying to '.gitattributes' in branch-3.4 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1604669 13f79535-47bb-0310-9956-ffa450edef68 --- src/contrib/zooinspector/NOTICE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contrib/zooinspector/NOTICE.txt b/src/contrib/zooinspector/NOTICE.txt index 059bdc117f9..e0bf2f0d798 100644 --- a/src/contrib/zooinspector/NOTICE.txt +++ b/src/contrib/zooinspector/NOTICE.txt @@ -1,3 +1,3 @@ This contrib module includes icons available under the Eclipse Public Licence Version 1.0 -. from the Eclipse Java Devlopment Platform. +. from the Eclipse Java Devlopment Platform. The lib sub-directory includes a binary only jar library developed at http://sourceforge.net/projects/jtoaster/ \ No newline at end of file From 7cb4dc74b608f0bee610c4681c776f315c92e654 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 25 Jun 2014 13:41:30 +0000 Subject: [PATCH 257/444] ZOOKEEPER-1945. deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 (Mark Flickinger via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1605396 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/packages/deb/init.d/zookeeper | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 480b3a1d3bd..43392be0d25 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -31,6 +31,9 @@ BUGFIXES: ZOOKEEPER-1797. PurgeTxnLog may delete data logs during roll (Rakesh R via michim) + ZOOKEEPER-1945. deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused + by ZOOKEEPER-1663 (Mark Flickinger via fpj) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/packages/deb/init.d/zookeeper b/src/packages/deb/init.d/zookeeper index 656076fc5e5..d0f7216deed 100644 --- a/src/packages/deb/init.d/zookeeper +++ b/src/packages/deb/init.d/zookeeper @@ -1,4 +1,4 @@ -#! /bin/sh +#! /usr/bin/env bash # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with From d4270af659d09ce5a95c6ed764bdb7017c2ad4f9 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 25 Jun 2014 17:08:45 +0000 Subject: [PATCH 258/444] ZOOKEEPER-1746. AsyncCallback.*Callback don't have any Javadoc (Hongchao Deng via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1605505 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../org/apache/zookeeper/AsyncCallback.java | 207 ++++++++++++++++++ .../org/apache/zookeeper/test/AsyncOps.java | 84 ++++++- .../apache/zookeeper/test/AsyncOpsTest.java | 37 +++- 4 files changed, 329 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 43392be0d25..d8b762b0af4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -42,6 +42,9 @@ IMPROVEMENTS: ZOOKEEPER-657. Cut down the running time of ZKDatabase corruption (Michi Mutsuzaki via rakeshr) + ZOOKEEPER-1746. AsyncCallback.*Callback don't have any Javadoc + (Hongchao Deng via phunt) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/AsyncCallback.java b/src/java/main/org/apache/zookeeper/AsyncCallback.java index 30377238b5d..c9f76c1c532 100644 --- a/src/java/main/org/apache/zookeeper/AsyncCallback.java +++ b/src/java/main/org/apache/zookeeper/AsyncCallback.java @@ -22,36 +22,243 @@ import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; +/** + * Interface definitions of asynchronous callbacks. + * An asynchronous callback is deferred to invoke after a function returns. + * Asynchronous calls usually improve system efficiency on IO-related APIs. + *

            + * ZooKeeper provides asynchronous version as equivalent to synchronous APIs. + */ public interface AsyncCallback { + + /** + * This callback is used to retrieve the stat of the node. + */ interface StatCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + *

            + * On failure, rc is set to the corresponding failure code in + * {@link org.apache.zookeeper.KeeperException}. + *

              + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NONODE} + * - The node on given path doesn't exist for some API calls. + *
            • + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#BADVERSION} + * - The given version doesn't match the node's version + * for some API calls. + *
            • + *
            + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param stat {@link org.apache.zookeeper.data.Stat} object of + * the node on given path. + */ public void processResult(int rc, String path, Object ctx, Stat stat); } + /** + * This callback is used to retrieve the data and stat of the node. + */ interface DataCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + *

            + * On failure, rc is set to the corresponding failure code in + * {@link org.apache.zookeeper.KeeperException}. + *

              + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NONODE} + * - The node on given path doesn't exist for some API calls. + *
            • + *
            + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param data The {@link org.apache.zookeeper.server.DataNode#data} + * of the node. + * @param stat {@link org.apache.zookeeper.data.Stat} object of + * the node on given path. + */ public void processResult(int rc, String path, Object ctx, byte data[], Stat stat); } + /** + * This callback is used to retrieve the ACL and stat of the node. + */ interface ACLCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + *

            + * On failure, rc is set to the corresponding failure code in + * {@link org.apache.zookeeper.KeeperException}. + *

              + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NONODE} + * - The node on given path doesn't exist for some API calls. + *
            • + *
            + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param acl ACL Id in + * {@link org.apache.zookeeper.ZooDefs.Ids}. + * @param stat {@link org.apache.zookeeper.data.Stat} object of + * the node on given path. + */ public void processResult(int rc, String path, Object ctx, List acl, Stat stat); } + /** + * This callback is used to retrieve the children of the node. + */ interface ChildrenCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + *

            + * On failure, rc is set to the corresponding failure code in + * {@link org.apache.zookeeper.KeeperException}. + *

              + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NONODE} + * - The node on given path doesn't exist for some API calls. + *
            • + *
            + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param children An unordered array of children of the node on + * given path. + */ public void processResult(int rc, String path, Object ctx, List children); } + /** + * This callback is used to retrieve the children and stat of the node. + */ interface Children2Callback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + * See {@link org.apache.zookeeper.AsyncCallback.ChildrenCallback}. + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param children An unordered array of children of the node on + * given path. + * @param stat {@link org.apache.zookeeper.data.Stat} object of + * the node on given path. + */ public void processResult(int rc, String path, Object ctx, List children, Stat stat); } + /** + * This callback is used to retrieve the name of the node. + */ interface StringCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + *

            + * On failure, rc is set to the corresponding failure code in + * {@link org.apache.zookeeper.KeeperException}. + *

              + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NODEEXISTS} + * - The node on give path already exists for some API calls. + *
            • + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NONODE} + * - The node on given path doesn't exist for some API calls. + *
            • + *
            • + * {@link + * org.apache.zookeeper.KeeperException.Code#NOCHILDRENFOREPHEMERALS} + * - an ephemeral node cannot have children. There is discussion in + * community. It might be changed in the future. + *
            • + *
            + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param name The name of the Znode that was created. + * On success, name and path are usually + * equal, unless a sequential node has been created. + */ public void processResult(int rc, String path, Object ctx, String name); } + /** + * This callback doesn't retrieve anything from the node. It is useful + * for some APIs that doesn't want anything sent back, e.g. {@link + * org.apache.zookeeper.ZooKeeper#sync(String, + * org.apache.zookeeper.AsyncCallback.VoidCallback, Object)}. + */ interface VoidCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + *

            + * On failure, rc is set to the corresponding failure code in + * {@link org.apache.zookeeper.KeeperException}. + *

              + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NONODE} + * - The node on given path doesn't exist for some API calls. + *
            • + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#BADVERSION} + * - The given version doesn't match the node's version + * for some API calls. + *
            • + *
            • + * {@link org.apache.zookeeper.KeeperException.Code#NOTEMPTY} + * - the node has children and some API calls cannnot succeed, + * e.g. {@link + * org.apache.zookeeper.ZooKeeper#delete(String, int, + * org.apache.zookeeper.AsyncCallback.VoidCallback, Object)}. + *
            • + *
            + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + */ public void processResult(int rc, String path, Object ctx); } } diff --git a/src/java/test/org/apache/zookeeper/test/AsyncOps.java b/src/java/test/org/apache/zookeeper/test/AsyncOps.java index de4c5157565..2dbe64455ea 100644 --- a/src/java/test/org/apache/zookeeper/test/AsyncOps.java +++ b/src/java/test/org/apache/zookeeper/test/AsyncOps.java @@ -148,11 +148,21 @@ public AsyncCB create() { zk.create(path, data, acl, flags, this, toString()); return this; } + + public AsyncCB createEphemeral() { + zk.create(path, data, acl, CreateMode.EPHEMERAL, this, toString()); + return this; + } public void verifyCreate() { create(); verify(); } + + public void verifyCreateEphemeral() { + createEphemeral(); + verify(); + } public void verifyCreateFailure_NodeExists() { new StringCB(zk).verifyCreate(); @@ -162,7 +172,28 @@ public void verifyCreateFailure_NodeExists() { zk.create(path, data, acl, flags, this, toString()); verify(); } - + + public void verifyCreateFailure_NoNode() { + + rc = Code.NONODE; + name = null; + path = path + "/bar"; + zk.create(path, data, acl, flags, this, toString()); + + verify(); + } + + public void verifyCreateFailure_NoChildForEphemeral() { + new StringCB(zk).verifyCreateEphemeral(); + + rc = Code.NOCHILDRENFOREPHEMERALS; + name = null; + path = path + "/bar"; + zk.create(path, data, acl, flags, this, toString()); + + verify(); + } + @Override public String toString() { return super.toString() + name; @@ -201,8 +232,21 @@ public void verifyGetACL() { zk.getACL(path, stat, this, toString()); verify(); } + + public void verifyGetACLFailure_NoNode(){ + rc = Code.NONODE; + stat = null; + acl = null; + zk.getACL(path, stat, this, toString()); + + verify(); + } public String toString(List acls) { + if (acls == null) { + return ""; + } + StringBuilder result = new StringBuilder(); for(ACL acl : acls) { result.append(acl.getPerms() + "::"); @@ -478,6 +522,16 @@ public void verifySetACLFailure_NoNode() { zk.setACL(path, acl, version, this, toString()); verify(); } + + public void verifySetACLFailure_BadVersion() { + new StringCB(zk).verifyCreate(); + + rc = Code.BADVERSION; + stat = null; + zk.setACL(path, acl, version + 1, this, toString()); + + verify(); + } public void setData() { zk.setData(path, data, version, this, toString()); @@ -497,6 +551,16 @@ public void verifySetDataFailure_NoNode() { zk.setData(path, data, version, this, toString()); verify(); } + + public void verifySetDataFailure_BadVersion() { + new StringCB(zk).verifyCreate(); + + rc = Code.BADVERSION; + stat = null; + zk.setData(path, data, version + 1, this, toString()); + + verify(); + } public void verifyExists() { new StringCB(zk).verifyCreate(); @@ -553,6 +617,24 @@ public void verifyDeleteFailure_NoNode() { zk.delete(path, version, this, toString()); verify(); } + + public void verifyDeleteFailure_BadVersion() { + new StringCB(zk).verifyCreate(); + rc = Code.BADVERSION; + zk.delete(path, version + 1, this, toString()); + verify(); + } + + public void verifyDeleteFailure_NotEmpty() { + StringCB scb = new StringCB(zk); + scb.create(); + scb.setPath(path + "/bar"); + scb.create(); + + rc = Code.NOTEMPTY; + zk.delete(path, version, this, toString()); + verify(); + } public void sync() { zk.sync(path, this, toString()); diff --git a/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java b/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java index 340bd735821..c807d7288c4 100644 --- a/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java +++ b/src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java @@ -90,6 +90,16 @@ public void testAsyncCreateFailure_NodeExists() { new StringCB(zk).verifyCreateFailure_NodeExists(); } + @Test + public void testAsyncCreateFailure_NoNode() { + new StringCB(zk).verifyCreateFailure_NoNode(); + } + + @Test + public void testAsyncCreateFailure_NoChildForEphemeral() { + new StringCB(zk).verifyCreateFailure_NoChildForEphemeral(); + } + @Test public void testAsyncDelete() { new VoidCB(zk).verifyDelete(); @@ -100,6 +110,16 @@ public void testAsyncDeleteFailure_NoNode() { new VoidCB(zk).verifyDeleteFailure_NoNode(); } + @Test + public void testAsyncDeleteFailure_BadVersion() { + new VoidCB(zk).verifyDeleteFailure_BadVersion(); + } + + @Test + public void testAsyncDeleteFailure_NotEmpty() { + new VoidCB(zk).verifyDeleteFailure_NotEmpty(); + } + @Test public void testAsyncSync() { new VoidCB(zk).verifySync(); @@ -115,6 +135,11 @@ public void testAsyncSetACLFailure_NoNode() { new StatCB(zk).verifySetACLFailure_NoNode(); } + @Test + public void testAsyncSetACLFailure_BadVersion() { + new StatCB(zk).verifySetACLFailure_BadVersion(); + } + @Test public void testAsyncSetData() { new StatCB(zk).verifySetData(); @@ -125,6 +150,11 @@ public void testAsyncSetDataFailure_NoNode() { new StatCB(zk).verifySetDataFailure_NoNode(); } + @Test + public void testAsyncSetDataFailure_BadVersion() { + new StatCB(zk).verifySetDataFailure_BadVersion(); + } + @Test public void testAsyncExists() { new StatCB(zk).verifyExists(); @@ -140,6 +170,11 @@ public void testAsyncGetACL() { new ACLCB(zk).verifyGetACL(); } + @Test + public void testAsyncGetACLFailure_NoNode() { + new ACLCB(zk).verifyGetACLFailure_NoNode(); + } + @Test public void testAsyncGetChildrenEmpty() { new ChildrenCB(zk).verifyGetChildrenEmpty(); @@ -162,7 +197,7 @@ public void testAsyncGetChildrenFailure_NoNode() { @Test public void testAsyncGetChildren2Empty() { - new ChildrenCB(zk).verifyGetChildrenEmpty(); + new Children2CB(zk).verifyGetChildrenEmpty(); } @Test From 24e7c4eab33fc9e50075fc4f9d5164531027746e Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Wed, 25 Jun 2014 17:28:04 +0000 Subject: [PATCH 259/444] ZOOKEEPER-1939. ReconfigRecoveryTest.testNextConfigUnreachable is failing (Rakesh R via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1605513 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/test/org/apache/zookeeper/test/QuorumBase.java | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d8b762b0af4..9142a0516d7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -34,6 +34,9 @@ BUGFIXES: ZOOKEEPER-1945. deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 (Mark Flickinger via fpj) + ZOOKEEPER-1939. ReconfigRecoveryTest.testNextConfigUnreachable is + failing (Rakesh R via phunt) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/test/org/apache/zookeeper/test/QuorumBase.java b/src/java/test/org/apache/zookeeper/test/QuorumBase.java index f1c8821ce56..dc08d7c5673 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumBase.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumBase.java @@ -323,9 +323,13 @@ public static void shutdown(QuorumPeer qp) { LOG.info("No election available to shutdown " + qp.getName()); } LOG.info("Waiting for " + qp.getName() + " to exit thread"); - qp.join(30000); + long readTimeout = qp.getTickTime() * qp.getInitLimit(); + long connectTimeout = qp.getTickTime() * qp.getSyncLimit(); + long maxTimeout = Math.max(readTimeout, connectTimeout); + maxTimeout = Math.max(maxTimeout, ClientBase.CONNECTION_TIMEOUT); + qp.join(maxTimeout * 2); if (qp.isAlive()) { - Assert.fail("QP failed to shutdown in 30 seconds: " + qp.getName()); + Assert.fail("QP failed to shutdown in " + (maxTimeout * 2) + " seconds: " + qp.getName()); } } catch (InterruptedException e) { LOG.debug("QP interrupted: " + qp.getName(), e); From 8ff14a71216c56ed5b5aaee28977a03018108e53 Mon Sep 17 00:00:00 2001 From: Alexander Shraer Date: Mon, 30 Jun 2014 17:19:19 +0000 Subject: [PATCH 260/444] ZOOKEEPER-1900 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1606842 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../server/persistence/FileTxnLog.java | 5 ++++ .../zookeeper/server/quorum/Follower.java | 2 +- .../zookeeper/server/quorum/Observer.java | 2 +- .../apache/zookeeper/test/TruncateTest.java | 28 +++++++++++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9142a0516d7..2eff7236c77 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,8 @@ Backward compatible changes: BUGFIXES: + ZOOKEEPER-1900. NullPointerException in truncate (Camille Fournier) + ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun MXBean classes (Adalberto Medeiros via phunt) diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index b0dd79c1c37..7456f743ba3 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -364,6 +364,11 @@ public boolean truncate(long zxid) throws IOException { try { itr = new FileTxnIterator(this.logDir, zxid); PositionInputStream input = itr.inputStream; + if(input == null) { + throw new IOException("No log files found to truncate! This could " + + "happen if you still have snapshots from an old setup or " + + "log files were deleted accidentally or dataLogDir was changed in zoo.cfg."); + } long pos = input.getPosition(); // now, truncate at the current position RandomAccessFile raf = new RandomAccessFile(itr.logFile, "rw"); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java index ab3f288ed5b..2aeb0c34d44 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java @@ -85,7 +85,7 @@ void followLeader() throws InterruptedException { readPacket(qp); processPacket(qp); } - } catch (IOException e) { + } catch (Exception e) { LOG.warn("Exception when following the leader", e); try { sock.close(); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java index ee61a90c47c..33b03796153 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java @@ -75,7 +75,7 @@ void observeLeader() throws InterruptedException { readPacket(qp); processPacket(qp); } - } catch (IOException e) { + } catch (Exception e) { LOG.warn("Exception when observing the leader", e); try { sock.close(); diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index 3d2854c17b0..4cf3f8fb943 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -126,6 +126,34 @@ private void append(ZKDatabase zkdb, int i) throws IOException { zkdb.commit(); } + + @Test + public void testTruncationNullLog() throws Exception { + File tmpdir = ClientBase.createTmpDir(); + FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpdir, tmpdir); + ZKDatabase zkdb = new ZKDatabase(snaplog); + + for (int i = 1; i <= 100; i++) { + append(zkdb, i); + } + File[] logs = snaplog.getDataDir().listFiles(); + for(int i = 0; i < logs.length; i++) { + logs[i].delete(); + } + try { + zkdb.truncateLog(1); + Assert.assertTrue("Should not get here", false); + } + catch(IOException e) { + Assert.assertTrue("Should have received an IOException", true); + } + catch(NullPointerException npe) { + Assert.fail("This should not throw NPE!"); + } + + ClientBase.recursiveDelete(tmpdir); + } + @Test public void testTruncate() throws IOException, InterruptedException, KeeperException { // Prime the server that is going to come in late with 50 txns From 65913ae86fd0ba91f24fe51d0c3deb0aa6a347f6 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 8 Jul 2014 18:27:36 +0000 Subject: [PATCH 261/444] ZOOKEEPER-1222. getACL should only call DataTree.copyStat when passed in stat is not null (Michi Mutsuzaki via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1608873 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/ZooKeeper.java | 7 +++++-- src/java/test/org/apache/zookeeper/test/ClientTest.java | 6 ++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2eff7236c77..94773772429 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -37,6 +37,9 @@ BUGFIXES: ZOOKEEPER-1939. ReconfigRecoveryTest.testNextConfigUnreachable is failing (Rakesh R via phunt) + ZOOKEEPER-1222. getACL should only call DataTree.copyStat when passed in + stat is not null (Michi Mutsuzaki via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index 6385781f883..139ce663992 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -1306,7 +1306,8 @@ public void setData(final String path, byte data[], int version, * @param path * the given path for the node * @param stat - * the stat of the node will be copied to this parameter. + * the stat of the node will be copied to this parameter if + * not null. * @return the ACL array of the given node. * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. @@ -1330,7 +1331,9 @@ public List getACL(final String path, Stat stat) throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } - DataTree.copyStat(response.getStat(), stat); + if (stat != null) { + DataTree.copyStat(response.getStat(), stat); + } return response.getAcl(); } diff --git a/src/java/test/org/apache/zookeeper/test/ClientTest.java b/src/java/test/org/apache/zookeeper/test/ClientTest.java index 5dd0ac9efcb..d37041dd574 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientTest.java @@ -162,6 +162,12 @@ public void testACLs() throws Exception { List acls = zk.getACL("/acltest", new Stat()); Assert.assertEquals(1, acls.size()); Assert.assertEquals(Ids.OPEN_ACL_UNSAFE, acls); + + // The stat parameter should be optional. + acls = zk.getACL("/acltest", null); + Assert.assertEquals(1, acls.size()); + Assert.assertEquals(Ids.OPEN_ACL_UNSAFE, acls); + zk.close(); } finally { if (zk != null) { From b446ec638e1614136df52b1a9a82699aa9e4a529 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 18 Sep 2014 15:51:08 +0000 Subject: [PATCH 262/444] ZOOKEEPER-2039. Jute compareBytes incorrect comparison index (Ian Dimayuga via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1626009 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/java/main/org/apache/jute/Utils.java | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 94773772429..4813f3fe3fe 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -40,6 +40,8 @@ BUGFIXES: ZOOKEEPER-1222. getACL should only call DataTree.copyStat when passed in stat is not null (Michi Mutsuzaki via rakeshr) + ZOOKEEPER-2039. Jute compareBytes incorrect comparison index (Ian Dimayuga via fpj) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/jute/Utils.java b/src/java/main/org/apache/jute/Utils.java index 243f2c8b0e7..1205fa2f33a 100644 --- a/src/java/main/org/apache/jute/Utils.java +++ b/src/java/main/org/apache/jute/Utils.java @@ -268,15 +268,15 @@ static byte[] fromCSVBuffer(String s) return stream.toByteArray(); } public static int compareBytes(byte b1[], int off1, int len1, byte b2[], int off2, int len2) { - int i; - for(i=0; i < len1 && i < len2; i++) { - if (b1[off1+i] != b2[off2+i]) { - return b1[off1+i] < b2[off2+1] ? -1 : 1; - } - } - if (len1 != len2) { - return len1 < len2 ? -1 : 1; - } - return 0; + int i; + for(i=0; i < len1 && i < len2; i++) { + if (b1[off1+i] != b2[off2+i]) { + return b1[off1+i] < b2[off2+i] ? -1 : 1; + } + } + if (len1 != len2) { + return len1 < len2 ? -1 : 1; + } + return 0; } } From 6847f8e716b15e085afded103aa5a9e176084afd Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Sat, 27 Sep 2014 06:24:38 +0000 Subject: [PATCH 263/444] ZOOKEEPER-2047 testTruncationNullLog fails on windows (flavio via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1627924 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/java/test/org/apache/zookeeper/test/TruncateTest.java | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4813f3fe3fe..ddb73574df6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -42,6 +42,8 @@ BUGFIXES: ZOOKEEPER-2039. Jute compareBytes incorrect comparison index (Ian Dimayuga via fpj) + ZOOKEEPER-2047 testTruncationNullLog fails on windows (flavio via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index 4cf3f8fb943..c528681dcdf 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -136,9 +136,11 @@ public void testTruncationNullLog() throws Exception { for (int i = 1; i <= 100; i++) { append(zkdb, i); } + zkdb.close(); File[] logs = snaplog.getDataDir().listFiles(); for(int i = 0; i < logs.length; i++) { - logs[i].delete(); + LOG.debug("Deleting: {}", logs[i].getName()); + Assert.assertTrue("Failed to delete log file: " + logs[i].getName(), logs[i].delete()); } try { zkdb.truncateLog(1); From 0763fab1240c6361ad39a64798ab3b32df7fe4d1 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Sun, 28 Sep 2014 17:18:16 +0000 Subject: [PATCH 264/444] ZOOKEEPER-2026 Startup order in ServerCnxnFactory-ies is wrong (Stevo Slavic via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1628087 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../server/NIOServerCnxnFactory.java | 2 +- .../server/NettyServerCnxnFactory.java | 2 +- .../server/ZooKeeperServerMainTest.java | 76 ++++++++++++++++++- 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ddb73574df6..5b9d2ecf17d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -44,6 +44,8 @@ BUGFIXES: ZOOKEEPER-2047 testTruncationNullLog fails on windows (flavio via rakeshr) + ZOOKEEPER-2026 Startup order in ServerCnxnFactory-ies is wrong (Stevo Slavic via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java index 8b4c46bc9a1..a74bd1ee751 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java @@ -119,9 +119,9 @@ public void start() { public void startup(ZooKeeperServer zks) throws IOException, InterruptedException { start(); + setZooKeeperServer(zks); zks.startdata(); zks.startup(); - setZooKeeperServer(zks); } @Override diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index 7a84ce91111..edff0ff1dbb 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -368,9 +368,9 @@ public void start() { public void startup(ZooKeeperServer zks) throws IOException, InterruptedException { start(); + setZooKeeperServer(zks); zks.startdata(); zks.startup(); - setZooKeeperServer(zks); } @Override diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java index 217100b1c2b..c31b487aa05 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java @@ -27,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; @@ -129,7 +130,7 @@ public void shutdown() { public void testStandalone() throws Exception { ClientBase.setupTestEnv(); - final int CLIENT_PORT = 3181; + final int CLIENT_PORT = PortAssignment.unique(); MainThread main = new MainThread(CLIENT_PORT, true); main.start(); @@ -162,7 +163,7 @@ public void testStandalone() throws Exception { @Test(timeout = 30000) public void testAutoCreateDataLogDir() throws Exception { ClientBase.setupTestEnv(); - final int CLIENT_PORT = 3181; + final int CLIENT_PORT = PortAssignment.unique(); MainThread main = new MainThread(CLIENT_PORT, false); String args[] = new String[1]; @@ -191,6 +192,77 @@ public void testAutoCreateDataLogDir() throws Exception { ClientBase.CONNECTION_TIMEOUT)); } + @Test + public void testJMXRegistrationWithNIO() throws Exception { + ClientBase.setupTestEnv(); + File tmpDir_1 = ClientBase.createTmpDir(); + ServerCnxnFactory server_1 = startServer(tmpDir_1); + File tmpDir_2 = ClientBase.createTmpDir(); + ServerCnxnFactory server_2 = startServer(tmpDir_2); + + server_1.shutdown(); + server_2.shutdown(); + + deleteFile(tmpDir_1); + deleteFile(tmpDir_2); + } + + @Test + public void testJMXRegistrationWithNetty() throws Exception { + String originalServerCnxnFactory = System + .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, + NettyServerCnxnFactory.class.getName()); + try { + ClientBase.setupTestEnv(); + File tmpDir_1 = ClientBase.createTmpDir(); + ServerCnxnFactory server_1 = startServer(tmpDir_1); + File tmpDir_2 = ClientBase.createTmpDir(); + ServerCnxnFactory server_2 = startServer(tmpDir_2); + + server_1.shutdown(); + server_2.shutdown(); + + deleteFile(tmpDir_1); + deleteFile(tmpDir_2); + } finally { + // setting back + if (originalServerCnxnFactory == null + || originalServerCnxnFactory.isEmpty()) { + System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); + } else { + System.setProperty( + ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, + originalServerCnxnFactory); + } + } + } + + private void deleteFile(File f) throws IOException { + if (f.isDirectory()) { + for (File c : f.listFiles()) + deleteFile(c); + } + if (!f.delete()) + // double check for the file existence + if (f.exists()) { + throw new IOException("Failed to delete file: " + f); + } + } + + private ServerCnxnFactory startServer(File tmpDir) throws IOException, + InterruptedException { + final int CLIENT_PORT = PortAssignment.unique(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(CLIENT_PORT, -1); + f.startup(zks); + Assert.assertNotNull("JMX initialization failed!", zks.jmxServerBean); + Assert.assertTrue("waiting for server being up", + ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT)); + return f; + } + public void process(WatchedEvent event) { // ignore for this test } From 862d025849afc49e86e5be5ee904b29b8dee3157 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Mon, 29 Sep 2014 06:21:13 +0000 Subject: [PATCH 265/444] ZOOKEEPER-1917. Apache Zookeeper logs cleartext admin passwords (fpj via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1628124 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../src/documentation/content/xdocs/zookeeperAdmin.xml | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 5b9d2ecf17d..2ee749db5da 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -57,6 +57,8 @@ IMPROVEMENTS: ZOOKEEPER-1746. AsyncCallback.*Callback don't have any Javadoc (Hongchao Deng via phunt) + ZOOKEEPER-1917. Apache Zookeeper logs cleartext admin passwords (fpj via michim) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 6ea648fad77..9d1b8928634 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1454,6 +1454,13 @@ imok and maintenance of ZooKeeper storage. + + The data stored in these files is not encrypted. In the case of + storing sensitive data in ZooKeeper (which is fairly uncommon), necessary + measures need to be taken to prevent unauthorized access. Such measures + are external to ZooKeeper (e.g., control access to the files) and + depend on the individual settings in which it is being deployed. +
            From aac736ae182ca309442a8e48cc4d0cc66b4af6da Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 29 Sep 2014 17:15:59 +0000 Subject: [PATCH 266/444] ZOOKEEPER-1948 Enable JMX remote monitoring (Biju Nair via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1628225 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ bin/zkServer.sh | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 2ee749db5da..9b8cce5cc4b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -59,6 +59,8 @@ IMPROVEMENTS: ZOOKEEPER-1917. Apache Zookeeper logs cleartext admin passwords (fpj via michim) + ZOOKEEPER-1948 Enable JMX remote monitoring (Biju Nair via rakeshr) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/bin/zkServer.sh b/bin/zkServer.sh index 0490982760a..e4a6c933428 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -32,11 +32,32 @@ fi if [ "x$JMXDISABLE" = "x" ] then - echo "JMX enabled by default" >&2 + echo "ZooKeeper JMX enabled by default" >&2 + if [ "x$JMXPORT" = "x" ] + then # for some reason these two options are necessary on jdk6 on Ubuntu # accord to the docs they are not necessary, but otw jconsole cannot # do a local attach ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain" + else + if [ "x$JMXAUTH" = "x" ] + then + JMXAUTH=false + fi + if [ "x$JMXSSL" = "x" ] + then + JMXSSL=false + fi + if [ "x$JMXLOG4J" = "x" ] + then + JMXLOG4J=true + fi + echo "ZooKeeper remote JMX Port set to $JMXPORT" >&2 + echo "ZooKeeper remote JMX authenticate set to $JMXAUTH" >&2 + echo "ZooKeeper remote JMX ssl set to $JMXSSL" >&2 + echo "ZooKeeper remote JMX log4j set to $JMXLOG4J" >&2 + ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=$JMXPORT -Dcom.sun.management.jmxremote.authenticate=$JMXAUTH -Dcom.sun.management.jmxremote.ssl=$JMXSSL -Dzookeeper.jmx.log4j.disable=$JMXLOG4J org.apache.zookeeper.server.quorum.QuorumPeerMain" + fi else echo "JMX disabled by user request" >&2 ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" From d6fe5fc44988f83febdd6015032ac3e124a1b7f1 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 13 Oct 2014 03:14:02 +0000 Subject: [PATCH 267/444] ZOOKEEPER-1917 Apache Zookeeper logs cleartext admin passwords (michim via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1631279 13f79535-47bb-0310-9956-ffa450edef68 --- .../documentation/content/xdocs/zookeeperAdmin.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 9d1b8928634..5aa14eb0c5a 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1453,14 +1453,14 @@ imok this document for more details on setting a retention policy and maintenance of ZooKeeper storage. + + The data stored in these files is not encrypted. In the case of + storing sensitive data in ZooKeeper, necessary measures need to be + taken to prevent unauthorized access. Such measures are external to + ZooKeeper (e.g., control access to the files) and depend on the + individual settings in which it is being deployed. +
            - - The data stored in these files is not encrypted. In the case of - storing sensitive data in ZooKeeper (which is fairly uncommon), necessary - measures need to be taken to prevent unauthorized access. Such measures - are external to ZooKeeper (e.g., control access to the files) and - depend on the individual settings in which it is being deployed. -
            From 7c316df02f9321cbca3c5ef4decb1c6554f11afa Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Thu, 16 Oct 2014 04:54:19 +0000 Subject: [PATCH 268/444] ZOOKEEPER-2049 Yosemite build failure: htonll conflict (Till Toenshoff via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1632212 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/include/recordio.h | 2 +- src/c/src/recordio.c | 6 +++--- src/c/src/zookeeper.c | 6 +++--- src/c/tests/ZKMocks.cc | 6 +++--- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9b8cce5cc4b..bfbf17153e2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -46,6 +46,9 @@ BUGFIXES: ZOOKEEPER-2026 Startup order in ServerCnxnFactory-ies is wrong (Stevo Slavic via rakeshr) + ZOOKEEPER-2049 Yosemite build failure: htonll conflict (Till Toenshoff via + michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/include/recordio.h b/src/c/include/recordio.h index 4e1b78eab24..90f458b4aaa 100644 --- a/src/c/include/recordio.h +++ b/src/c/include/recordio.h @@ -73,7 +73,7 @@ void close_buffer_iarchive(struct iarchive **ia); char *get_buffer(struct oarchive *); int get_buffer_len(struct oarchive *); -int64_t htonll(int64_t v); +int64_t zoo_htonll(int64_t v); #ifdef __cplusplus } diff --git a/src/c/src/recordio.c b/src/c/src/recordio.c index cf8a1ac7c81..41797fbc978 100644 --- a/src/c/src/recordio.c +++ b/src/c/src/recordio.c @@ -80,7 +80,7 @@ int oa_serialize_int(struct oarchive *oa, const char *tag, const int32_t *d) priv->off+=sizeof(i); return 0; } -int64_t htonll(int64_t v) +int64_t zoo_htonll(int64_t v) { int i = 0; char *s = (char *)&v; @@ -98,7 +98,7 @@ int64_t htonll(int64_t v) int oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d) { - const int64_t i = htonll(*d); + const int64_t i = zoo_htonll(*d); struct buff_struct *priv = oa->priv; if ((priv->len - priv->off) < sizeof(i)) { int rc = resize_buffer(priv, priv->len + sizeof(i)); @@ -207,7 +207,7 @@ int ia_deserialize_long(struct iarchive *ia, const char *tag, int64_t *count) } memcpy(count, priv->buffer+priv->off, sizeof(*count)); priv->off+=sizeof(*count); - v = htonll(*count); // htonll and ntohll do the same + v = zoo_htonll(*count); // htonll and ntohll do the same *count = v; return 0; } diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 27ecf8a6aee..ba55d2770cd 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1408,7 +1408,7 @@ static int serialize_prime_connect(struct connect_req *req, char* buffer){ memcpy(buffer + offset, &req->protocolVersion, sizeof(req->protocolVersion)); offset = offset + sizeof(req->protocolVersion); - req->lastZxidSeen = htonll(req->lastZxidSeen); + req->lastZxidSeen = zoo_htonll(req->lastZxidSeen); memcpy(buffer + offset, &req->lastZxidSeen, sizeof(req->lastZxidSeen)); offset = offset + sizeof(req->lastZxidSeen); @@ -1416,7 +1416,7 @@ static int serialize_prime_connect(struct connect_req *req, char* buffer){ memcpy(buffer + offset, &req->timeOut, sizeof(req->timeOut)); offset = offset + sizeof(req->timeOut); - req->sessionId = htonll(req->sessionId); + req->sessionId = zoo_htonll(req->sessionId); memcpy(buffer + offset, &req->sessionId, sizeof(req->sessionId)); offset = offset + sizeof(req->sessionId); @@ -1447,7 +1447,7 @@ static int serialize_prime_connect(struct connect_req *req, char* buffer){ memcpy(&req->sessionId, buffer + offset, sizeof(req->sessionId)); offset = offset + sizeof(req->sessionId); - req->sessionId = htonll(req->sessionId); + req->sessionId = zoo_htonll(req->sessionId); memcpy(&req->passwd_len, buffer + offset, sizeof(req->passwd_len)); offset = offset + sizeof(req->passwd_len); diff --git a/src/c/tests/ZKMocks.cc b/src/c/tests/ZKMocks.cc index 89166747fc2..69bea166f32 100644 --- a/src/c/tests/ZKMocks.cc +++ b/src/c/tests/ZKMocks.cc @@ -41,7 +41,7 @@ HandshakeRequest* HandshakeRequest::parse(const std::string& buf){ int offset=sizeof(req->protocolVersion); memcpy(&req->lastZxidSeen,buf.data()+offset,sizeof(req->lastZxidSeen)); - req->lastZxidSeen = htonll(req->lastZxidSeen); + req->lastZxidSeen = zoo_htonll(req->lastZxidSeen); offset+=sizeof(req->lastZxidSeen); memcpy(&req->timeOut,buf.data()+offset,sizeof(req->timeOut)); @@ -49,7 +49,7 @@ HandshakeRequest* HandshakeRequest::parse(const std::string& buf){ offset+=sizeof(req->timeOut); memcpy(&req->sessionId,buf.data()+offset,sizeof(req->sessionId)); - req->sessionId = htonll(req->sessionId); + req->sessionId = zoo_htonll(req->sessionId); offset+=sizeof(req->sessionId); memcpy(&req->passwd_len,buf.data()+offset,sizeof(req->passwd_len)); @@ -322,7 +322,7 @@ string HandshakeResponse::toString() const { buf.append((char*)&tmp,sizeof(tmp)); tmp=htonl(timeOut); buf.append((char*)&tmp,sizeof(tmp)); - int64_t tmp64=htonll(sessionId); + int64_t tmp64=zoo_htonll(sessionId); buf.append((char*)&tmp64,sizeof(sessionId)); tmp=htonl(passwd_len); buf.append((char*)&tmp,sizeof(tmp)); From 5591a49be229384a2e7384ca65e34b9ac20ed9ae Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 28 Oct 2014 04:36:48 +0000 Subject: [PATCH 269/444] ZOOKEEPER-2052 Unable to delete a node when the node has no children (Hongchao Deng and Yip Ng via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1634777 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../server/PrepRequestProcessor.java | 105 +++++----- .../server/PrepRequestProcessorTest.java | 184 ++++++++++++++---- .../zookeeper/test/MultiTransactionTest.java | 52 +++++ 4 files changed, 258 insertions(+), 86 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bfbf17153e2..138a85cab44 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -49,6 +49,9 @@ BUGFIXES: ZOOKEEPER-2049 Yosemite build failure: htonll conflict (Till Toenshoff via michim) + ZOOKEEPER-2052 Unable to delete a node when the node has no children + (Hongchao Deng and Yip Ng via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index f636d2a87cb..4e1e52684e5 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -147,14 +147,6 @@ ChangeRecord getRecordForPath(String path) throws KeeperException.NoNodeExceptio ChangeRecord lastChange = null; synchronized (zks.outstandingChanges) { lastChange = zks.outstandingChangesForPath.get(path); - /* - for (int i = 0; i < zks.outstandingChanges.size(); i++) { - ChangeRecord c = zks.outstandingChanges.get(i); - if (c.path.equals(path)) { - lastChange = c; - } - } - */ if (lastChange == null) { DataNode n = zks.getZKDatabase().getNode(path); if (n != null) { @@ -176,6 +168,12 @@ ChangeRecord getRecordForPath(String path) throws KeeperException.NoNodeExceptio return lastChange; } + private ChangeRecord getOutstandingChange(String path) { + synchronized (zks.outstandingChanges) { + return zks.outstandingChangesForPath.get(path); + } + } + void addChangeRecord(ChangeRecord c) { synchronized (zks.outstandingChanges) { zks.outstandingChanges.add(c); @@ -190,41 +188,40 @@ void addChangeRecord(ChangeRecord c) { * of a failed multi-op. * * @param multiRequest + * @return a map that contains previously existed records that probably need to be + * rolled back in any failure. */ HashMap getPendingChanges(MultiTransactionRecord multiRequest) { - HashMap pendingChangeRecords = new HashMap(); - - for(Op op: multiRequest) { - String path = op.getPath(); - - try { - ChangeRecord cr = getRecordForPath(path); - if (cr != null) { - pendingChangeRecords.put(path, cr); - } - /* - * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord - * of the parent node of a request. So that if this is a - * sequential node creation request, rollbackPendingChanges() - * can restore previous parent's ChangeRecord correctly. - * - * Otherwise, sequential node name generation will be incorrect - * for a subsequent request. - */ - int lastSlash = path.lastIndexOf('/'); - if (lastSlash == -1 || path.indexOf('\0') != -1) { - continue; - } - String parentPath = path.substring(0, lastSlash); - ChangeRecord parentCr = getRecordForPath(parentPath); - if (parentCr != null) { - pendingChangeRecords.put(parentPath, parentCr); - } - } catch (KeeperException.NoNodeException e) { - // ignore this one - } - } - + HashMap pendingChangeRecords = new HashMap(); + + for (Op op : multiRequest) { + String path = op.getPath(); + ChangeRecord cr = getOutstandingChange(path); + // only previously existing records need to be rolled back. + if (cr != null) { + pendingChangeRecords.put(path, cr); + } + + /* + * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord + * of the parent node of a request. So that if this is a + * sequential node creation request, rollbackPendingChanges() + * can restore previous parent's ChangeRecord correctly. + * + * Otherwise, sequential node name generation will be incorrect + * for a subsequent request. + */ + int lastSlash = path.lastIndexOf('/'); + if (lastSlash == -1 || path.indexOf('\0') != -1) { + continue; + } + String parentPath = path.substring(0, lastSlash); + ChangeRecord parentCr = getOutstandingChange(parentPath); + if (parentCr != null) { + pendingChangeRecords.put(parentPath, parentCr); + } + } + return pendingChangeRecords; } @@ -239,7 +236,6 @@ HashMap getPendingChanges(MultiTransactionRecord multiRequ * @param pendingChangeRecords */ void rollbackPendingChanges(long zxid, HashMappendingChangeRecords) { - synchronized (zks.outstandingChanges) { // Grab a list iterator starting at the END of the list so we can iterate in reverse ListIterator iter = zks.outstandingChanges.listIterator(zks.outstandingChanges.size()); @@ -247,27 +243,30 @@ void rollbackPendingChanges(long zxid, HashMappendingChang ChangeRecord c = iter.previous(); if (c.zxid == zxid) { iter.remove(); + // Remove all outstanding changes for paths of this multi. + // Previous records will be added back later. zks.outstandingChangesForPath.remove(c.path); } else { break; } } - - boolean empty = zks.outstandingChanges.isEmpty(); - long firstZxid = 0; - if (!empty) { - firstZxid = zks.outstandingChanges.get(0).zxid; + + // we don't need to roll back any records because there is nothing left. + if (zks.outstandingChanges.isEmpty()) { + return; } - Iterator priorIter = pendingChangeRecords.values().iterator(); - while (priorIter.hasNext()) { - ChangeRecord c = priorIter.next(); - - /* Don't apply any prior change records less than firstZxid */ - if (!empty && (c.zxid < firstZxid)) { + long firstZxid = zks.outstandingChanges.get(0).zxid; + + for (ChangeRecord c : pendingChangeRecords.values()) { + // Don't apply any prior change records less than firstZxid. + // Note that previous outstanding requests might have been removed + // once they are completed. + if (c.zxid < firstZxid) { continue; } + // add previously existing records back. zks.outstandingChangesForPath.put(c.path, c); } } diff --git a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java index 67b9fc92c89..1d09b136e5b 100644 --- a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java +++ b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java @@ -18,68 +18,186 @@ package org.apache.zookeeper.server; -import static org.junit.Assert.*; - -import java.io.File; -import java.io.PrintWriter; -import java.nio.ByteBuffer; -import java.util.concurrent.CountDownLatch; - -import org.apache.zookeeper.PortAssignment; -import org.apache.zookeeper.KeeperException.Code; +import org.apache.jute.BinaryOutputArchive; +import org.apache.jute.Record; +import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.KeeperException.SessionMovedException; +import org.apache.zookeeper.MultiTransactionRecord; +import org.apache.zookeeper.Op; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; -import org.apache.zookeeper.server.PrepRequestProcessor; -import org.apache.zookeeper.server.Request; -import org.apache.zookeeper.server.RequestProcessor; -import org.apache.zookeeper.server.ServerCnxnFactory; -import org.apache.zookeeper.server.SyncRequestProcessor; -import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.data.Id; +import org.apache.zookeeper.server.ZooKeeperServer.ChangeRecord; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.ErrorTxn; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CountDownLatch; public class PrepRequestProcessorTest extends ClientBase { - private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); + private static final Logger LOG = LoggerFactory.getLogger(PrepRequestProcessorTest.class); private static final int CONNECTION_TIMEOUT = 3000; - private final CountDownLatch testEnd = new CountDownLatch(1); + private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); + private CountDownLatch pLatch; - @Test - public void testPRequest() throws Exception { + private ZooKeeperServer zks; + private ServerCnxnFactory servcnxnf; + private PrepRequestProcessor processor; + private Request outcome; + + @Before + public void setup() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); - ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(100); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); - ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); - f.startup(zks); + + servcnxnf = ServerCnxnFactory.createFactory(PORT, -1); + servcnxnf.startup(zks); Assert.assertTrue("waiting for server being up ", - ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); - zks.sessionTracker = new MySessionTracker(); - PrepRequestProcessor processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); + ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT)); + zks.sessionTracker = new MySessionTracker(); + } + + @After + public void teardown() throws Exception { + if (servcnxnf != null) { + servcnxnf.shutdown(); + } + if (zks != null) { + zks.shutdown(); + } + } + + @Test + public void testPRequest() throws Exception { + pLatch = new CountDownLatch(1); + processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); Request foo = new Request(null, 1l, 1, OpCode.create, ByteBuffer.allocate(3), null); processor.pRequest(foo); - testEnd.await(5, java.util.concurrent.TimeUnit.SECONDS); - f.shutdown(); - zks.shutdown(); + + Assert.assertEquals("Request should have marshalling error", new ErrorTxn(KeeperException.Code.MARSHALLINGERROR.intValue()), + outcome.txn); + Assert.assertTrue("request hasn't been processed in chain", + pLatch.await(5, java.util.concurrent.TimeUnit.SECONDS)); + } + + private Request createMultiRequest(List ops) throws IOException { + Record record = new MultiTransactionRecord(ops); + + // encoding + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); + record.serialize(boa, "request"); + baos.close(); + + // Id + List ids = Arrays.asList(Ids.ANYONE_ID_UNSAFE); + + return new Request(null, 1l, 0, OpCode.multi, ByteBuffer.wrap(baos.toByteArray()), ids); + } + + private void process(List ops) throws Exception { + pLatch = new CountDownLatch(1); + processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); + + Request req = createMultiRequest(ops); + + processor.pRequest(req); + Assert.assertTrue("request hasn't been processed in chain", + pLatch.await(5, java.util.concurrent.TimeUnit.SECONDS)); + } + + /** + * This test checks that a successful multi will change outstanding record + * and failed multi shouldn't change outstanding record. + */ + @Test + public void testMultiOutstandingChange() throws Exception { + zks.getZKDatabase().dataTree.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); + + Assert.assertNull(zks.outstandingChangesForPath.get("/foo")); + + process(Arrays.asList( + Op.setData("/foo", new byte[0], -1))); + + ChangeRecord cr = zks.outstandingChangesForPath.get("/foo"); + Assert.assertNotNull("Change record wasn't set", cr); + Assert.assertEquals("Record zxid wasn't set correctly", + 1, cr.zxid); + + process(Arrays.asList( + Op.delete("/foo", -1))); + cr = zks.outstandingChangesForPath.get("/foo"); + Assert.assertEquals("Record zxid wasn't set correctly", + 2, cr.zxid); + + + // It should fail and shouldn't change outstanding record. + process(Arrays.asList( + Op.delete("/foo", -1))); + cr = zks.outstandingChangesForPath.get("/foo"); + // zxid should still be previous result because record's not changed. + Assert.assertEquals("Record zxid wasn't set correctly", + 2, cr.zxid); + } + + /** + * ZOOKEEPER-2052: + * This test checks that if a multi operation aborted, and during the multi there is side effect + * that changed outstandingChangesForPath, after aborted the side effect should be removed and + * everything should be restored correctly. + */ + @Test + public void testMultiRollbackNoLastChange() throws Exception { + zks.getZKDatabase().dataTree.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); + zks.getZKDatabase().dataTree.createNode("/foo/bar", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); + + pLatch = new CountDownLatch(1); + processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); + + Assert.assertNull(zks.outstandingChangesForPath.get("/foo")); + + // multi record: + // set "/foo" => succeed, leave a outstanding change + // delete "/foo" => fail, roll back change + process(Arrays.asList( + Op.setData("/foo", new byte[0], -1), + Op.delete("/foo", -1))); + + // aborting multi shouldn't leave any record. + Assert.assertNull(zks.outstandingChangesForPath.get("/foo")); } - private class MyRequestProcessor implements RequestProcessor { @Override public void processRequest(Request request) { - Assert.assertEquals("Request should have marshalling error", new ErrorTxn(Code.MARSHALLINGERROR.intValue()), request.txn); - testEnd.countDown(); + // getting called by PrepRequestProcessor + outcome = request; + pLatch.countDown(); } + @Override public void shutdown() { // TODO Auto-generated method stub - } } - + private class MySessionTracker implements SessionTracker { @Override public void addSession(long id, int to) { diff --git a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java index de1d50fca88..08d92b89faf 100644 --- a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java +++ b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java @@ -124,6 +124,58 @@ public void testInvalidPath() throws Exception { } } + /** + * ZOOKEEPER-2052: + * Multi abort shouldn't have any side effect. + * We fix a bug in rollback and the following scenario should work: + * 1. multi delete abort because of not empty directory + * 2. ephemeral nodes under that directory are deleted + * 3. multi delete should succeed. + */ + @Test + public void testMultiRollback() throws Exception { + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + + ZooKeeper epheZk = createClient(); + epheZk.create("/foo/bar", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); + + List opList = Arrays.asList(Op.delete("/foo", -1)); + try { + zk.multi(opList); + Assert.fail("multi delete should failed for not empty directory"); + } catch (KeeperException.NotEmptyException e) { + } + + final CountDownLatch latch = new CountDownLatch(1); + + zk.exists("/foo/bar", new Watcher() { + @Override + public void process(WatchedEvent event) { + if (event.getType() == Event.EventType.NodeDeleted){ + latch.countDown(); + } + } + }); + + epheZk.close(); + + latch.await(); + + try { + zk.getData("/foo/bar", false, null); + Assert.fail("ephemeral node should have been deleted"); + } catch (KeeperException.NoNodeException e) { + } + + zk.multi(opList); + + try { + zk.getData("/foo", false, null); + Assert.fail("persistent node should have been deleted after multi"); + } catch (KeeperException.NoNodeException e) { + } + } + /** * Test verifies the multi calls with blank znode path */ From 6aedadb56c4e641cef88c959437348e92c526c53 Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 19 Nov 2014 22:39:20 +0000 Subject: [PATCH 270/444] ZOOKEEPER-2060 Trace bug in NettyServerCnxnFactory (Ian via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1640637 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../apache/zookeeper/server/NettyServerCnxnFactory.java | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 138a85cab44..8d826ed2ec1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -51,6 +51,8 @@ BUGFIXES: ZOOKEEPER-2052 Unable to delete a node when the node has no children (Hongchao Deng and Yip Ng via rakeshr) + + ZOOKEEPER-2060 Trace bug in NettyServerCnxnFactory (Ian via fpj) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index edff0ff1dbb..2087eaa96e9 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -182,9 +182,11 @@ private void processMessage(MessageEvent e, NettyServerCnxn cnxn) { cnxn.queuedBuffer = dynamicBuffer(buf.readableBytes()); } cnxn.queuedBuffer.writeBytes(buf); - LOG.debug(Long.toHexString(cnxn.sessionId) - + " queuedBuffer 0x" - + ChannelBuffers.hexDump(cnxn.queuedBuffer)); + if (LOG.isTraceEnabled()) { + LOG.trace(Long.toHexString(cnxn.sessionId) + + " queuedBuffer 0x" + + ChannelBuffers.hexDump(cnxn.queuedBuffer)); + } } else { LOG.debug("not throttled"); if (cnxn.queuedBuffer != null) { From 620f9f4c22192e17d0fb9a4bb5486dcd2d8db2bd Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Sat, 29 Nov 2014 15:55:50 +0000 Subject: [PATCH 271/444] ZOOKEEPER-2064 Prevent resource leak in various classes (Ted Yu via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1642441 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../java/org/apache/zookeeper/graph/Log4JSource.java | 10 ++++++++++ .../apache/zookeeper/graph/RandomAccessFileReader.java | 1 + .../org/apache/zookeeper/graph/servlets/NumEvents.java | 1 + .../apache/zookeeper/graph/servlets/StaticContent.java | 7 +++++-- .../apache/zookeeper/server/jersey/cfg/RestCfg.java | 4 ++++ .../org/apache/zookeeper/test/system/GenerateLoad.java | 10 +++++++--- .../zookeeper/test/system/QuorumPeerInstance.java | 7 ++++++- 8 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8d826ed2ec1..658ac68a29e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,8 @@ BUGFIXES: ZOOKEEPER-2060 Trace bug in NettyServerCnxnFactory (Ian via fpj) + ZOOKEEPER-2064 Prevent resource leak in various classes (Ted Yu via fpj) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java index 78f0898b125..84a9d983e78 100644 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java +++ b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java @@ -351,11 +351,21 @@ public static void main(String[] args) throws IOException { LogIterator iter = s.iterator(starttime, endtime); System.out.println(iter); + try { + iter.close(); + } catch (IOException ioe) { + System.out.println(ioe.getMessage()); + } }; }; Thread t2 = new Thread() { public void run () { LogIterator iter = s.iterator(starttime, endtime); System.out.println(iter); + try { + iter.close(); + } catch (IOException ioe) { + System.out.println(ioe.getMessage()); + } }; }; Thread t3 = new Thread() { public void run () { diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java index 827a8a7a0a8..13a41a5ae3a 100644 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java +++ b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java @@ -324,5 +324,6 @@ public static void main(String[] args) throws IOException { System.out.println(f.readLine()); f.seek(pos2); System.out.println(f.readLine()); + f.close(); } }; diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java index ed46945816e..5961a125832 100644 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java +++ b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java @@ -81,6 +81,7 @@ String handleRequest(JsonRequest request) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", numEntries=" + size +")"); } + iter.close(); return JSONValue.toJSONString(data); } } diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java index 4af78959a84..d91acb60096 100644 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java +++ b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java @@ -39,9 +39,12 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } - - while (resource.available() > 0) { + try { + while (resource.available() > 0) { response.getWriter().write(resource.read()); + } + } finally { + resource.close(); } // response.setContentType("text/plain;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java index c7730201d4c..93dd63246fb 100644 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java +++ b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java @@ -36,9 +36,13 @@ public RestCfg(String resource) throws IOException { } public RestCfg(InputStream io) throws IOException { + try { cfg.load(io); extractEndpoints(); extractCredentials(); + } finally { + io.close(); + } } private void extractCredentials() { diff --git a/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java b/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java index ce239f9cc0b..cfd4e7b5245 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java +++ b/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java @@ -699,12 +699,16 @@ private static String getMode(String hostPort) throws NumberFormatException, Unk s.getOutputStream().write("stat".getBytes()); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line; - while((line = br.readLine()) != null) { + try { + while((line = br.readLine()) != null) { if (line.startsWith("Mode: ")) { - return line.substring(6); + return line.substring(6); } + } + return "unknown"; + } finally { + s.close(); } - return "unknown"; } private static void doUsage() { diff --git a/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java b/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java index 5fe308aa6f5..e052101317f 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java +++ b/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java @@ -67,7 +67,12 @@ public QuorumPeerInstance() { Properties p; if (zkDirs.exists()) { p = new Properties(); - p.load(new FileInputStream(zkDirs)); + FileInputStream input = new FileInputStream(zkDirs); + try { + p.load(input); + } finally { + input.close(); + } } else { p = System.getProperties(); } From a9718779da86d9e61e2432e6e87c031db311f42f Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Wed, 11 Feb 2015 07:01:42 +0000 Subject: [PATCH 272/444] ZOOKEEPER-1949 recipes jar not included in the distribution package (Rakesh R via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1658890 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/recipes/build-recipes.xml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 658ac68a29e..4118f116bbc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -56,6 +56,9 @@ BUGFIXES: ZOOKEEPER-2064 Prevent resource leak in various classes (Ted Yu via fpj) + ZOOKEEPER-1949 recipes jar not included in the distribution package (Rakesh R + via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/recipes/build-recipes.xml b/src/recipes/build-recipes.xml index dff659df9d2..470f593482f 100644 --- a/src/recipes/build-recipes.xml +++ b/src/recipes/build-recipes.xml @@ -111,7 +111,7 @@ @@ -126,7 +126,7 @@ - + From 7b3ba5fca5802cec37605f8099c6fdead6d3c872 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sun, 22 Feb 2015 21:11:11 +0000 Subject: [PATCH 273/444] ZOOKEEPER-2114 jute generated allocate_* functions are not externally visible (Tim Crowder via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1661562 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/Makefile.am | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4118f116bbc..883ed2aa416 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -59,6 +59,9 @@ BUGFIXES: ZOOKEEPER-1949 recipes jar not included in the distribution package (Rakesh R via michim) + ZOOKEEPER-2114 jute generated allocate_* functions are not externally visible + (Tim Crowder via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/Makefile.am b/src/c/Makefile.am index dbf4080f54e..0a9ccb7f3c5 100644 --- a/src/c/Makefile.am +++ b/src/c/Makefile.am @@ -22,7 +22,7 @@ COMMON_SRC = src/zookeeper.c include/zookeeper.h include/zookeeper_version.h inc src/zookeeper_log.h src/zk_log.c src/zk_hashtable.h src/zk_hashtable.c # These are the symbols (classes, mostly) we want to export from our library. -EXPORT_SYMBOLS = '(zoo_|zookeeper_|zhandle|Z|format_log_message|log_message|logLevel|deallocate_|zerror|is_unrecoverable)' +EXPORT_SYMBOLS = '(zoo_|zookeeper_|zhandle|Z|format_log_message|log_message|logLevel|deallocate_|allocate_|zerror|is_unrecoverable)' noinst_LTLIBRARIES += libzkst.la libzkst_la_SOURCES =$(COMMON_SRC) src/st_adaptor.c libzkst_la_LIBADD = -lm From 415bcd091228caef78e0d8b7bbdbbe67c48dee2f Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sun, 22 Feb 2015 22:08:16 +0000 Subject: [PATCH 274/444] ZOOKEEPER-2073 Memory leak on zookeeper_close (Dave Gosselin via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1661573 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/src/zookeeper.c | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 883ed2aa416..ab3f5d1149d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -62,6 +62,8 @@ BUGFIXES: ZOOKEEPER-2114 jute generated allocate_* functions are not externally visible (Tim Crowder via michim) + ZOOKEEPER-2073 Memory leak on zookeeper_close (Dave Gosselin via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index ba55d2770cd..6d1de8a45da 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -2235,6 +2235,7 @@ int zookeeper_process(zhandle_t *zh, int events) if (zh->close_requested == 1 && cptr == NULL) { LOG_DEBUG(("Completion queue has been cleared by zookeeper_close()")); close_buffer_iarchive(&ia); + free_buffer(bptr); return api_epilog(zh,ZINVALIDSTATE); } assert(cptr); From 2c271faa1517ddaca0747cdf8760a1fb670f955f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Tue, 7 Apr 2015 00:50:58 +0000 Subject: [PATCH 275/444] ZOOKEEPER-2056 Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant (Deiwin Sarjas via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1671716 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ab3f5d1149d..cbefc78570e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -64,6 +64,9 @@ BUGFIXES: ZOOKEEPER-2073 Memory leak on zookeeper_close (Dave Gosselin via michim) + ZOOKEEPER-2056 Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant + (Deiwin Sarjas via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/build.xml b/build.xml index eedc168a08a..9caeb808e08 100644 --- a/build.xml +++ b/build.xml @@ -563,7 +563,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + @@ -604,7 +604,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 87fc5e19bfae81508de96b4bb86d0aa1d5374c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 18 Apr 2015 00:47:56 +0000 Subject: [PATCH 276/444] ZOOKEEPER-2146 BinaryInputArchive readString should check length before allocating memory (Hongchao Deng via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1674424 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../org/apache/jute/BinaryInputArchive.java | 20 +++++---- .../apache/jute/BinaryInputArchiveTest.java | 43 +++++++++++++++++++ 3 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/java/test/org/apache/jute/BinaryInputArchiveTest.java diff --git a/CHANGES.txt b/CHANGES.txt index cbefc78570e..efaf04e0851 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -40,6 +40,9 @@ BUGFIXES: ZOOKEEPER-1222. getACL should only call DataTree.copyStat when passed in stat is not null (Michi Mutsuzaki via rakeshr) + ZOOKEEPER-2146 BinaryInputArchive readString should check length before + allocating memory (Hongchao Deng via michim) + ZOOKEEPER-2039. Jute compareBytes incorrect comparison index (Ian Dimayuga via fpj) ZOOKEEPER-2047 testTruncationNullLog fails on windows (flavio via rakeshr) diff --git a/src/java/main/org/apache/jute/BinaryInputArchive.java b/src/java/main/org/apache/jute/BinaryInputArchive.java index 6b2cb46a854..7722bffc3ba 100644 --- a/src/java/main/org/apache/jute/BinaryInputArchive.java +++ b/src/java/main/org/apache/jute/BinaryInputArchive.java @@ -27,7 +27,7 @@ * */ public class BinaryInputArchive implements InputArchive { - + static public final String UNREASONBLE_LENGTH= "Unreasonable length = "; private DataInput in; static public BinaryInputArchive getArchive(InputStream strm) { @@ -78,6 +78,7 @@ public double readDouble(String tag) throws IOException { public String readString(String tag) throws IOException { int len = in.readInt(); if (len == -1) return null; + checkLength(len); byte b[] = new byte[len]; in.readFully(b); return new String(b, "UTF8"); @@ -88,12 +89,7 @@ public String readString(String tag) throws IOException { public byte[] readBuffer(String tag) throws IOException { int len = readInt(tag); if (len == -1) return null; - // Since this is a rough sanity check, add some padding to maxBuffer to - // make up for extra fields, etc. (otherwise e.g. clients may be able to - // write buffers larger than we can read from disk!) - if (len < 0 || len > maxBuffer + 1024) { - throw new IOException("Unreasonable length = " + len); - } + checkLength(len); byte[] arr = new byte[len]; in.readFully(arr); return arr; @@ -122,5 +118,13 @@ public Index startMap(String tag) throws IOException { } public void endMap(String tag) throws IOException {} - + + // Since this is a rough sanity check, add some padding to maxBuffer to + // make up for extra fields, etc. (otherwise e.g. clients may be able to + // write buffers larger than we can read from disk!) + private void checkLength(int len) throws IOException { + if (len < 0 || len > maxBuffer + 1024) { + throw new IOException(UNREASONBLE_LENGTH + len); + } + } } diff --git a/src/java/test/org/apache/jute/BinaryInputArchiveTest.java b/src/java/test/org/apache/jute/BinaryInputArchiveTest.java new file mode 100644 index 00000000000..a8d067d7d8a --- /dev/null +++ b/src/java/test/org/apache/jute/BinaryInputArchiveTest.java @@ -0,0 +1,43 @@ +/** + * 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.jute; + +import junit.framework.Assert; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + + +public class BinaryInputArchiveTest { + + @Test + public void testReadStringCheckLength() { + byte[] buf = new byte[]{ + Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE}; + ByteArrayInputStream is = new ByteArrayInputStream(buf); + BinaryInputArchive ia = BinaryInputArchive.getArchive(is); + try { + ia.readString(""); + Assert.fail("Should have thrown an IOException"); + } catch (IOException e) { + Assert.assertTrue("Not 'Unreasonable length' exception: " + e, + e.getMessage().startsWith(BinaryInputArchive.UNREASONBLE_LENGTH)); + } + } +} From 7c3fe9d224e318a842b27113b7594cc7a33f88ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 3 May 2015 18:02:51 +0000 Subject: [PATCH 277/444] ZOOKEEPER-2174 JUnit4ZKTestRunner logs test failure for all exceptions JUnit4ZKTestRunner logs test failure for all exceptions, even if the test method is annotated with an expected exception (Chris Nauroth via rgs). git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1677463 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++++ .../apache/zookeeper/JUnit4ZKTestRunner.java | 21 +++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index efaf04e0851..3c0fe3d57bd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -70,6 +70,10 @@ BUGFIXES: ZOOKEEPER-2056 Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant (Deiwin Sarjas via rgs) + ZOOKEEPER-2174 JUnit4ZKTestRunner logs test failure for all exceptions even + if the test method is annotated with an expected exception (Chris Nauroth + via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java b/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java index 588dcd19867..37149ccaaf4 100644 --- a/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java +++ b/src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java @@ -20,6 +20,7 @@ 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; @@ -38,16 +39,18 @@ public JUnit4ZKTestRunner(Class klass) throws InitializationError { } public class LoggedInvokeMethod extends InvokeMethod { - private String name; + 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); + LOG.info("RUNNING TEST METHOD {}", name); try { super.evaluate(); Runtime rt = Runtime.getRuntime(); @@ -59,10 +62,20 @@ public void evaluate() throws Throwable { } LOG.info("Number of threads {}", tg.activeCount()); } catch (Throwable t) { - LOG.info("TEST METHOD FAILED " + name, 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); + LOG.info("FINISHED TEST METHOD {}", name); } } From c7830c532e6a8fe0d14c82f1ad1a3ebcdfc83187 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Fri, 15 May 2015 04:06:54 +0000 Subject: [PATCH 278/444] ZOOKEEPER-2126 Improve exit log messsage of EventThread and SendThread by adding SessionId (surendra singh lilhore via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1679491 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/ClientCnxn.java | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3c0fe3d57bd..7521a36b1b1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -89,6 +89,9 @@ IMPROVEMENTS: ZOOKEEPER-1948 Enable JMX remote monitoring (Biju Nair via rakeshr) + ZOOKEEPER-2126 Improve exit log messsage of EventThread and SendThread by + adding SessionId (surendra singh lilhore via rakeshr) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 6b230d5849b..a9d59b62384 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -509,7 +509,8 @@ public void run() { LOG.error("Event thread exiting due to interruption", e); } - LOG.info("EventThread shut down"); + LOG.info("EventThread shut down for session: 0x{}", + Long.toHexString(getSessionId())); } private void processEvent(Object event) { @@ -1126,7 +1127,8 @@ public void run() { Event.KeeperState.Disconnected, null)); } ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(), - "SendThread exitedloop."); + "SendThread exited loop for session: 0x" + + Long.toHexString(getSessionId())); } private void pingRwServer() throws RWServerFoundException { From 1169a5b5eca3982d24359c27f669af429ff71157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Mon, 18 May 2015 07:38:36 +0000 Subject: [PATCH 279/444] ZOOKEEPER-1077: C client lib doesn't build on Solaris (Chris Nauroth via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1679953 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/c/Makefile.am | 26 ++++++++++++++++++++------ src/c/configure.ac | 10 ++++++++++ src/c/src/zk_log.c | 5 +++-- src/c/tests/LibCMocks.cc | 2 +- src/c/tests/wrappers.opt | 1 + 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7521a36b1b1..a3313386d1e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -74,6 +74,8 @@ BUGFIXES: if the test method is annotated with an expected exception (Chris Nauroth via rgs) + ZOOKEEPER-1077: C client lib doesn't build on Solaris (Chris Nauroth via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/Makefile.am b/src/c/Makefile.am index 0a9ccb7f3c5..5c2315244b8 100644 --- a/src/c/Makefile.am +++ b/src/c/Makefile.am @@ -1,11 +1,15 @@ # need this for Doxygen integration include $(top_srcdir)/aminclude.am -AM_CPPFLAGS = -I${srcdir}/include -I${srcdir}/tests -I${srcdir}/generated +if SOLARIS + SOLARIS_CPPFLAGS = -D_POSIX_PTHREAD_SEMANTICS + SOLARIS_LIB_LDFLAGS = -lnsl -lsocket +endif +AM_CPPFLAGS = -I${srcdir}/include -I${srcdir}/tests -I${srcdir}/generated $(SOLARIS_CPPFLAGS) AM_CFLAGS = -Wall -Werror AM_CXXFLAGS = -Wall $(USEIPV6) -LIB_LDFLAGS = -no-undefined -version-info 2 +LIB_LDFLAGS = -no-undefined -version-info 2 $(SOLARIS_LIB_LDFLAGS) pkginclude_HEADERS = include/zookeeper.h include/zookeeper_version.h include/zookeeper_log.h include/proto.h include/recordio.h generated/zookeeper.jute.h EXTRA_DIST=LICENSE @@ -78,21 +82,31 @@ TEST_SOURCES = tests/TestDriver.cc tests/LibCMocks.cc tests/LibCSymTable.cc \ tests/TestMulti.cc tests/TestWatchers.cc -SYMBOL_WRAPPERS=$(shell cat ${srcdir}/tests/wrappers.opt) +if SOLARIS + SHELL_SYMBOL_WRAPPERS = cat ${srcdir}/tests/wrappers.opt + SYMBOL_WRAPPERS=$(SHELL_SYMBOL_WRAPPERS:sh) +else + SYMBOL_WRAPPERS=$(shell cat ${srcdir}/tests/wrappers.opt) +endif check_PROGRAMS = zktest-st nodist_zktest_st_SOURCES = $(TEST_SOURCES) zktest_st_LDADD = libzkst.la libhashtable.la $(CPPUNIT_LIBS) -zktest_st_CXXFLAGS = -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(USEIPV6) -zktest_st_LDFLAGS = -static-libtool-libs $(SYMBOL_WRAPPERS) +zktest_st_CXXFLAGS = -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(USEIPV6) $(SOLARIS_CPPFLAGS) +zktest_st_LDFLAGS = -static-libtool-libs $(SYMBOL_WRAPPERS) $(SOLARIS_LIB_LDFLAGS) if WANT_SYNCAPI check_PROGRAMS += zktest-mt nodist_zktest_mt_SOURCES = $(TEST_SOURCES) tests/PthreadMocks.cc zktest_mt_LDADD = libzkmt.la libhashtable.la -lpthread $(CPPUNIT_LIBS) zktest_mt_CXXFLAGS = -DUSE_STATIC_LIB -DTHREADED $(CPPUNIT_CFLAGS) $(USEIPV6) +if SOLARIS + SHELL_SYMBOL_WRAPPERS_MT = cat ${srcdir}/tests/wrappers-mt.opt + SYMBOL_WRAPPERS_MT=$(SYMBOL_WRAPPERS) $(SHELL_SYMBOL_WRAPPERS_MT:sh) +else SYMBOL_WRAPPERS_MT=$(SYMBOL_WRAPPERS) $(shell cat ${srcdir}/tests/wrappers-mt.opt) - zktest_mt_LDFLAGS = -static-libtool-libs $(SYMBOL_WRAPPERS_MT) +endif + zktest_mt_LDFLAGS = -static-libtool-libs $(SYMBOL_WRAPPERS_MT) $(SOLARIS_LIB_LDFLAGS) endif run-check: check diff --git a/src/c/configure.ac b/src/c/configure.ac index b4ed639fcd9..b980ac06c37 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -146,4 +146,14 @@ fi AC_CHECK_FUNCS([getcwd gethostbyname gethostname getlogin getpwuid_r gettimeofday getuid memmove memset poll socket strchr strdup strerror strtol]) AC_CONFIG_FILES([Makefile]) +AC_CANONICAL_HOST +AM_CONDITIONAL([SOLARIS],[ + case "$host_os" in + *solaris*) + true + ;; + *) + false + ;; + esac ]) AC_OUTPUT diff --git a/src/c/src/zk_log.c b/src/c/src/zk_log.c index 4dc0f216788..37ff856ca0d 100644 --- a/src/c/src/zk_log.c +++ b/src/c/src/zk_log.c @@ -133,7 +133,8 @@ void log_message(ZooLogLevel curLevel,int line,const char* funcName, #endif if(pid==0)pid=getpid(); #ifndef THREADED - fprintf(LOGSTREAM, "%s:%d:%s@%s@%d: %s\n", time_now(get_time_buffer()),pid, + // pid_t is long on Solaris + fprintf(LOGSTREAM, "%s:%ld:%s@%s@%d: %s\n", time_now(get_time_buffer()),(long)pid, dbgLevelStr[curLevel],funcName,line,message); #else #ifdef WIN32 @@ -141,7 +142,7 @@ void log_message(ZooLogLevel curLevel,int line,const char* funcName, (unsigned long int)(pthread_self().thread_id), dbgLevelStr[curLevel],funcName,line,message); #else - fprintf(LOGSTREAM, "%s:%d(0x%lx):%s@%s@%d: %s\n", time_now(get_time_buffer()),pid, + fprintf(LOGSTREAM, "%s:%ld(0x%lx):%s@%s@%d: %s\n", time_now(get_time_buffer()),(long)pid, (unsigned long int)pthread_self(), dbgLevelStr[curLevel],funcName,line,message); #endif diff --git a/src/c/tests/LibCMocks.cc b/src/c/tests/LibCMocks.cc index 44ab0a9ad38..5db45532e7f 100644 --- a/src/c/tests/LibCMocks.cc +++ b/src/c/tests/LibCMocks.cc @@ -147,7 +147,7 @@ Mock_calloc* Mock_calloc::mock_=0; // realloc #ifndef USING_DUMA -void* realloc(void* p, size_t s){ +DECLARE_WRAPPER(void*,realloc,(void* p, size_t s)){ if(!Mock_realloc::mock_) return LIBC_SYMBOLS.realloc(p,s); return Mock_realloc::mock_->call(p,s); diff --git a/src/c/tests/wrappers.opt b/src/c/tests/wrappers.opt index 963e7ea5806..bce192fcfa9 100644 --- a/src/c/tests/wrappers.opt +++ b/src/c/tests/wrappers.opt @@ -4,3 +4,4 @@ -Wl,--wrap -Wl,get_xid -Wl,--wrap -Wl,deliverWatchers -Wl,--wrap -Wl,activateWatcher +-Wl,--wrap -Wl,realloc From 6b8f0cd84e22494a664fcb7369d15286b083a1ed Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sun, 24 May 2015 06:33:43 +0000 Subject: [PATCH 280/444] ZOOKEEPER-2186 QuorumCnxManager#receiveConnection may crash with random input (rgs via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1681417 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../server/quorum/QuorumCnxManager.java | 20 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a3313386d1e..ada7acfa01a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -76,6 +76,9 @@ BUGFIXES: ZOOKEEPER-1077: C client lib doesn't build on Solaris (Chris Nauroth via rgs) + ZOOKEEPER-2186 QuorumCnxManager#receiveConnection may crash with random input + (rgs via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index eacea312b27..dea70da4852 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -78,6 +78,11 @@ public class QuorumCnxManager { */ static final int MAX_CONNECTION_ATTEMPTS = 2; + + /* + * Max buffer size to be read from the network. + */ + static public final int maxBuffer = 2048; /* * Negative counter for observer server ids. @@ -228,7 +233,7 @@ public boolean initiateConnection(Socket sock, Long sid) { * possible long value to lose the challenge. * */ - public boolean receiveConnection(Socket sock) { + public void receiveConnection(Socket sock) { Long sid = null; try { @@ -237,9 +242,17 @@ public boolean receiveConnection(Socket sock) { sid = din.readLong(); if (sid < 0) { // this is not a server id but a protocol version (see ZOOKEEPER-1633) sid = din.readLong(); + // next comes the #bytes in the remainder of the message + // note that 0 bytes is fine (old servers) int num_remaining_bytes = din.readInt(); + if (num_remaining_bytes < 0 || num_remaining_bytes > maxBuffer) { + LOG.error("Unreasonable buffer length: {}", num_remaining_bytes); + closeSocket(sock); + return; + } byte[] b = new byte[num_remaining_bytes]; + // remove the remainder of the message from din int num_read = din.read(b); if (num_read != num_remaining_bytes) { @@ -258,7 +271,7 @@ public boolean receiveConnection(Socket sock) { } catch (IOException e) { closeSocket(sock); LOG.warn("Exception reading or writing challenge: " + e.toString()); - return false; + return; } //If wins the challenge, then close the new connection. @@ -301,9 +314,8 @@ public boolean receiveConnection(Socket sock) { sw.start(); rw.start(); - return true; + return; } - return false; } /** From ba96bd8a68c8eacab3a9e61b401cab0f35c7c314 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sun, 24 May 2015 06:37:32 +0000 Subject: [PATCH 281/444] ZOOKEEPER-2124 Allow Zookeeper version string to have underscore '_' (Chris Nauroth via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1681418 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 13 ++++++++++++- src/contrib/zkpython/build.xml | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ada7acfa01a..070318ecf01 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -97,6 +97,9 @@ IMPROVEMENTS: ZOOKEEPER-2126 Improve exit log messsage of EventThread and SendThread by adding SessionId (surendra singh lilhore via rakeshr) + ZOOKEEPER-2124 Allow Zookeeper version string to have underscore '_' + (Chris Nauroth via michim) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/build.xml b/build.xml index 9caeb808e08..760912e1515 100644 --- a/build.xml +++ b/build.xml @@ -981,10 +981,21 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + + + + + + + + + + @@ -1010,7 +1021,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/src/contrib/zkpython/build.xml b/src/contrib/zkpython/build.xml index d8254d14b0a..029d4f29cff 100644 --- a/src/contrib/zkpython/build.xml +++ b/src/contrib/zkpython/build.xml @@ -132,7 +132,7 @@ - + From 6ce171a99878f745c0b7f212e27c9064f95cc4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Fri, 29 May 2015 19:51:58 +0000 Subject: [PATCH 282/444] ZOOKEEPER-2179: Typo in Watcher.java (Archana T via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1682541 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ src/java/main/org/apache/zookeeper/Watcher.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 070318ecf01..11bb660ec02 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -79,6 +79,8 @@ BUGFIXES: ZOOKEEPER-2186 QuorumCnxManager#receiveConnection may crash with random input (rgs via michim) + ZOOKEEPER-2179: Typo in Watcher.java (Archana T via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/Watcher.java b/src/java/main/org/apache/zookeeper/Watcher.java index 36c7b5b8d39..12f2af36bfa 100644 --- a/src/java/main/org/apache/zookeeper/Watcher.java +++ b/src/java/main/org/apache/zookeeper/Watcher.java @@ -20,7 +20,7 @@ /** * This interface specifies the public interface an event handler class must - * implement. A ZooKeeper client will get various events from the ZooKeepr + * implement. A ZooKeeper client will get various events from the ZooKeeper * server it connects to. An application using such a client handles these * events by registering a callback object with the client. The callback object * is expected to be an instance of a class that implements Watcher interface. From e269cd5f4f76e8e2472de47354525674ad434659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Tue, 2 Jun 2015 20:43:29 +0000 Subject: [PATCH 283/444] ZOOKEEPER-2096: C client builds with incorrect error codes in VisualStudio 2010+ (Vitaly Stakhovsky via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1683180 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/include/winconfig.h | 5 +++++ src/c/src/zookeeper.c | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 11bb660ec02..7add2578006 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -81,6 +81,9 @@ BUGFIXES: ZOOKEEPER-2179: Typo in Watcher.java (Archana T via rgs) + ZOOKEEPER-2096: C client builds with incorrect error codes in VisualStudio 2010+ + (Vitaly Stakhovsky via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index a68dcc6287e..d3c80625a08 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -184,8 +184,13 @@ #define EHOSTDOWN EPIPE #define ESTALE ENODEV +#ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS +#endif typedef int pid_t; #endif diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 6d1de8a45da..00e62749281 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1599,6 +1599,16 @@ int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, rc = connect(zh->fd, (struct sockaddr*) &zh->addrs[zh->connect_index], sizeof(struct sockaddr_in)); #ifdef WIN32 get_errno(); +#if _MSC_VER >= 1600 + switch (errno) { + case WSAEWOULDBLOCK: + errno = EWOULDBLOCK; + break; + case WSAEINPROGRESS: + errno = EINPROGRESS; + break; + } +#endif #endif } if (rc == -1) { From a351209b3bd7e3123ce742789063784f6322d7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 4 Jun 2015 16:27:19 +0000 Subject: [PATCH 284/444] ZOOKEEPER-2194: Let DataNode.getChildren() return an unmodifiable view of its children set (Hitoshi Mitake via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1683589 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/server/DataNode.java | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7add2578006..7057550137b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -84,6 +84,9 @@ BUGFIXES: ZOOKEEPER-2096: C client builds with incorrect error codes in VisualStudio 2010+ (Vitaly Stakhovsky via rgs) + ZOOKEEPER-2194: Let DataNode.getChildren() return an unmodifiable view of its children set + (Hitoshi Mitake via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/DataNode.java b/src/java/main/org/apache/zookeeper/server/DataNode.java index 9498204fc56..4c79d55439d 100644 --- a/src/java/main/org/apache/zookeeper/server/DataNode.java +++ b/src/java/main/org/apache/zookeeper/server/DataNode.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.HashSet; import java.util.Set; +import java.util.Collections; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; @@ -128,7 +129,11 @@ public synchronized void setChildren(HashSet children) { * @return the children of this datanode */ public synchronized Set getChildren() { - return children; + if (children == null) { + return children; + } + + return Collections.unmodifiableSet(children); } synchronized public void copyStat(Stat to) { From 67cb77e74d0d8389a5d58781d7ed9b44ca3b3c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Fri, 5 Jun 2015 18:44:25 +0000 Subject: [PATCH 285/444] ZOOKEEPER-2205: Log type of unexpected quorum packet in learner handler loop (Hitoshi Mitake via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1683838 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/server/quorum/LearnerHandler.java | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7057550137b..e8ceb902903 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -108,6 +108,9 @@ IMPROVEMENTS: ZOOKEEPER-2124 Allow Zookeeper version string to have underscore '_' (Chris Nauroth via michim) + ZOOKEEPER-2205: Log type of unexpected quorum packet in learner handler loop + (Hitoshi Mitake via rgs) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 01e79dc1b00..bf53c7a9f5a 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -230,8 +230,6 @@ private void sendPackets() throws InterruptedException { } static public String packetToString(QuorumPacket p) { - if (true) - return null; String type = null; String mess = null; Record txn = null; @@ -626,6 +624,8 @@ public void run() { leader.zk.submitRequest(si); break; default: + LOG.warn("unexpected quorum packet, type: {}", packetToString(qp)); + break; } } } catch (IOException e) { From 67019674379e6fac647d5eb23b694f1172d2849f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 6 Jun 2015 16:53:10 +0000 Subject: [PATCH 286/444] ZOOKEEPER-2201: Network issues can cause cluster to hang due to near-deadlock (Donny Nadolny via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1683931 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../org/apache/zookeeper/server/DataTree.java | 10 +++- .../apache/zookeeper/server/DataTreeTest.java | 56 +++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e8ceb902903..bbc8e373a88 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -87,6 +87,9 @@ BUGFIXES: ZOOKEEPER-2194: Let DataNode.getChildren() return an unmodifiable view of its children set (Hitoshi Mitake via rgs) + ZOOKEEPER-2201: Network issues can cause cluster to hang due to near-deadlock + (Donny Nadolny via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index 363b256b64b..47ef6e2d81c 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -1109,15 +1109,21 @@ void serializeNode(OutputArchive oa, StringBuilder path) throws IOException { return; } String children[] = null; + DataNode nodeCopy; synchronized (node) { scount++; - oa.writeString(pathString, "path"); - oa.writeRecord(node, "node"); + StatPersisted statCopy = new StatPersisted(); + copyStatPersisted(node.stat, statCopy); + //we do not need to make a copy of node.data because the contents + //are never changed + nodeCopy = new DataNode(node.parent, node.data, node.acl, statCopy); Set childs = node.getChildren(); if (childs != null) { children = childs.toArray(new String[childs.size()]); } } + oa.writeString(pathString, "path"); + oa.writeRecord(nodeCopy, "node"); path.append('/'); int off = path.length(); if (children != null) { diff --git a/src/java/test/org/apache/zookeeper/server/DataTreeTest.java b/src/java/test/org/apache/zookeeper/server/DataTreeTest.java index ebc2fca0cd5..a484cf58ef9 100644 --- a/src/java/test/org/apache/zookeeper/server/DataTreeTest.java +++ b/src/java/test/org/apache/zookeeper/server/DataTreeTest.java @@ -34,14 +34,19 @@ import org.apache.zookeeper.server.DataNode; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import org.apache.zookeeper.Quotas; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; +import org.apache.jute.Record; import org.apache.zookeeper.common.PathTrie; import java.lang.reflect.*; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class DataTreeTest extends ZKTestCase { @@ -176,4 +181,55 @@ public void testPathTrieClearOnDeserialize() throws Exception { //Check that the node path is removed from pTrie Assert.assertEquals("/bug is still in pTrie", "", pTrie.findMaxPrefix("/bug")); } + + /* + * ZOOKEEPER-2201 - OutputArchive.writeRecord can block for long periods of + * time, we must call it outside of the node lock. + * We call tree.serialize, which calls our modified writeRecord method that + * blocks until it can verify that a separate thread can lock the DataNode + * currently being written, i.e. that DataTree.serializeNode does not hold + * the DataNode lock while calling OutputArchive.writeRecord. + */ + @Test(timeout = 60000) + public void testSerializeDoesntLockDataNodeWhileWriting() throws Exception { + DataTree tree = new DataTree(); + tree.createNode("/marker", new byte[] {42}, null, -1, 1, 1, 1); + final DataNode markerNode = tree.getNode("/marker"); + final AtomicBoolean ranTestCase = new AtomicBoolean(); + DataOutputStream out = new DataOutputStream(new ByteArrayOutputStream()); + BinaryOutputArchive oa = new BinaryOutputArchive(out) { + @Override + public void writeRecord(Record r, String tag) throws IOException { + DataNode node = (DataNode) r; + if (node.data.length == 1 && node.data[0] == 42) { + final Semaphore semaphore = new Semaphore(0); + new Thread(new Runnable() { + @Override + public void run() { + synchronized (markerNode) { + //When we lock markerNode, allow writeRecord to continue + semaphore.release(); + } + } + }).start(); + + try { + boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS); + //This is the real assertion - could another thread lock + //the DataNode we're currently writing + Assert.assertTrue("Couldn't acquire a lock on the DataNode while we were calling tree.serialize", acquired); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + ranTestCase.set(true); + } + super.writeRecord(r, tag); + } + }; + + tree.serialize(oa, "test"); + + //Let's make sure that we hit the code that ran the real assertion above + Assert.assertTrue("Didn't find the expected node", ranTestCase.get()); + } } From 6fdb35ff1e1d3fbd1f5f22f9d4a8581ef8244851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 11 Jun 2015 18:14:21 +0000 Subject: [PATCH 287/444] ZOOKEEPER-2213: Empty path in Set crashes server and prevents restart (Hongchao Deng via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1684958 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../server/PrepRequestProcessor.java | 22 +++++++---- .../server/PrepRequestProcessorTest.java | 37 +++++++++++++------ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bbc8e373a88..374afe05180 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -90,6 +90,9 @@ BUGFIXES: ZOOKEEPER-2201: Network issues can cause cluster to hang due to near-deadlock (Donny Nadolny via rgs) + ZOOKEEPER-2213: Empty path in Set crashes server and prevents restart + (Hongchao Deng via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 4e1e52684e5..c08df59d306 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -39,6 +39,7 @@ import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.KeeperException.BadArgumentsException; import org.apache.zookeeper.MultiTransactionRecord; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs; @@ -351,13 +352,7 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, if (createMode.isSequential()) { path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion); } - try { - PathUtils.validatePath(path); - } catch(IllegalArgumentException ie) { - LOG.info("Invalid path " + path + " with session 0x" + - Long.toHexString(request.sessionId)); - throw new KeeperException.BadArgumentsException(path); - } + validatePath(path, request.sessionId); try { if (getRecordForPath(path) != null) { throw new KeeperException.NodeExistsException(path); @@ -420,6 +415,7 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, if(deserialize) ByteBufferInputStream.byteBuffer2Record(request.request, setDataRequest); path = setDataRequest.getPath(); + validatePath(path, request.sessionId); nodeRecord = getRecordForPath(path); checkACL(zks, nodeRecord.acl, ZooDefs.Perms.WRITE, request.authInfo); @@ -440,6 +436,7 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, if(deserialize) ByteBufferInputStream.byteBuffer2Record(request.request, setAclRequest); path = setAclRequest.getPath(); + validatePath(path, request.sessionId); listACL = removeDuplicates(setAclRequest.getAcl()); if (!fixupACL(request.authInfo, listACL)) { throw new KeeperException.InvalidACLException(path); @@ -499,6 +496,7 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, if(deserialize) ByteBufferInputStream.byteBuffer2Record(request.request, checkVersionRequest); path = checkVersionRequest.getPath(); + validatePath(path, request.sessionId); nodeRecord = getRecordForPath(path); checkACL(zks, nodeRecord.acl, ZooDefs.Perms.READ, request.authInfo); @@ -513,6 +511,16 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, } } + private void validatePath(String path, long sessionId) throws BadArgumentsException { + try { + PathUtils.validatePath(path); + } catch(IllegalArgumentException ie) { + LOG.info("Invalid path " + path + " with session 0x" + Long.toHexString(sessionId) + + ", reason: " + ie.getMessage()); + throw new BadArgumentsException(path); + } + } + /** * This method will be called inside the ProcessRequestThread, which is a * singleton, so there will be a single thread calling this code. diff --git a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java index 1d09b136e5b..d86cf2a7f67 100644 --- a/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java +++ b/src/java/test/org/apache/zookeeper/server/PrepRequestProcessorTest.java @@ -29,6 +29,7 @@ import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.data.Id; +import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.server.ZooKeeperServer.ChangeRecord; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.ErrorTxn; @@ -47,6 +48,7 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; public class PrepRequestProcessorTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(PrepRequestProcessorTest.class); @@ -93,13 +95,10 @@ public void testPRequest() throws Exception { Assert.assertEquals("Request should have marshalling error", new ErrorTxn(KeeperException.Code.MARSHALLINGERROR.intValue()), outcome.txn); - Assert.assertTrue("request hasn't been processed in chain", - pLatch.await(5, java.util.concurrent.TimeUnit.SECONDS)); + Assert.assertTrue("request hasn't been processed in chain", pLatch.await(5, TimeUnit.SECONDS)); } - private Request createMultiRequest(List ops) throws IOException { - Record record = new MultiTransactionRecord(ops); - + private Request createRequest(Record record, int opCode) throws IOException { // encoding ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); @@ -109,18 +108,18 @@ private Request createMultiRequest(List ops) throws IOException { // Id List ids = Arrays.asList(Ids.ANYONE_ID_UNSAFE); - return new Request(null, 1l, 0, OpCode.multi, ByteBuffer.wrap(baos.toByteArray()), ids); + return new Request(null, 1l, 0, opCode, ByteBuffer.wrap(baos.toByteArray()), ids); } private void process(List ops) throws Exception { pLatch = new CountDownLatch(1); processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); - Request req = createMultiRequest(ops); + Record record = new MultiTransactionRecord(ops); + Request req = createRequest(record, OpCode.multi); processor.pRequest(req); - Assert.assertTrue("request hasn't been processed in chain", - pLatch.await(5, java.util.concurrent.TimeUnit.SECONDS)); + Assert.assertTrue("request hasn't been processed in chain", pLatch.await(5, TimeUnit.SECONDS)); } /** @@ -168,9 +167,6 @@ public void testMultiRollbackNoLastChange() throws Exception { zks.getZKDatabase().dataTree.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); zks.getZKDatabase().dataTree.createNode("/foo/bar", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); - pLatch = new CountDownLatch(1); - processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); - Assert.assertNull(zks.outstandingChangesForPath.get("/foo")); // multi record: @@ -184,6 +180,23 @@ public void testMultiRollbackNoLastChange() throws Exception { Assert.assertNull(zks.outstandingChangesForPath.get("/foo")); } + /** + * It tests that PrepRequestProcessor will return BadArgument KeeperException + * if the request path (if it exists) is not valid, e.g. empty string. + */ + @Test + public void testInvalidPath() throws Exception { + pLatch = new CountDownLatch(1); + processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); + + SetDataRequest record = new SetDataRequest("", new byte[0], -1); + Request req = createRequest(record, OpCode.setData); + processor.pRequest(req); + pLatch.await(); + Assert.assertEquals(outcome.hdr.getType(), OpCode.error); + Assert.assertEquals(outcome.getException().code(), KeeperException.Code.BADARGUMENTS); + } + private class MyRequestProcessor implements RequestProcessor { @Override public void processRequest(Request request) { From 62888591c33968c3bf4fc79bb843cae9ff3ae5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 13 Jun 2015 00:49:48 +0000 Subject: [PATCH 288/444] ZOOKEEPER-706: Large numbers of watches can cause session re-establishment to fail (Chris Thunes via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1685205 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../main/org/apache/zookeeper/ClientCnxn.java | 63 ++++++++++++--- .../test/DisconnectedWatcherTest.java | 81 +++++++++++++++++++ 3 files changed, 138 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 374afe05180..67e0a566031 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -93,6 +93,9 @@ BUGFIXES: ZOOKEEPER-2213: Empty path in Set crashes server and prevents restart (Hongchao Deng via rgs) + ZOOKEEPER-706: Large numbers of watches can cause session re-establishment to fail + (Chris Thunes via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index a9d59b62384..08fd1b7594f 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -28,6 +28,8 @@ import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Random; @@ -88,6 +90,16 @@ public class ClientCnxn { private static final String ZK_SASL_CLIENT_USERNAME = "zookeeper.sasl.client.username"; + /* ZOOKEEPER-706: If a session has a large number of watches set then + * attempting to re-establish those watches after a connection loss may + * fail due to the SetWatches request exceeding the server's configured + * jute.maxBuffer value. To avoid this we instead split the watch + * re-establishement across multiple SetWatches calls. This constant + * controls the size of each call. It is set to 128kB to be conservative + * with respect to the server's 1MB default for jute.maxBuffer. + */ + private static final int SET_WATCHES_MAX_LENGTH = 128 * 1024; + /** This controls whether automatic watch resetting is enabled. * Clients automatically reset watches during session reconnect, this * option allows the client to turn off this behavior by setting @@ -868,15 +880,48 @@ void primeConnection() throws IOException { List childWatches = zooKeeper.getChildWatches(); if (!dataWatches.isEmpty() || !existWatches.isEmpty() || !childWatches.isEmpty()) { - SetWatches sw = new SetWatches(lastZxid, - prependChroot(dataWatches), - prependChroot(existWatches), - prependChroot(childWatches)); - RequestHeader h = new RequestHeader(); - h.setType(ZooDefs.OpCode.setWatches); - h.setXid(-8); - Packet packet = new Packet(h, new ReplyHeader(), sw, null, null); - outgoingQueue.addFirst(packet); + + Iterator dataWatchesIter = prependChroot(dataWatches).iterator(); + Iterator existWatchesIter = prependChroot(existWatches).iterator(); + Iterator childWatchesIter = prependChroot(childWatches).iterator(); + long setWatchesLastZxid = lastZxid; + + while (dataWatchesIter.hasNext() + || existWatchesIter.hasNext() || childWatchesIter.hasNext()) { + List dataWatchesBatch = new ArrayList(); + List existWatchesBatch = new ArrayList(); + List childWatchesBatch = new ArrayList(); + int batchLength = 0; + + // Note, we may exceed our max length by a bit when we add the last + // watch in the batch. This isn't ideal, but it makes the code simpler. + while (batchLength < SET_WATCHES_MAX_LENGTH) { + final String watch; + if (dataWatchesIter.hasNext()) { + watch = dataWatchesIter.next(); + dataWatchesBatch.add(watch); + } else if (existWatchesIter.hasNext()) { + watch = existWatchesIter.next(); + existWatchesBatch.add(watch); + } else if (childWatchesIter.hasNext()) { + watch = childWatchesIter.next(); + childWatchesBatch.add(watch); + } else { + break; + } + batchLength += watch.length(); + } + + SetWatches sw = new SetWatches(setWatchesLastZxid, + dataWatchesBatch, + existWatchesBatch, + childWatchesBatch); + RequestHeader h = new RequestHeader(); + h.setType(ZooDefs.OpCode.setWatches); + h.setXid(-8); + Packet packet = new Packet(h, new ReplyHeader(), sw, null, null); + outgoingQueue.addFirst(packet); + } } } diff --git a/src/java/test/org/apache/zookeeper/test/DisconnectedWatcherTest.java b/src/java/test/org/apache/zookeeper/test/DisconnectedWatcherTest.java index 4a76f124bfe..975d225e02d 100644 --- a/src/java/test/org/apache/zookeeper/test/DisconnectedWatcherTest.java +++ b/src/java/test/org/apache/zookeeper/test/DisconnectedWatcherTest.java @@ -18,6 +18,8 @@ package org.apache.zookeeper.test; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -173,4 +175,83 @@ public void testDeepChildWatcherAutoResetWithChroot() throws Exception { Assert.assertEquals(EventType.NodeChildrenChanged, e.getType()); Assert.assertEquals("/are", e.getPath()); } + + // @see jira issue ZOOKEEPER-706. Test auto reset of a large number of + // watches which require multiple SetWatches calls. + @Test + public void testManyChildWatchersAutoReset() throws Exception { + ZooKeeper zk1 = createClient(); + + MyWatcher watcher = new MyWatcher(); + ZooKeeper zk2 = createClient(watcher); + + // 110 character base path + String pathBase = "/long-path-000000000-111111111-222222222-333333333-444444444-" + + "555555555-666666666-777777777-888888888-999999999"; + + zk1.create(pathBase, null, Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + // Create 10,000 nodes. This should ensure the length of our + // watches set below exceeds 1MB. + List paths = new ArrayList(); + for (int i = 0; i < 10000; i++) { + String path = zk1.create(pathBase + "/ch-", null, Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + paths.add(path); + } + + MyWatcher childWatcher = new MyWatcher(); + + // Set a combination of child/exists/data watches + int i = 0; + for (String path : paths) { + if (i % 3 == 0) { + zk2.getChildren(path, childWatcher); + } else if (i % 3 == 1) { + zk2.exists(path + "/foo", childWatcher); + } else if (i % 3 == 2) { + zk2.getData(path, childWatcher, null); + } + + i++; + } + + stopServer(); + watcher.waitForDisconnected(30000); + startServer(); + watcher.waitForConnected(30000); + + // Trigger the watches and ensure they properly propagate to the client + i = 0; + for (String path : paths) { + if (i % 3 == 0) { + zk1.create(path + "/ch", null, Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + WatchedEvent e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertNotNull(e); + Assert.assertEquals(EventType.NodeChildrenChanged, e.getType()); + Assert.assertEquals(path, e.getPath()); + } else if (i % 3 == 1) { + zk1.create(path + "/foo", null, Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + + WatchedEvent e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertNotNull(e); + Assert.assertEquals(EventType.NodeCreated, e.getType()); + Assert.assertEquals(path + "/foo", e.getPath()); + } else if (i % 3 == 2) { + zk1.setData(path, new byte[]{1, 2, 3}, -1); + + WatchedEvent e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); + Assert.assertNotNull(e); + Assert.assertEquals(EventType.NodeDataChanged, e.getType()); + Assert.assertEquals(path, e.getPath()); + } + + i++; + } + } + } From b407b1593ec6d1b3879f532a3aff700cec50bf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 18 Jun 2015 18:58:48 +0000 Subject: [PATCH 289/444] ZOOKEEPER-602: log all exceptions not caught by ZK threads (Rakesh R via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1686289 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../main/org/apache/zookeeper/ClientCnxn.java | 14 +--- .../server/NIOServerCnxnFactory.java | 7 +- .../server/PrepRequestProcessor.java | 3 +- .../zookeeper/server/SessionTrackerImpl.java | 2 +- .../server/SyncRequestProcessor.java | 6 +- .../server/ZooKeeperCriticalThread.java | 50 ++++++++++++ .../zookeeper/server/ZooKeeperThread.java | 62 +++++++++++++++ .../server/quorum/AuthFastLeaderElection.java | 7 +- .../server/quorum/CommitProcessor.java | 3 +- .../server/quorum/FastLeaderElection.java | 7 +- .../quorum/FollowerRequestProcessor.java | 3 +- .../zookeeper/server/quorum/Leader.java | 11 ++- .../server/quorum/LearnerHandler.java | 3 +- .../quorum/ObserverRequestProcessor.java | 3 +- .../server/quorum/QuorumCnxManager.java | 13 +++- .../zookeeper/server/quorum/QuorumPeer.java | 5 +- .../quorum/ReadOnlyRequestProcessor.java | 3 +- src/java/test/config/findbugsExcludeFile.xml | 4 +- .../zookeeper/server/ZooKeeperThreadTest.java | 78 +++++++++++++++++++ 20 files changed, 244 insertions(+), 43 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java create mode 100644 src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java create mode 100644 src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 67e0a566031..63a80204ce2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -96,6 +96,9 @@ BUGFIXES: ZOOKEEPER-706: Large numbers of watches can cause session re-establishment to fail (Chris Thunes via rgs) + ZOOKEEPER-602: log all exceptions not caught by ZK threads + (Rakesh R via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 08fd1b7594f..2435e321f0b 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -74,6 +74,7 @@ import org.apache.zookeeper.proto.SetWatches; import org.apache.zookeeper.proto.WatcherEvent; import org.apache.zookeeper.server.ByteBufferInputStream; +import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.ZooTrace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -420,13 +421,6 @@ public void start() { private Object eventOfDeath = new Object(); - private final static UncaughtExceptionHandler uncaughtExceptionHandler = new UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - LOG.error("from " + t.getName(), e); - } - }; - private static class WatcherSetEventPair { private final Set watchers; private final WatchedEvent event; @@ -448,7 +442,7 @@ private static String makeThreadName(String suffix) { return name + suffix; } - class EventThread extends Thread { + class EventThread extends ZooKeeperThread { private final LinkedBlockingQueue waitingEvents = new LinkedBlockingQueue(); @@ -463,7 +457,6 @@ class EventThread extends Thread { EventThread() { super(makeThreadName("-EventThread")); - setUncaughtExceptionHandler(uncaughtExceptionHandler); setDaemon(true); } @@ -711,7 +704,7 @@ public RWServerFoundException(String msg) { * This class services the outgoing request queue and generates the heart * beats. It also spawns the ReadThread. */ - class SendThread extends Thread { + class SendThread extends ZooKeeperThread { private long lastPingSentNs; private final ClientCnxnSocket clientCnxnSocket; private Random r = new Random(System.nanoTime()); @@ -840,7 +833,6 @@ else if (serverPath.length() > chrootPath.length()) super(makeThreadName("-SendThread()")); state = States.CONNECTING; this.clientCnxnSocket = clientCnxnSocket; - setUncaughtExceptionHandler(uncaughtExceptionHandler); setDaemon(true); } diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java index a74bd1ee751..bd86cdd2df3 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java @@ -39,11 +39,6 @@ public class NIOServerCnxnFactory extends ServerCnxnFactory implements Runnable private static final Logger LOG = LoggerFactory.getLogger(NIOServerCnxnFactory.class); static { - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - public void uncaughtException(Thread t, Throwable e) { - LOG.error("Thread " + t + " died", e); - } - }); /** * this is to avoid the jvm bug: * NullPointerException in Selector.open() @@ -86,7 +81,7 @@ public NIOServerCnxnFactory() throws IOException { public void configure(InetSocketAddress addr, int maxcc) throws IOException { configureSaslLogin(); - thread = new Thread(this, "NIOServerCxn.Factory:" + addr); + thread = new ZooKeeperThread(this, "NIOServerCxn.Factory:" + addr); thread.setDaemon(true); maxClientCnxns = maxcc; this.ss = ServerSocketChannel.open(); diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index c08df59d306..34392d37131 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -76,7 +76,8 @@ * outstandingRequests, so that it can take into account transactions that are * in the queue to be applied when generating a transaction. */ -public class PrepRequestProcessor extends Thread implements RequestProcessor { +public class PrepRequestProcessor extends ZooKeeperCriticalThread implements + RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(PrepRequestProcessor.class); static boolean skipACL; diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java index 0cf2fa9fc6f..b5edd661b2a 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java @@ -39,7 +39,7 @@ * period. Sessions are thus expired in batches made up of sessions that expire * in a given interval. */ -public class SessionTrackerImpl extends Thread implements SessionTracker { +public class SessionTrackerImpl extends ZooKeeperCriticalThread implements SessionTracker { private static final Logger LOG = LoggerFactory.getLogger(SessionTrackerImpl.class); HashMap sessionsById = new HashMap(); diff --git a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java index 92999614766..a37aa42974d 100644 --- a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java @@ -44,7 +44,7 @@ * be null. This change the semantic of txnlog on the observer * since it only contains committed txns. */ -public class SyncRequestProcessor extends Thread implements RequestProcessor { +public class SyncRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(SyncRequestProcessor.class); private final ZooKeeperServer zks; private final LinkedBlockingQueue queuedRequests = @@ -147,7 +147,7 @@ public void run() { if (snapInProcess != null && snapInProcess.isAlive()) { LOG.warn("Too busy to snap, skipping"); } else { - snapInProcess = new Thread("Snapshot Thread") { + snapInProcess = new ZooKeeperThread("Snapshot Thread") { public void run() { try { zks.takeSnapshot(); @@ -180,7 +180,7 @@ public void run() { } } } catch (Throwable t) { - LOG.error("Severe unrecoverable error, exiting", t); + super.handleException(this.getName(), t); running = false; System.exit(11); } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java new file mode 100644 index 00000000000..138d22ea8e1 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.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.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents critical thread. When there is an uncaught exception thrown by the + * thread this will exit the system. + */ +public class ZooKeeperCriticalThread extends ZooKeeperThread { + private static final Logger LOG = LoggerFactory + .getLogger(ZooKeeperCriticalThread.class); + private static final int DEFAULT_EXIT_CODE = 1; + + public ZooKeeperCriticalThread(String threadName) { + super(threadName); + } + + /** + * This will be used by the uncaught exception handler and make the system + * exit. + * + * @param thName + * - thread name + * @param e + * - exception object + */ + @Override + protected void handleException(String thName, Throwable e) { + LOG.error("Severe unrecoverable error, from thread : {}", thName, e); + System.exit(DEFAULT_EXIT_CODE); + } +} diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java new file mode 100644 index 00000000000..2830624f41e --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java @@ -0,0 +1,62 @@ +/** + * 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.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is the main class for catching all the uncaught exceptions thrown by the + * threads. + */ +public class ZooKeeperThread extends Thread { + + private static final Logger LOG = LoggerFactory + .getLogger(ZooKeeperThread.class); + + private UncaughtExceptionHandler uncaughtExceptionalHandler = new UncaughtExceptionHandler() { + + @Override + public void uncaughtException(Thread t, Throwable e) { + handleException(t.getName(), e); + } + }; + + public ZooKeeperThread(Runnable thread, String threadName) { + super(thread, threadName); + setUncaughtExceptionHandler(uncaughtExceptionalHandler); + } + + public ZooKeeperThread(String threadName) { + super(threadName); + setUncaughtExceptionHandler(uncaughtExceptionalHandler); + } + + /** + * This will be used by the uncaught exception handler and just log a + * warning message and return. + * + * @param thName + * - thread name + * @param e + * - exception object + */ + protected void handleException(String thName, Throwable e) { + LOG.warn("Exception occured from thread {}", thName, e); + } +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java index ad6019a7e89..5bf54f81498 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java @@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.MBeanRegistry; +import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.quorum.Election; import org.apache.zookeeper.server.quorum.Vote; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; @@ -716,7 +717,7 @@ public boolean queueEmpty() { lastEpoch = 0; for (int i = 0; i < threads; ++i) { - Thread t = new Thread(new WorkerSender(3), + Thread t = new ZooKeeperThread(new WorkerSender(3), "WorkerSender Thread: " + (i + 1)); t.setDaemon(true); t.start(); @@ -728,8 +729,8 @@ public boolean queueEmpty() { addrChallengeMap.put(saddr, new ConcurrentHashMap()); } - Thread t = new Thread(new WorkerReceiver(s, this), - "WorkerReceiver Thread"); + Thread t = new ZooKeeperThread(new WorkerReceiver(s, this), + "WorkerReceiver-" + s.getRemoteSocketAddress()); t.start(); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java index a0c2cbdd0c3..36f5390cd8f 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java @@ -26,6 +26,7 @@ import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; +import org.apache.zookeeper.server.ZooKeeperCriticalThread; /** * This RequestProcessor matches the incoming committed requests with the @@ -33,7 +34,7 @@ * change the state of the system will come back as incoming committed requests, * so we need to match them up. */ -public class CommitProcessor extends Thread implements RequestProcessor { +public class CommitProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(CommitProcessor.class); /** diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index 6b79d04a1ec..78f3aa6687c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit; import org.apache.zookeeper.jmx.MBeanRegistry; +import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.quorum.QuorumCnxManager.Message; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; @@ -227,11 +228,12 @@ protected class Messenger { * method run(), and processes such messages. */ - class WorkerReceiver implements Runnable { + class WorkerReceiver extends ZooKeeperThread { volatile boolean stop; QuorumCnxManager manager; WorkerReceiver(QuorumCnxManager manager) { + super("WorkerReceiver"); this.stop = false; this.manager = manager; } @@ -412,11 +414,12 @@ public void run() { * and queues it on the manager's queue. */ - class WorkerSender implements Runnable { + class WorkerSender extends ZooKeeperThread { volatile boolean stop; QuorumCnxManager manager; WorkerSender(QuorumCnxManager manager){ + super("WorkerSender"); this.stop = false; this.manager = manager; } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java index a1c8ce23dea..6388589fb48 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java @@ -26,13 +26,14 @@ import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooTrace; /** * This RequestProcessor forwards any requests that modify the state of the * system to the Leader. */ -public class FollowerRequestProcessor extends Thread implements +public class FollowerRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(FollowerRequestProcessor.class); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index dd11a91dc7f..40c6748f369 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -21,7 +21,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.BindException; -import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; @@ -39,10 +38,10 @@ import java.util.concurrent.ConcurrentMap; import org.apache.jute.BinaryOutputArchive; -import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; +import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ZxidUtils; @@ -302,9 +301,13 @@ boolean isLearnerSynced(LearnerHandler peer){ Proposal newLeaderProposal = new Proposal(); - class LearnerCnxAcceptor extends Thread{ + class LearnerCnxAcceptor extends ZooKeeperThread{ private volatile boolean stop = false; - + + public LearnerCnxAcceptor() { + super("LearnerCnxAcceptor-" + ss.getLocalSocketAddress()); + } + @Override public void run() { try { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index bf53c7a9f5a..6d0ee256bd5 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -39,6 +39,7 @@ import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; @@ -53,7 +54,7 @@ * learner. All communication with a learner is handled by this * class. */ -public class LearnerHandler extends Thread { +public class LearnerHandler extends ZooKeeperThread { private static final Logger LOG = LoggerFactory.getLogger(LearnerHandler.class); protected final Socket sock; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java index e94414ff4da..ccd19536e2a 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java @@ -26,13 +26,14 @@ import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooTrace; /** * This RequestProcessor forwards any requests that modify the state of the * system to the Leader. */ -public class ObserverRequestProcessor extends Thread implements +public class ObserverRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(ObserverRequestProcessor.class); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index dea70da4852..c7e3bbc16d7 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.server.ZooKeeperThread; /** * This class implements a connection manager for leader election using TCP. It @@ -492,10 +493,16 @@ public QuorumPeer getQuorumPeer() { /** * Thread to listen on some port */ - public class Listener extends Thread { + public class Listener extends ZooKeeperThread { volatile ServerSocket ss = null; + public Listener() { + // During startup of thread, thread name will be overridden to + // specific election address + super("ListenerThread"); + } + /** * Sleeps on accept(). */ @@ -569,7 +576,7 @@ void halt(){ * soon as there is one available. If connection breaks, then opens a new * one. */ - class SendWorker extends Thread { + class SendWorker extends ZooKeeperThread { Long sid; Socket sock; RecvWorker recvWorker; @@ -723,7 +730,7 @@ public void run() { * Thread to receive messages. Instance waits on a socket read. If the * channel breaks, then removes itself from the pool of receivers. */ - class RecvWorker extends Thread { + class RecvWorker extends ZooKeeperThread { Long sid; Socket sock; volatile boolean running = true; 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 df2a7413f31..095df59657a 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -41,6 +41,7 @@ import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; @@ -75,7 +76,7 @@ * * The request for the current leader will consist solely of an xid: int xid; */ -public class QuorumPeer extends Thread implements QuorumStats.Provider { +public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider { private static final Logger LOG = LoggerFactory.getLogger(QuorumPeer.class); QuorumBean jmxQuorumBean; @@ -293,7 +294,7 @@ synchronized void setBCVote(Vote v) { * */ @Deprecated - class ResponderThread extends Thread { + class ResponderThread extends ZooKeeperThread { ResponderThread() { super("ResponderThread"); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java index 14c09988fc5..33e5dd17120 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java @@ -26,6 +26,7 @@ import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; +import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.quorum.Leader.XidRolloverException; @@ -38,7 +39,7 @@ * OpCode.getData, OpCode.exists) through to the next processor, but drops * state-changing operations (e.g. OpCode.create, OpCode.setData). */ -public class ReadOnlyRequestProcessor extends Thread implements RequestProcessor { +public class ReadOnlyRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyRequestProcessor.class); diff --git a/src/java/test/config/findbugsExcludeFile.xml b/src/java/test/config/findbugsExcludeFile.xml index 13295c19a8a..7c6c5323e14 100644 --- a/src/java/test/config/findbugsExcludeFile.xml +++ b/src/java/test/config/findbugsExcludeFile.xml @@ -10,8 +10,8 @@ In particular we want to make sure we exit if this occurs Also notice logged as fatal error --> - - + + diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java new file mode 100644 index 00000000000..78f8b30acc9 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.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.server; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.Assert; + +import org.junit.Test; + +public class ZooKeeperThreadTest { + private CountDownLatch runningLatch = new CountDownLatch(1); + + public class MyThread extends ZooKeeperThread { + + public MyThread(String threadName) { + super(threadName); + } + + public void run() { + throw new Error(); + } + + @Override + protected void handleException(String thName, Throwable e) { + runningLatch.countDown(); + } + } + + public class MyCriticalThread extends ZooKeeperCriticalThread { + + public MyCriticalThread(String threadName) { + super(threadName); + } + + public void run() { + throw new Error(); + } + + @Override + protected void handleException(String thName, Throwable e) { + runningLatch.countDown(); + } + } + + /** + * Test verifies uncaught exception handling of ZooKeeperThread + */ + @Test(timeout = 30000) + public void testUncaughtException() throws Exception { + MyThread t1 = new MyThread("Test-Thread"); + t1.start(); + Assert.assertTrue("Uncaught exception is not properly handled.", + runningLatch.await(10000, TimeUnit.MILLISECONDS)); + + runningLatch = new CountDownLatch(1); + MyCriticalThread t2 = new MyCriticalThread("Test-Critical-Thread"); + t2.start(); + Assert.assertTrue("Uncaught exception is not properly handled.", + runningLatch.await(10000, TimeUnit.MILLISECONDS)); + } +} From d66048c73a550aae8d34ccbae6ec384f3e8f74a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Mon, 6 Jul 2015 15:48:24 +0000 Subject: [PATCH 290/444] ZOOKEEPER-2224: Four letter command hangs when network is slow (Arshad Mohammad via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1689431 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../zookeeper/client/FourLetterWordMain.java | 27 ++++++++++++++++++- .../zookeeper/test/FourLetterWordsTest.java | 18 +++++++++++-- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 63a80204ce2..ecf166691a7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -99,6 +99,9 @@ BUGFIXES: ZOOKEEPER-602: log all exceptions not caught by ZK threads (Rakesh R via rgs) + ZOOKEEPER-2224: Four letter command hangs when network is slow + (Arshad Mohammad via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java b/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java index e41465ab93a..a8ecb2201e1 100644 --- a/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java +++ b/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java @@ -24,9 +24,14 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketTimeoutException; public class FourLetterWordMain { + //in milliseconds, socket should connect/read within this period otherwise SocketTimeoutException + private static final int DEFAULT_SOCKET_TIMEOUT = 5000; protected static final Logger LOG = Logger.getLogger(FourLetterWordMain.class); /** @@ -39,11 +44,29 @@ public class FourLetterWordMain { */ public static String send4LetterWord(String host, int port, String cmd) throws IOException + { + return send4LetterWord(host, port, cmd, DEFAULT_SOCKET_TIMEOUT); + } + /** + * Send the 4letterword + * @param host the destination host + * @param port the destination port + * @param cmd the 4letterword + * @param timeout in milliseconds, maximum time to wait while connecting/reading data + * @return server response + * @throws java.io.IOException + */ + public static String send4LetterWord(String host, int port, String cmd, int timeout) + throws IOException { LOG.info("connecting to " + host + " " + port); - Socket sock = new Socket(host, port); + Socket sock = new Socket(); + InetSocketAddress hostaddress= host != null ? new InetSocketAddress(host, port) : + new InetSocketAddress(InetAddress.getByName(null), port); BufferedReader reader = null; try { + sock.setSoTimeout(timeout); + sock.connect(hostaddress, timeout); OutputStream outstream = sock.getOutputStream(); outstream.write(cmd.getBytes()); outstream.flush(); @@ -59,6 +82,8 @@ public static String send4LetterWord(String host, int port, String cmd) sb.append(line + "\n"); } return sb.toString(); + } catch (SocketTimeoutException e) { + throw new IOException("Exception while executing four letter word: " + cmd, e); } finally { sock.close(); if (reader != null) { diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java index 281b1786954..0f81b65f4e4 100644 --- a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java @@ -103,6 +103,10 @@ private String sendRequest(String cmd) throws IOException { HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); return send4LetterWord(hpobj.host, hpobj.port, cmd); } + private String sendRequest(String cmd, int timeout) throws IOException { + HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); + return send4LetterWord(hpobj.host, hpobj.port, cmd, timeout); + } private void verify(String cmd, String expected) throws IOException { String resp = sendRequest(cmd); @@ -111,7 +115,7 @@ private void verify(String cmd, String expected) throws IOException { } @Test - public void validateStatOutput() throws Exception { + public void testValidateStatOutput() throws Exception { ZooKeeper zk1 = createClient(); ZooKeeper zk2 = createClient(); @@ -154,7 +158,7 @@ public void validateStatOutput() throws Exception { } @Test - public void validateConsOutput() throws Exception { + public void testValidateConsOutput() throws Exception { ZooKeeper zk1 = createClient(); ZooKeeper zk2 = createClient(); @@ -173,4 +177,14 @@ public void validateConsOutput() throws Exception { zk1.close(); zk2.close(); } + + @Test(timeout=60000) + public void testValidateSocketTimeout() throws Exception { + /** + * testing positive scenario that even with timeout parameter the + * functionality works fine + */ + String resp = sendRequest("isro", 2000); + Assert.assertTrue(resp.contains("rw")); + } } From 0d254c8df43acae08c71edd299bda35b593d2f06 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Tue, 28 Jul 2015 04:39:20 +0000 Subject: [PATCH 291/444] ZOOKEEPER-2235: License update (fpj via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1692991 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + build.xml | 16 +- src/LICENSE.txt | 216 +++++++++++++++++++++++++++ src/NOTICE.txt | 6 + src/java/lib/slf4j-1.6.1.LICENSE.txt | 21 +++ 5 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 src/LICENSE.txt create mode 100644 src/NOTICE.txt create mode 100644 src/java/lib/slf4j-1.6.1.LICENSE.txt diff --git a/CHANGES.txt b/CHANGES.txt index ecf166691a7..81143aa1ccb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -102,6 +102,8 @@ BUGFIXES: ZOOKEEPER-2224: Four letter command hangs when network is slow (Arshad Mohammad via rgs) + ZOOKEEPER-2235: License update (fpj via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/build.xml b/build.xml index 760912e1515..be64cac7a85 100644 --- a/build.xml +++ b/build.xml @@ -749,12 +749,14 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - - - + + + + + @@ -864,12 +866,14 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - - - + + + + + diff --git a/src/LICENSE.txt b/src/LICENSE.txt new file mode 100644 index 00000000000..867ac6d84cf --- /dev/null +++ b/src/LICENSE.txt @@ -0,0 +1,216 @@ + + 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 [yyyy] [name of copyright owner] + + 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. + + +This distribution bundles jline 0.9.94, which is available under the +2-clause BSD License. For details, see a copy of the license in +lib/jline-0.9.94.LICENSE.txt + +This distribution bundles SLF4J 1.6.1, which is available under the MIT +License. For details, see a copy of the license in +lib/slf4j-1.6.1.LICENSE.txt + +This distribution bundles a modified version of 'JZLib' as part of +Netty-3.7.0, which is available under the 3-clause BSD licence. For +details, see a copy of the licence in META-INF/license/LICENSE-jzlib.txt +as part of the Netty jar in lib/netty-3.7.0.Final.jar. diff --git a/src/NOTICE.txt b/src/NOTICE.txt new file mode 100644 index 00000000000..d0446a6f7f2 --- /dev/null +++ b/src/NOTICE.txt @@ -0,0 +1,6 @@ +Apache ZooKeeper +Copyright 2009-2015 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + diff --git a/src/java/lib/slf4j-1.6.1.LICENSE.txt b/src/java/lib/slf4j-1.6.1.LICENSE.txt new file mode 100644 index 00000000000..f5ecafa0074 --- /dev/null +++ b/src/java/lib/slf4j-1.6.1.LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2004-2008 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. From b2703be61c7d6e31ec2b535f5f130b1703eefad9 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Sat, 1 Aug 2015 03:39:21 +0000 Subject: [PATCH 292/444] ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1693680 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 + .../org/apache/zookeeper/AsyncCallback.java | 35 ++ .../main/org/apache/zookeeper/ClientCnxn.java | 21 +- .../org/apache/zookeeper/Transaction.java | 5 + .../main/org/apache/zookeeper/ZooKeeper.java | 61 ++- .../zookeeper/test/MultiTransactionTest.java | 455 ++++++++++++------ 6 files changed, 432 insertions(+), 149 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 81143aa1ccb..4c4a6893b60 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -128,6 +128,10 @@ IMPROVEMENTS: ZOOKEEPER-2205: Log type of unexpected quorum packet in learner handler loop (Hitoshi Mitake via rgs) +NEW FEATURES: + + ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) + Release 3.4.6 - 2014-03-10 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/AsyncCallback.java b/src/java/main/org/apache/zookeeper/AsyncCallback.java index c9f76c1c532..4e8fbf4afbc 100644 --- a/src/java/main/org/apache/zookeeper/AsyncCallback.java +++ b/src/java/main/org/apache/zookeeper/AsyncCallback.java @@ -261,4 +261,39 @@ interface VoidCallback extends AsyncCallback { */ public void processResult(int rc, String path, Object ctx); } + + /** + * This callback is used to process the multiple results from + * a single multi call. + * See {@link org.apache.zookeeper.ZooKeeper#multi} for more information. + * @since 3.4.7 + */ + interface MultiCallback extends AsyncCallback { + /** + * Process the result of the asynchronous call. + *

            + * On success, rc is + * {@link org.apache.zookeeper.KeeperException.Code#OK}. + * All opResults are + * non-{@link org.apache.zookeeper.OpResult.ErrorResult}, + * + *

            + * On failure, rc is a failure code in + * {@link org.apache.zookeeper.KeeperException.Code}. + * All opResults are + * {@link org.apache.zookeeper.OpResult.ErrorResult}. + * All operations will be rollback-ed even if operations + * before the failing one were successful. + * + * @param rc The return code or the result of the call. + * @param path The path that we passed to asynchronous calls. + * @param ctx Whatever context object that we passed to + * asynchronous calls. + * @param opResults The list of results. + * One result for each operation, + * and the order matches that of input. + */ + public void processResult(int rc, String path, Object ctx, + List opResults); + } } diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 2435e321f0b..d5575485246 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -47,9 +47,11 @@ import org.apache.zookeeper.AsyncCallback.Children2Callback; import org.apache.zookeeper.AsyncCallback.ChildrenCallback; import org.apache.zookeeper.AsyncCallback.DataCallback; +import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; +import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.Watcher.Event; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; @@ -610,7 +612,24 @@ private void processEvent(Object event) { } else { cb.processResult(rc, clientPath, p.ctx, null); } - } else if (p.cb instanceof VoidCallback) { + } else if (p.response instanceof MultiResponse) { + MultiCallback cb = (MultiCallback) p.cb; + MultiResponse rsp = (MultiResponse) p.response; + if (rc == 0) { + List results = rsp.getResultList(); + int newRc = rc; + for (OpResult result : results) { + if (result instanceof ErrorResult + && KeeperException.Code.OK.intValue() + != (newRc = ((ErrorResult) result).getErr())) { + break; + } + } + cb.processResult(newRc, clientPath, p.ctx, results); + } else { + cb.processResult(rc, clientPath, p.ctx, null); + } + } else if (p.cb instanceof VoidCallback) { VoidCallback cb = (VoidCallback) p.cb; cb.processResult(rc, clientPath, p.ctx); } diff --git a/src/java/main/org/apache/zookeeper/Transaction.java b/src/java/main/org/apache/zookeeper/Transaction.java index 7e67b87a4c1..d8f0a76fdbc 100644 --- a/src/java/main/org/apache/zookeeper/Transaction.java +++ b/src/java/main/org/apache/zookeeper/Transaction.java @@ -17,6 +17,7 @@ package org.apache.zookeeper; +import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.data.ACL; import java.util.ArrayList; import java.util.List; @@ -60,4 +61,8 @@ public Transaction setData(final String path, byte data[], int version) { public List commit() throws InterruptedException, KeeperException { return zk.multi(ops); } + + public void commit(MultiCallback cb, Object ctx) { + zk.multi(ops, cb, ctx); + } } diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index 139ce663992..cabc4d53235 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -907,12 +907,62 @@ public List multi(Iterable ops) throws InterruptedException, Keepe for (Op op : ops) { op.validate(); } - // reconstructing transaction with the chroot prefix + return multiInternal(generateMultiTransaction(ops)); + } + + /** + * The asynchronous version of multi. + * + * @see #multi(Iterable) + * @since 3.4.7 + */ + public void multi(Iterable ops, MultiCallback cb, Object ctx) { + List results = validatePath(ops); + if (results.size() > 0) { + cb.processResult(KeeperException.Code.BADARGUMENTS.intValue(), + null, ctx, results); + return; + } + multiInternal(generateMultiTransaction(ops), cb, ctx); + } + + private List validatePath(Iterable ops) { + List results = new ArrayList(); + boolean error = false; + for (Op op : ops) { + try { + op.validate(); + } catch (IllegalArgumentException iae) { + LOG.error("IllegalArgumentException: " + iae.getMessage()); + ErrorResult err = new ErrorResult( + KeeperException.Code.BADARGUMENTS.intValue()); + results.add(err); + error = true; + continue; + } catch (KeeperException ke) { + LOG.error("KeeperException: " + ke.getMessage()); + ErrorResult err = new ErrorResult(ke.code().intValue()); + results.add(err); + error = true; + continue; + } + ErrorResult err = new ErrorResult( + KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); + results.add(err); + } + if (false == error) { + results.clear(); + } + return results; + } + + private MultiTransactionRecord generateMultiTransaction(Iterable ops) { List transaction = new ArrayList(); + for (Op op : ops) { transaction.add(withRootPrefix(op)); } - return multiInternal(new MultiTransactionRecord(transaction)); + return new MultiTransactionRecord(transaction); } private Op withRootPrefix(Op op) { @@ -925,6 +975,13 @@ private Op withRootPrefix(Op op) { return op; } + protected void multiInternal(MultiTransactionRecord request, MultiCallback cb, Object ctx) { + RequestHeader h = new RequestHeader(); + h.setType(ZooDefs.OpCode.multi); + MultiResponse response = new MultiResponse(); + cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, null, null, ctx, null); + } + protected List multiInternal(MultiTransactionRecord request) throws InterruptedException, KeeperException { RequestHeader h = new RequestHeader(); diff --git a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java index 08d92b89faf..0277c230653 100644 --- a/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java +++ b/src/java/test/org/apache/zookeeper/test/MultiTransactionTest.java @@ -21,12 +21,14 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.apache.zookeeper.AsyncCallback; +import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; @@ -46,13 +48,30 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class MultiTransactionTest extends ClientBase { private static final Logger LOG = Logger.getLogger(MultiTransactionTest.class); private ZooKeeper zk; private ZooKeeper zk_chroot; + private final boolean useAsync; + + public MultiTransactionTest(boolean useAsync) { + this.useAsync = useAsync; + } + + @Parameters + public static Collection configs() { + return Arrays.asList(new Object[][] { + { false }, { true }, + }); + } + @Before public void setUp() throws Exception { SyncRequestProcessor.setSnapCount(150); @@ -60,68 +79,226 @@ public void setUp() throws Exception { zk = createClient(); } - /** - * Test verifies the multi calls with invalid znode path - */ - @Test(timeout = 90000) - public void testInvalidPath() throws Exception { + static class MultiResult { + int rc; + List results; + boolean finished = false; + } + + private List multi(ZooKeeper zk, Iterable ops) + throws KeeperException, InterruptedException { + if (useAsync) { + final MultiResult res = new MultiResult(); + zk.multi(ops, new MultiCallback() { + @Override + public void processResult(int rc, String path, Object ctx, + List opResults) { + synchronized (res) { + res.rc = rc; + res.results = opResults; + res.finished = true; + res.notifyAll(); + } + } + }, null); + synchronized (res) { + while (!res.finished) { + res.wait(); + } + } + if (KeeperException.Code.OK.intValue() != res.rc) { + KeeperException ke = KeeperException.create(KeeperException.Code.get(res.rc)); + throw ke; + } + return res.results; + } else { + return zk.multi(ops); + } + } + + private void multiHavingErrors(ZooKeeper zk, Iterable ops, + List expectedResultCodes, String expectedErr) + throws KeeperException, InterruptedException { + if (useAsync) { + final MultiResult res = new MultiResult(); + zk.multi(ops, new MultiCallback() { + @Override + public void processResult(int rc, String path, Object ctx, + List opResults) { + synchronized (res) { + res.rc = rc; + res.results = opResults; + res.finished = true; + res.notifyAll(); + } + } + }, null); + synchronized (res) { + while (!res.finished) { + res.wait(); + } + } + for (int i = 0; i < res.results.size(); i++) { + OpResult opResult = res.results.get(i); + Assert.assertTrue("Did't recieve proper error response", + opResult instanceof ErrorResult); + ErrorResult errRes = (ErrorResult) opResult; + Assert.assertEquals("Did't recieve proper error code", + expectedResultCodes.get(i).intValue(), errRes.getErr()); + } + } else { + try { + zk.multi(ops); + Assert.fail("Shouldn't have validated in ZooKeeper client!"); + } catch (KeeperException e) { + Assert.assertEquals("Wrong exception", expectedErr, e.code() + .name()); + } catch (IllegalArgumentException e) { + Assert.assertEquals("Wrong exception", expectedErr, + e.getMessage()); + } + } + } + + private List commit(Transaction txn) + throws KeeperException, InterruptedException { + if (useAsync) { + final MultiResult res = new MultiResult(); + txn.commit(new MultiCallback() { + @Override + public void processResult(int rc, String path, Object ctx, + List opResults) { + synchronized (res) { + res.rc = rc; + res.results = opResults; + res.finished = true; + res.notifyAll(); + } + } + }, null); + synchronized (res) { + while (!res.finished) { + res.wait(); + } + } + if (KeeperException.Code.OK.intValue() != res.rc) { + KeeperException ke = KeeperException.create(KeeperException.Code.get(res.rc)); + throw ke; + } + return res.results; + } else { + return txn.commit(); + } + } + + /** + * Test verifies the multi calls with invalid znode path + */ + @Test(timeout = 90000) + public void testInvalidPath() throws Exception { + List expectedResultCodes = new ArrayList(); + expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY + .intValue()); + expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); + expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY + .intValue()); // create with CreateMode List opList = Arrays.asList(Op.create("/multi0", new byte[0], - Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create( - "/multi1/", new byte[0], Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], - Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (IllegalArgumentException e) { - // expected - } + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT), + Op.create( + "/multi1/", new byte[0], + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT), + Op.create("/multi2", new byte[0], + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT)); + String expectedErr = "Path must not end with / character"; + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); // create with valid sequential flag opList = Arrays.asList(Op.create("/multi0", new byte[0], - Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create( - "multi1/", new byte[0], Ids.OPEN_ACL_UNSAFE, - CreateMode.EPHEMERAL_SEQUENTIAL.toFlag()), Op.create("/multi2", - new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (IllegalArgumentException e) { - // expected - } + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT), + Op.create("multi1/", new byte[0], + Ids.OPEN_ACL_UNSAFE, + CreateMode.EPHEMERAL_SEQUENTIAL.toFlag()), + Op.create("/multi2", + new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT)); + expectedErr = "Path must start with / character"; + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); // check opList = Arrays.asList(Op.check("/multi0", -1), - Op.check("/multi1/", 100), Op.check("/multi2", 5)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (IllegalArgumentException e) { - // expected - } + Op.check("/multi1/", 100), + Op.check("/multi2", 5)); + expectedErr = "Path must not end with / character"; + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); // delete opList = Arrays.asList(Op.delete("/multi0", -1), - Op.delete("/multi1/", 100), Op.delete("/multi2", 5)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (IllegalArgumentException e) { - // expected - } + Op.delete("/multi1/", 100), + Op.delete("/multi2", 5)); + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); + + // Multiple bad arguments + expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); // setdata opList = Arrays.asList(Op.setData("/multi0", new byte[0], -1), - Op.setData("/multi1/", new byte[0], -1), - Op.setData("/multi2", new byte[0], -1), - Op.setData("multi3", new byte[0], -1)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (IllegalArgumentException e) { - // expected - } + Op.setData("/multi1/", new byte[0], -1), + Op.setData("/multi2", new byte[0], -1), + Op.setData("multi3", new byte[0], -1)); + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); + } + + /** + * Test verifies the multi calls with blank znode path + */ + @Test(timeout = 90000) + public void testBlankPath() throws Exception { + List expectedResultCodes = new ArrayList(); + expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY + .intValue()); + expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); + expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY + .intValue()); + expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); + + // delete + String expectedErr = "Path cannot be null"; + List opList = Arrays.asList(Op.delete("/multi0", -1), + Op.delete(null, 100), + Op.delete("/multi2", 5), + Op.delete("", -1)); + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); + } + + /** + * Test verifies the multi.create with invalid createModeFlag + */ + @Test(timeout = 90000) + public void testInvalidCreateModeFlag() throws Exception { + List expectedResultCodes = new ArrayList(); + expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY + .intValue()); + expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); + expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY + .intValue()); + + int createModeFlag = 6789; + List opList = Arrays.asList(Op.create("/multi0", new byte[0], + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT), + Op.create("/multi1", new byte[0], + Ids.OPEN_ACL_UNSAFE, + createModeFlag), + Op.create("/multi2", new byte[0], + Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT)); + String expectedErr = KeeperException.Code.BADARGUMENTS.name(); + multiHavingErrors(zk, opList, expectedResultCodes, expectedErr); } /** @@ -141,7 +318,7 @@ public void testMultiRollback() throws Exception { List opList = Arrays.asList(Op.delete("/foo", -1)); try { - zk.multi(opList); + multi(zk, opList); Assert.fail("multi delete should failed for not empty directory"); } catch (KeeperException.NotEmptyException e) { } @@ -149,13 +326,13 @@ public void testMultiRollback() throws Exception { final CountDownLatch latch = new CountDownLatch(1); zk.exists("/foo/bar", new Watcher() { - @Override - public void process(WatchedEvent event) { - if (event.getType() == Event.EventType.NodeDeleted){ - latch.countDown(); + @Override + public void process(WatchedEvent event) { + if (event.getType() == Event.EventType.NodeDeleted){ + latch.countDown(); + } } - } - }); + }); epheZk.close(); @@ -167,7 +344,7 @@ public void process(WatchedEvent event) { } catch (KeeperException.NoNodeException e) { } - zk.multi(opList); + multi(zk, opList); try { zk.getData("/foo", false, null); @@ -176,42 +353,6 @@ public void process(WatchedEvent event) { } } - /** - * Test verifies the multi calls with blank znode path - */ - @Test(timeout = 90000) - public void testBlankPath() throws Exception { - // delete - List opList = Arrays.asList(Op.delete("/multi0", -1), - Op.delete(null, 100), Op.delete("/multi2", 5), - Op.delete("", -1)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (IllegalArgumentException e) { - // expected - } - } - - /** - * Test verifies the multi.create with invalid createModeFlag - */ - @Test(timeout = 90000) - public void testInvalidCreateModeFlag() throws Exception { - int createModeFlag = 6789; - List opList = Arrays.asList(Op.create("/multi0", new byte[0], - Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create( - "/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, createModeFlag), - Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT)); - try { - zk.multi(opList); - Assert.fail("Shouldn't have validated in ZooKeeper client!"); - } catch (KeeperException.BadArgumentsException e) { - // expected - } - } - @Test public void testChRootCreateDelete() throws Exception { // creating the subtree for chRoot clients. @@ -220,7 +361,7 @@ public void testChRootCreateDelete() throws Exception { zk_chroot = createClient(this.hostPort + chRoot); Op createChild = Op.create("/myid", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - zk_chroot.multi(Arrays.asList(createChild)); + multi(zk_chroot, Arrays.asList(createChild)); Assert.assertNotNull("zNode is not created under chroot:" + chRoot, zk .exists(chRoot + "/myid", false)); @@ -231,7 +372,7 @@ public void testChRootCreateDelete() throws Exception { // Deleting child using chRoot client. Op deleteChild = Op.delete("/myid", 0); - zk_chroot.multi(Arrays.asList(deleteChild)); + multi(zk_chroot, Arrays.asList(deleteChild)); Assert.assertNull("zNode exists under chroot:" + chRoot, zk.exists( chRoot + "/myid", false)); Assert.assertNull("zNode exists under chroot:" + chRoot, zk_chroot @@ -253,7 +394,7 @@ public void testChRootSetData() throws Exception { ops.add(Op.setData(names[i], names[i].getBytes(), 0)); } - zk_chroot.multi(ops) ; + multi(zk_chroot, ops) ; for (int i = 0; i < names.length; i++) { Assert.assertArrayEquals("zNode data not matching", names[i] @@ -276,7 +417,7 @@ public void testChRootCheck() throws Exception { for (int i = 0; i < names.length; i++) { ops.add(Op.check(names[i], 0)); } - zk_chroot.multi(ops) ; + multi(zk_chroot, ops) ; } @Test @@ -291,7 +432,7 @@ public void testChRootTransaction() throws Exception { CreateMode.PERSISTENT); transaction.check(childPath, 0); transaction.setData(childPath, childPath.getBytes(), 0); - transaction.commit(); + commit(transaction); Assert.assertNotNull("zNode is not created under chroot:" + chRoot, zk .exists(chRoot + childPath, false)); @@ -305,7 +446,7 @@ public void testChRootTransaction() throws Exception { transaction = zk_chroot.transaction(); // Deleting child using chRoot client. transaction.delete(childPath, 1); - transaction.commit(); + commit(transaction); Assert.assertNull("chroot:" + chRoot + " exists after delete", zk .exists(chRoot + "/myid", false)); @@ -319,13 +460,13 @@ private String createNameSpace() throws InterruptedException, String chRoot = "/appsX"; Op createChRoot = Op.create(chRoot, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - zk.multi(Arrays.asList(createChRoot)); + multi(zk, Arrays.asList(createChRoot)); return chRoot; } @Test public void testCreate() throws Exception { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) @@ -338,7 +479,7 @@ public void testCreate() throws Exception { @Test public void testCreateDelete() throws Exception { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 0) )); @@ -351,7 +492,7 @@ public void testCreateDelete() throws Exception { public void testInvalidVersion() throws Exception { try { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 1) )); @@ -364,7 +505,7 @@ public void testInvalidVersion() throws Exception { @Test public void testNestedCreate() throws Exception { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( /* Create */ Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi/a", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), @@ -393,7 +534,7 @@ public void testSetData() throws Exception { ops.add(Op.setData(names[i], names[i].getBytes(), 0)); } - zk.multi(ops) ; + multi(zk, ops) ; for (int i = 0; i < names.length; i++) { Assert.assertArrayEquals(names[i].getBytes(), zk.getData(names[i], false, null)); @@ -406,7 +547,7 @@ public void testUpdateConflict() throws Exception { Assert.assertNull(zk.exists("/multi", null)); try { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData("/multi", "X".getBytes(), 0), Op.setData("/multi", "Y".getBytes(), 0) @@ -420,7 +561,7 @@ public void testUpdateConflict() throws Exception { Assert.assertNull(zk.exists("/multi", null)); //Updating version solves conflict -- order matters - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData("/multi", "X".getBytes(), 0), Op.setData("/multi", "Y".getBytes(), 1) @@ -434,7 +575,7 @@ public void TestDeleteUpdateConflict() throws Exception { /* Delete of a node folowed by an update of the (now) deleted node */ try { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 0), Op.setData("/multi", "Y".getBytes(), 0) @@ -451,24 +592,53 @@ public void TestDeleteUpdateConflict() throws Exception { @Test public void TestGetResults() throws Exception { /* Delete of a node folowed by an update of the (now) deleted node */ - try { - zk.multi(Arrays.asList( - Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), - Op.delete("/multi", 0), - Op.setData("/multi", "Y".getBytes(), 0), - Op.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) - )); - Assert.fail("/multi should have been deleted so setData should have failed"); - } catch (KeeperException e) { - // '/multi' should never have been created as entire op should fail + Iterable ops = Arrays.asList( + Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), + Op.delete("/multi", 0), + Op.setData("/multi", "Y".getBytes(), 0), + Op.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) + ); + List results = null; + if (useAsync) { + final MultiResult res = new MultiResult(); + zk.multi(ops, new MultiCallback() { + @Override + public void processResult(int rc, String path, Object ctx, + List opResults) { + synchronized (res) { + res.rc = rc; + res.results = opResults; + res.finished = true; + res.notifyAll(); + } + } + }, null); + synchronized (res) { + while (!res.finished) { + res.wait(); + } + } + Assert.assertFalse("/multi should have been deleted so setData should have failed", + KeeperException.Code.OK.intValue() == res.rc); Assert.assertNull(zk.exists("/multi", null)); + results = res.results; + } else { + try { + zk.multi(ops); + Assert.fail("/multi should have been deleted so setData should have failed"); + } catch (KeeperException e) { + // '/multi' should never have been created as entire op should fail + Assert.assertNull(zk.exists("/multi", null)); + results = e.getResults(); + } + } - for (OpResult r : e.getResults()) { - LOG.info("RESULT==> " + r); - if (r instanceof ErrorResult) { - ErrorResult er = (ErrorResult) r; - LOG.info("ERROR RESULT: " + er + " ERR=>" + KeeperException.Code.get(er.getErr())); - } + Assert.assertNotNull(results); + for (OpResult r : results) { + LOG.info("RESULT==> " + r); + if (r instanceof ErrorResult) { + ErrorResult er = (ErrorResult) r; + LOG.info("ERROR RESULT: " + er + " ERR=>" + KeeperException.Code.get(er.getErr())); } } } @@ -516,7 +686,7 @@ private void opEquals(OpResult expected, OpResult value, OpResult near) { public void testWatchesTriggered() throws KeeperException, InterruptedException { HasTriggeredWatcher watcher = new HasTriggeredWatcher(); zk.getChildren("/", watcher); - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/t", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/t", -1) )); @@ -528,7 +698,7 @@ public void testNoWatchesTriggeredForFailedMultiRequest() throws InterruptedExce HasTriggeredWatcher watcher = new HasTriggeredWatcher(); zk.getChildren("/", watcher); try { - zk.multi(Arrays.asList( + multi(zk, Arrays.asList( Op.create("/t", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/nonexisting", -1) )); @@ -546,11 +716,10 @@ public void testNoWatchesTriggeredForFailedMultiRequest() throws InterruptedExce @Test public void testTransactionBuilder() throws Exception { - List results = zk.transaction() + List results = commit(zk.transaction() .create("/t1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) .create("/t1/child", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) - .create("/t2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL) - .commit(); + .create("/t2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL)); assertEquals(3, results.size()); for (OpResult r : results) { CreateResult c = (CreateResult)r; @@ -561,11 +730,10 @@ public void testTransactionBuilder() throws Exception { assertNotNull(zk.exists("/t1/child", false)); assertNotNull(zk.exists("/t2", false)); - results = zk.transaction() + results = commit(zk.transaction() .check("/t1", 0) .check("/t1/child", 0) - .check("/t2", 0) - .commit(); + .check("/t2", 0)); assertEquals(3, results.size()); for (OpResult r : results) { CheckResult c = (CheckResult)r; @@ -573,46 +741,41 @@ public void testTransactionBuilder() throws Exception { } try { - results = zk.transaction() + results = commit(zk.transaction() .check("/t1", 0) .check("/t1/child", 0) - .check("/t2", 1) - .commit(); + .check("/t2", 1)); fail(); } catch (KeeperException.BadVersionException e) { // expected } - results = zk.transaction() + results = commit(zk.transaction() .check("/t1", 0) - .setData("/t1", new byte[0], 0) - .commit(); + .setData("/t1", new byte[0], 0)); assertEquals(2, results.size()); for (OpResult r : results) { assertNotNull(r.toString()); } try { - results = zk.transaction() + results = commit(zk.transaction() .check("/t1", 1) - .setData("/t1", new byte[0], 2) - .commit(); + .setData("/t1", new byte[0], 2)); fail(); } catch (KeeperException.BadVersionException e) { // expected } - results = zk.transaction() + results = commit(zk.transaction() .check("/t1", 1) .check("/t1/child", 0) - .check("/t2", 0) - .commit(); + .check("/t2", 0)); assertEquals(3, results.size()); - results = zk.transaction() + results = commit(zk.transaction() .delete("/t2", -1) - .delete("/t1/child", -1) - .commit(); + .delete("/t1/child", -1)); assertEquals(2, results.size()); for (OpResult r : results) { DeleteResult d = (DeleteResult)r; From 8de5ad8d99a0f974a4393cc4eda546c66643dd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Wed, 5 Aug 2015 20:17:18 +0000 Subject: [PATCH 293/444] ZOOKEEPER-2239: JMX State from LocalPeerBean incorrect (Kevin Lee via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1694318 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/server/quorum/LocalPeerBean.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4c4a6893b60..747b6cb7cf2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -104,6 +104,9 @@ BUGFIXES: ZOOKEEPER-2235: License update (fpj via michim) + ZOOKEEPER-2239: JMX State from LocalPeerBean incorrect + (Kevin Lee via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LocalPeerBean.java b/src/java/main/org/apache/zookeeper/server/quorum/LocalPeerBean.java index 03f3f06bf74..0d9fa70be12 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LocalPeerBean.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LocalPeerBean.java @@ -66,7 +66,7 @@ public int getTick() { } public String getState() { - return peer.getState().toString(); + return peer.getServerState(); } public String getQuorumAddress() { From cfff3f06ae5c208ab566ab4fd941df835b880e53 Mon Sep 17 00:00:00 2001 From: Hongchao Deng Date: Mon, 17 Aug 2015 20:52:07 +0000 Subject: [PATCH 294/444] ZOOKEEPER-1907 Improve Thread handling (Rakesh R via hdeng) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1696337 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../org/apache/zookeeper/server/ExitCode.java | 27 ++++++++++ .../server/PrepRequestProcessor.java | 10 ++-- .../zookeeper/server/SessionTrackerImpl.java | 6 +-- .../server/SyncRequestProcessor.java | 8 +-- .../server/ZooKeeperCriticalThread.java | 14 ++--- .../zookeeper/server/ZooKeeperServer.java | 54 ++++++++++++++----- .../server/ZooKeeperServerListener.java | 35 ++++++++++++ .../server/quorum/CommitProcessor.java | 6 ++- .../quorum/FollowerRequestProcessor.java | 5 +- .../quorum/FollowerZooKeeperServer.java | 3 +- .../server/quorum/LeaderZooKeeperServer.java | 8 +-- .../server/quorum/LearnerSessionTracker.java | 4 +- .../server/quorum/LearnerZooKeeperServer.java | 5 +- .../quorum/ObserverRequestProcessor.java | 5 +- .../quorum/ObserverZooKeeperServer.java | 9 +++- .../quorum/ReadOnlyRequestProcessor.java | 12 ++--- .../quorum/ReadOnlyZooKeeperServer.java | 4 ++ .../zookeeper/server/ZooKeeperThreadTest.java | 8 ++- 19 files changed, 173 insertions(+), 53 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/server/ExitCode.java create mode 100644 src/java/main/org/apache/zookeeper/server/ZooKeeperServerListener.java diff --git a/CHANGES.txt b/CHANGES.txt index 747b6cb7cf2..e1c9c09fccb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -131,6 +131,9 @@ IMPROVEMENTS: ZOOKEEPER-2205: Log type of unexpected quorum packet in learner handler loop (Hitoshi Mitake via rgs) + ZOOKEEPER-1907 Improve Thread handling + (Rakesh R via hdeng) + NEW FEATURES: ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) diff --git a/src/java/main/org/apache/zookeeper/server/ExitCode.java b/src/java/main/org/apache/zookeeper/server/ExitCode.java new file mode 100644 index 00000000000..02d96cb51d4 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/ExitCode.java @@ -0,0 +1,27 @@ +/** + * 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.server; + +/** + * Exit code used to exit server + */ +public class ExitCode { + + /* Represents unexpected error */ + public final static int UNEXPECTED_ERROR = 1; +} diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 34392d37131..0b95e9d7f5c 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -102,8 +102,8 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements public PrepRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { - super("ProcessThread(sid:" + zks.getServerId() - + " cport:" + zks.getClientPort() + "):"); + super("ProcessThread(sid:" + zks.getServerId() + " cport:" + + zks.getClientPort() + "):", zks.getZooKeeperServerListener()); this.nextProcessor = nextProcessor; this.zks = zks; } @@ -132,15 +132,13 @@ public void run() { } pRequest(request); } - } catch (InterruptedException e) { - LOG.error("Unexpected interruption", e); } catch (RequestProcessorException e) { if (e.getCause() instanceof XidRolloverException) { LOG.info(e.getCause().getMessage()); } - LOG.error("Unexpected exception", e); + handleException(this.getName(), e); } catch (Exception e) { - LOG.error("Unexpected exception", e); + handleException(this.getName(), e); } LOG.info("PrepRequestProcessor exited loop!"); } diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java index b5edd661b2a..938e9dac289 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java @@ -92,9 +92,9 @@ private long roundToInterval(long time) { public SessionTrackerImpl(SessionExpirer expirer, ConcurrentHashMap sessionsWithTimeout, int tickTime, - long sid) + long sid, ZooKeeperServerListener listener) { - super("SessionTracker"); + super("SessionTracker", listener); this.expirer = expirer; this.expirationInterval = tickTime; this.sessionsWithTimeout = sessionsWithTimeout; @@ -157,7 +157,7 @@ synchronized public void run() { nextExpirationTime += expirationInterval; } } catch (InterruptedException e) { - LOG.error("Unexpected interruption", e); + handleException(this.getName(), e); } LOG.info("SessionTrackerImpl exited loop!"); } diff --git a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java index a37aa42974d..41aa3905b03 100644 --- a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java @@ -75,9 +75,9 @@ public class SyncRequestProcessor extends ZooKeeperCriticalThread implements Req private final Request requestOfDeath = Request.requestOfDeath; public SyncRequestProcessor(ZooKeeperServer zks, - RequestProcessor nextProcessor) - { - super("SyncThread:" + zks.getServerId()); + RequestProcessor nextProcessor) { + super("SyncThread:" + zks.getServerId(), zks + .getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; running = true; @@ -180,7 +180,7 @@ public void run() { } } } catch (Throwable t) { - super.handleException(this.getName(), t); + handleException(this.getName(), t); running = false; System.exit(11); } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java index 138d22ea8e1..c8de809d8c9 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperCriticalThread.java @@ -27,24 +27,26 @@ public class ZooKeeperCriticalThread extends ZooKeeperThread { private static final Logger LOG = LoggerFactory .getLogger(ZooKeeperCriticalThread.class); - private static final int DEFAULT_EXIT_CODE = 1; + private final ZooKeeperServerListener listener; - public ZooKeeperCriticalThread(String threadName) { + public ZooKeeperCriticalThread(String threadName, + ZooKeeperServerListener listener) { super(threadName); + this.listener = listener; } /** * This will be used by the uncaught exception handler and make the system * exit. * - * @param thName + * @param threadName * - thread name * @param e * - exception object */ @Override - protected void handleException(String thName, Throwable e) { - LOG.error("Severe unrecoverable error, from thread : {}", thName, e); - System.exit(DEFAULT_EXIT_CODE); + protected void handleException(String threadName, Throwable e) { + LOG.error("Severe unrecoverable error, from thread : {}", threadName, e); + listener.notifyStopping(threadName, ExitCode.UNEXPECTED_ERROR); } } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index c1812c453fe..435198bc104 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -110,7 +110,11 @@ public DataTree build() { protected long hzxid = 0; public final static Exception ok = new Exception("No prob"); protected RequestProcessor firstProcessor; - protected volatile boolean running; + protected volatile State state = State.INITIAL; + + enum State { + INITIAL, RUNNING, SHUTDOWN; + } /** * This is the secret that we use to generate passwords, for the moment it @@ -128,6 +132,8 @@ public DataTree build() { private final ServerStats serverStats; + private final ZooKeeperServerListener listener = new ZooKeeperServerListenerImpl(); + void removeCnxn(ServerCnxn cnxn) { zkDb.removeCnxn(cnxn); } @@ -400,7 +406,7 @@ public void startdata() } } - public void startup() { + public synchronized void startup() { if (sessionTracker == null) { createSessionTracker(); } @@ -409,10 +415,8 @@ public void startup() { registerJMX(); - synchronized (this) { - running = true; - notifyAll(); - } + state = State.RUNNING; + notifyAll(); } protected void setupRequestProcessors() { @@ -424,9 +428,27 @@ protected void setupRequestProcessors() { ((PrepRequestProcessor)firstProcessor).start(); } + public ZooKeeperServerListener getZooKeeperServerListener() { + return listener; + } + + /** + * Default listener implementation, which will do a graceful shutdown on + * notification + */ + private class ZooKeeperServerListenerImpl implements + ZooKeeperServerListener { + + @Override + public void notifyStopping(String threadName, int exitCode) { + LOG.info("Thread {} exits, error code {}", threadName, exitCode); + shutdown(); + } + } + protected void createSessionTracker() { sessionTracker = new SessionTrackerImpl(this, zkDb.getSessionWithTimeOuts(), - tickTime, 1); + tickTime, 1, getZooKeeperServerListener()); } protected void startSessionTracker() { @@ -434,14 +456,18 @@ protected void startSessionTracker() { } public boolean isRunning() { - return running; + return state == State.RUNNING; } - public void shutdown() { + public synchronized void shutdown() { + if (!isRunning()) { + LOG.debug("ZooKeeper server is not running, so not proceeding to shutdown!"); + return; + } LOG.info("shutting down"); // new RuntimeException("Calling shutdown").printStackTrace(); - this.running = false; + state = State.SHUTDOWN; // Since sessionTracker and syncThreads poll we just have to // set running to false and they will detect it during the poll // interval. @@ -652,13 +678,17 @@ public void submitRequest(Request si) { if (firstProcessor == null) { synchronized (this) { try { - while (!running) { + // Since all requests are passed to the request + // processor it should wait for setting up the request + // processor chain. The state will be updated to RUNNING + // after the setup. + while (state == State.INITIAL) { wait(1000); } } catch (InterruptedException e) { LOG.warn("Unexpected interruption", e); } - if (firstProcessor == null) { + if (firstProcessor == null || state != State.RUNNING) { throw new RuntimeException("Not started"); } } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerListener.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerListener.java new file mode 100644 index 00000000000..b1c4c8c373e --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerListener.java @@ -0,0 +1,35 @@ +/** + * 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.server; + +/** + * Listener for the critical resource events. + */ +public interface ZooKeeperServerListener { + + /** + * This will notify the server that some critical thread has stopped. It + * usually takes place when fatal error occurred. + * + * @param threadName + * - name of the thread + * @param errorCode + * - error code + */ + void notifyStopping(String threadName, int errorCode); +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java index 36f5390cd8f..220d6588cd1 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/CommitProcessor.java @@ -27,6 +27,7 @@ import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ZooKeeperCriticalThread; +import org.apache.zookeeper.server.ZooKeeperServerListener; /** * This RequestProcessor matches the incoming committed requests with the @@ -57,8 +58,9 @@ public class CommitProcessor extends ZooKeeperCriticalThread implements RequestP */ boolean matchSyncs; - public CommitProcessor(RequestProcessor nextProcessor, String id, boolean matchSyncs) { - super("CommitProcessor:" + id); + public CommitProcessor(RequestProcessor nextProcessor, String id, + boolean matchSyncs, ZooKeeperServerListener listener) { + super("CommitProcessor:" + id, listener); this.nextProcessor = nextProcessor; this.matchSyncs = matchSyncs; } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java index 6388589fb48..8013b656fc2 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java @@ -47,7 +47,8 @@ public class FollowerRequestProcessor extends ZooKeeperCriticalThread implements public FollowerRequestProcessor(FollowerZooKeeperServer zks, RequestProcessor nextProcessor) { - super("FollowerRequestProcessor:" + zks.getServerId()); + super("FollowerRequestProcessor:" + zks.getServerId(), zks + .getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; } @@ -91,7 +92,7 @@ public void run() { } } } catch (Exception e) { - LOG.error("Unexpected exception causing exit", e); + handleException(this.getName(), e); } LOG.info("FollowerRequestProcessor exited loop!"); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java index 95c77b5c8e8..1fe687bb745 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java @@ -73,7 +73,8 @@ public Follower getFollower(){ protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); commitProcessor = new CommitProcessor(finalProcessor, - Long.toString(getServerId()), true); + Long.toString(getServerId()), true, + getZooKeeperServerListener()); commitProcessor.start(); firstProcessor = new FollowerRequestProcessor(this, commitProcessor); ((FollowerRequestProcessor) firstProcessor).start(); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java index 575f73d41a1..e10cc49ee97 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java @@ -62,7 +62,8 @@ protected void setupRequestProcessors() { RequestProcessor toBeAppliedProcessor = new Leader.ToBeAppliedRequestProcessor( finalProcessor, getLeader().toBeApplied); commitProcessor = new CommitProcessor(toBeAppliedProcessor, - Long.toString(getServerId()), false); + Long.toString(getServerId()), false, + getZooKeeperServerListener()); commitProcessor.start(); ProposalRequestProcessor proposalProcessor = new ProposalRequestProcessor(this, commitProcessor); @@ -78,8 +79,9 @@ public int getGlobalOutstandingLimit() { @Override public void createSessionTracker() { - sessionTracker = new SessionTrackerImpl(this, getZKDatabase().getSessionWithTimeOuts(), - tickTime, self.getId()); + sessionTracker = new SessionTrackerImpl(this, getZKDatabase() + .getSessionWithTimeOuts(), tickTime, self.getId(), + getZooKeeperServerListener()); } @Override diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java index 318241970a5..72f007eb758 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java @@ -24,6 +24,7 @@ import org.apache.zookeeper.server.SessionTracker; import org.apache.zookeeper.server.SessionTrackerImpl; +import org.apache.zookeeper.server.ZooKeeperServerListener; /** * This is really just a shell of a SessionTracker that tracks session activity @@ -39,7 +40,8 @@ public class LearnerSessionTracker implements SessionTracker { private ConcurrentHashMap sessionsWithTimeouts; public LearnerSessionTracker(SessionExpirer expirer, - ConcurrentHashMap sessionsWithTimeouts, long id) { + ConcurrentHashMap sessionsWithTimeouts, long id, + ZooKeeperServerListener listener) { this.expirer = expirer; this.sessionsWithTimeouts = sessionsWithTimeouts; this.serverId = id; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java index 5dd13320212..a1744ea6021 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java @@ -71,8 +71,9 @@ public long getServerId() { @Override public void createSessionTracker() { - sessionTracker = new LearnerSessionTracker(this, getZKDatabase().getSessionWithTimeOuts(), - self.getId()); + sessionTracker = new LearnerSessionTracker(this, getZKDatabase() + .getSessionWithTimeOuts(), self.getId(), + getZooKeeperServerListener()); } @Override diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java index ccd19536e2a..c9c537088f7 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java @@ -55,7 +55,8 @@ public class ObserverRequestProcessor extends ZooKeeperCriticalThread implements */ public ObserverRequestProcessor(ObserverZooKeeperServer zks, RequestProcessor nextProcessor) { - super("ObserverRequestProcessor:" + zks.getServerId()); + super("ObserverRequestProcessor:" + zks.getServerId(), zks + .getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; } @@ -99,7 +100,7 @@ public void run() { } } } catch (Exception e) { - LOG.error("Unexpected exception causing exit", e); + handleException(this.getName(), e); } LOG.info("ObserverRequestProcessor exited loop!"); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java index 4359b8dd2d0..a9276ea995c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java @@ -100,7 +100,8 @@ protected void setupRequestProcessors() { // Currently, they behave almost exactly the same as followers. RequestProcessor finalProcessor = new FinalRequestProcessor(this); commitProcessor = new CommitProcessor(finalProcessor, - Long.toString(getServerId()), true); + Long.toString(getServerId()), true, + getZooKeeperServerListener()); commitProcessor.start(); firstProcessor = new ObserverRequestProcessor(this, commitProcessor); ((ObserverRequestProcessor) firstProcessor).start(); @@ -138,7 +139,11 @@ public String getState() { }; @Override - public void shutdown() { + public synchronized void shutdown() { + if (!isRunning()) { + LOG.debug("ZooKeeper server is not running, so not proceeding to shutdown!"); + return; + } super.shutdown(); if (syncRequestProcessorEnabled && syncProcessor != null) { syncProcessor.shutdown(); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java index 33e5dd17120..dd4ad4529bc 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java @@ -51,8 +51,10 @@ public class ReadOnlyRequestProcessor extends ZooKeeperCriticalThread implements private ZooKeeperServer zks; - public ReadOnlyRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { - super("ReadOnlyRequestProcessor:" + zks.getServerId()); + public ReadOnlyRequestProcessor(ZooKeeperServer zks, + RequestProcessor nextProcessor) { + super("ReadOnlyRequestProcessor:" + zks.getServerId(), zks + .getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; } @@ -98,15 +100,13 @@ public void run() { nextProcessor.processRequest(request); } } - } catch (InterruptedException e) { - LOG.error("Unexpected interruption", e); } catch (RequestProcessorException e) { if (e.getCause() instanceof XidRolloverException) { LOG.info(e.getCause().getMessage()); } - LOG.error("Unexpected exception", e); + handleException(this.getName(), e); } catch (Exception e) { - LOG.error("Unexpected exception", e); + handleException(this.getName(), e); } LOG.info("ReadOnlyRequestProcessor exited loop!"); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java index 3cbfcaa1836..af1b6787cf8 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java @@ -131,6 +131,10 @@ public long getServerId() { @Override public synchronized void shutdown() { + if (!isRunning()) { + LOG.debug("ZooKeeper server is not running, so not proceeding to shutdown!"); + return; + } shutdown = true; unregisterJMX(this); diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java index 78f8b30acc9..4db02c738c0 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperThreadTest.java @@ -46,7 +46,13 @@ protected void handleException(String thName, Throwable e) { public class MyCriticalThread extends ZooKeeperCriticalThread { public MyCriticalThread(String threadName) { - super(threadName); + super(threadName, new ZooKeeperServerListener() { + + @Override + public void notifyStopping(String threadName, int erroCode) { + + } + }); } public void run() { From 908ea4084ea7b38b74b1fcaab7bfdf108d2a6093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Tue, 25 Aug 2015 05:13:25 +0000 Subject: [PATCH 295/444] ZOOKEEPER-1927: zkServer.sh fails to read dataDir (and others) from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID) (Chris Nauroth via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1697552 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 4 ++++ bin/zkServer.sh | 26 +++++++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e1c9c09fccb..1c34cd9a184 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -107,6 +107,10 @@ BUGFIXES: ZOOKEEPER-2239: JMX State from LocalPeerBean incorrect (Kevin Lee via rgs) + ZOOKEEPER-1927: zkServer.sh fails to read dataDir (and others) + from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID) + (Chris Nauroth via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/bin/zkServer.sh b/bin/zkServer.sh index e4a6c933428..74bc8d5fc87 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -101,8 +101,16 @@ fi echo "Using config: $ZOOCFG" >&2 +case "$OSTYPE" in +*solaris*) + GREP=/usr/xpg4/bin/grep + ;; +*) + GREP=grep + ;; +esac if [ -z "$ZOOPIDFILE" ]; then - ZOO_DATADIR="$(grep "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" + ZOO_DATADIR="$($GREP "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" if [ ! -d "$ZOO_DATADIR" ]; then mkdir -p "$ZOO_DATADIR" fi @@ -131,7 +139,15 @@ start) -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null & if [ $? -eq 0 ] then - if /bin/echo -n $! > "$ZOOPIDFILE" + case "$OSTYPE" in + *solaris*) + /bin/echo "${!}\\c" > "$ZOOPIDFILE" + ;; + *) + /bin/echo -n $! > "$ZOOPIDFILE" + ;; + esac + if [ $? -eq 0 ]; then sleep 1 echo STARTED @@ -182,16 +198,16 @@ restart) ;; status) # -q is necessary on some versions of linux where nc returns too quickly, and no stat result is output - clientPortAddress=`grep "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` + clientPortAddress=`$GREP "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` if ! [ $clientPortAddress ] then clientPortAddress="localhost" fi - clientPort=`grep "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` + clientPort=`$GREP "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \ $clientPortAddress $clientPort srvr 2> /dev/null \ - | grep Mode` + | $GREP Mode` if [ "x$STAT" = "x" ] then echo "Error contacting service. It is probably not running." From 3a7c6a5f010086a2305bbbde5efbf6ddb2e767ed Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Thu, 3 Sep 2015 23:00:20 +0000 Subject: [PATCH 296/444] ZOOKEEPER-2033: zookeeper follower fails to start after a restart immediately following a new epoch (Asad Saeed via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1701146 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../server/quorum/LearnerHandler.java | 17 +++---- .../server/quorum/QuorumPeerMainTest.java | 51 +++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1c34cd9a184..599c2537515 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -111,6 +111,9 @@ BUGFIXES: from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID) (Chris Nauroth via rgs) + ZOOKEEPER-2033: zookeeper follower fails to start after + a restart immediately following a new epoch (Asad Saeed via fpj) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 6d0ee256bd5..8a748c78f49 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -388,7 +388,13 @@ public void run() { LinkedList proposals = leader.zk.getZKDatabase().getCommittedLog(); - if (proposals.size() != 0) { + if (peerLastZxid == leader.zk.getZKDatabase().getDataTreeLastProcessedZxid()) { + // Follower is already sync with us, send empty diff + LOG.info("leader and follower are in sync, zxid=0x{}", + Long.toHexString(peerLastZxid)); + packetToSend = Leader.DIFF; + zxidToSend = peerLastZxid; + } else if (proposals.size() != 0) { LOG.debug("proposal size is {}", proposals.size()); if ((maxCommittedLog >= peerLastZxid) && (minCommittedLog <= peerLastZxid)) { @@ -444,15 +450,6 @@ public void run() { } else { LOG.warn("Unhandled proposal scenario"); } - } else if (peerLastZxid == leader.zk.getZKDatabase().getDataTreeLastProcessedZxid()) { - // The leader may recently take a snapshot, so the committedLog - // is empty. We don't need to send snapshot if the follow - // is already sync with in-memory db. - LOG.debug("committedLog is empty but leader and follower " - + "are in sync, zxid=0x{}", - Long.toHexString(peerLastZxid)); - packetToSend = Leader.DIFF; - zxidToSend = peerLastZxid; } else { // just let the state transfer happen LOG.debug("proposals is empty"); 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 9ae4da841cc..2bf36ba5a6d 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -795,4 +795,55 @@ public void testUpdatingEpoch() throws Exception { Assert.assertFalse("updatingEpoch file should get deleted", updatingEpochFile.exists()); } + + @Test + public void testNewFollowerRestartAfterNewEpoch() throws Exception { + numServers = 3; + + servers = LaunchServers(numServers); + waitForAll(servers.zk, States.CONNECTED); + String inputString = "test"; + byte[] input = inputString.getBytes(); + byte[] output; + String path = "/newepochzxidtest"; + + // Create a couple of nodes + servers.zk[0].create(path, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + servers.zk[0].setData(path, input, -1); + + // make sure the updates indeed committed. If it is not + // the following statement will throw. + output = servers.zk[1].getData(path, false, null); + + // Shutdown every one + for (int i=0; i < numServers; i++) { + servers.mt[i].shutdown(); + } + + LOG.info("resetting follower"); + MainThread follower = servers.mt[0]; + // delete followers information + File followerDataDir = new File(follower.dataDir, "version-2"); + for(File file: followerDataDir.listFiles()) { + LOG.info("deleting " + file.getName()); + file.delete(); + } + + // Startup everyone except follower, wait for election. + for (int i=1; i < numServers; i++) { + servers.mt[i].start(); + } + for (int i=1; i < numServers; i++) { + waitForOne(servers.zk[i], States.CONNECTED); + } + + follower.start(); + waitForAll(servers.zk, States.CONNECTED); // snapshot should be recieved + + follower.shutdown(); + follower.start(); + + Assert.assertFalse(follower.mainFailed.await(10, TimeUnit.SECONDS)); + waitForAll(servers.zk, States.CONNECTED); + } } From 1c1a2c785b4b18e639ff4497d96d721aaf11eb40 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Sun, 6 Sep 2015 17:48:13 +0000 Subject: [PATCH 297/444] ZOOKEEPER-2256: Zookeeper is not using specified JMX port in zkEnv.sh(Arshad Mohammad via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1701503 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ bin/zkServer.sh | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 599c2537515..6adbd09441a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -114,6 +114,9 @@ BUGFIXES: ZOOKEEPER-2033: zookeeper follower fails to start after a restart immediately following a new epoch (Asad Saeed via fpj) + ZOOKEEPER-2256: Zookeeper is not using specified JMX port in zkEnv.sh + (Arshad Mohammad via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/bin/zkServer.sh b/bin/zkServer.sh index 74bc8d5fc87..fbe6b8f5e30 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -21,6 +21,19 @@ # relative to the canonical path of this script. # + + +# use POSTIX interface, symlink is followed automatically +ZOOBIN="${BASH_SOURCE-$0}" +ZOOBIN="$(dirname "${ZOOBIN}")" +ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" + +if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then + . "$ZOOBINDIR/../libexec/zkEnv.sh" +else + . "$ZOOBINDIR/zkEnv.sh" +fi + # See the following page for extensive details on setting # up the JVM to accept JMX remote management: # http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html @@ -63,17 +76,6 @@ else ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" fi -# use POSTIX interface, symlink is followed automatically -ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" - -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then - . "$ZOOBINDIR/../libexec/zkEnv.sh" -else - . "$ZOOBINDIR/zkEnv.sh" -fi - if [ "x$SERVER_JVMFLAGS" != "x" ] then JVMFLAGS="$SERVER_JVMFLAGS $JVMFLAGS" From 8f326d5f5b2af8710ec64941fa5db7bca6bc06da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 10 Sep 2015 04:12:05 +0000 Subject: [PATCH 298/444] ZOOKEEPER-2270: Allow MBeanRegistry to be overridden for better unit tests (Jordan Zimmerman via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1702157 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6adbd09441a..4683c48155d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -144,6 +144,9 @@ IMPROVEMENTS: ZOOKEEPER-1907 Improve Thread handling (Rakesh R via hdeng) + ZOOKEEPER-2270: Allow MBeanRegistry to be overridden for better unit tests + (Jordan Zimmerman via rgs) + NEW FEATURES: ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) diff --git a/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java b/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java index 895b81864c9..0fcfe2fc01f 100644 --- a/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java +++ b/src/java/main/org/apache/zookeeper/jmx/MBeanRegistry.java @@ -40,7 +40,7 @@ public class MBeanRegistry { private static final Logger LOG = LoggerFactory.getLogger(MBeanRegistry.class); - private static MBeanRegistry instance = new MBeanRegistry(); + private static volatile MBeanRegistry instance = new MBeanRegistry(); private Map mapBean2Path = new ConcurrentHashMap(); @@ -50,6 +50,10 @@ public class MBeanRegistry { private MBeanServer mBeanServer; + public static void setInstance(MBeanRegistry instance) { + MBeanRegistry.instance = instance; + } + public static MBeanRegistry getInstance() { return instance; } From 124670eb739d064e6bd01da451e92fe0084bdc6c Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Fri, 11 Sep 2015 06:02:29 +0000 Subject: [PATCH 299/444] ZOOKEEPER-2040: Server to log underlying cause of SASL connection problems. (Steve Loughran via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1702381 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4683c48155d..d0022a66d0f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -147,6 +147,9 @@ IMPROVEMENTS: ZOOKEEPER-2270: Allow MBeanRegistry to be overridden for better unit tests (Jordan Zimmerman via rgs) + ZOOKEEPER-2040: Server to log underlying cause of SASL connection problems. + (Steve Loughran via cnauroth) + NEW FEATURES: ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index 435198bc104..c03c9e0ff0c 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -996,7 +996,7 @@ private Record processSasl(ByteBuffer incomingBuffer, ServerCnxn cnxn) throws IO } } catch (SaslException e) { - LOG.warn("Client failed to SASL authenticate: " + e); + LOG.warn("Client failed to SASL authenticate: " + e, e); if ((System.getProperty("zookeeper.allowSaslFailedClients") != null) && (System.getProperty("zookeeper.allowSaslFailedClients").equals("true"))) { From 9350096d66c4e405f312a453185471b2306fb9ee Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Thu, 17 Sep 2015 07:12:00 +0000 Subject: [PATCH 300/444] ZOOKEEPER-2245: SimpleSysTest test cases fails (Arshad Mohammad via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1703504 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../apache/zookeeper/test/system/BaseSysTest.java | 13 +++++++++++-- .../apache/zookeeper/test/system/SimpleClient.java | 4 +++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d0022a66d0f..671d6c829d2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -150,6 +150,8 @@ IMPROVEMENTS: ZOOKEEPER-2040: Server to log underlying cause of SASL connection problems. (Steve Loughran via cnauroth) + ZOOKEEPER-2245: SimpleSysTest test cases fails (Arshad Mohammad via rakeshr) + NEW FEATURES: ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) diff --git a/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java b/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java index d033a46d7d6..cca1596c9ad 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java +++ b/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java @@ -61,7 +61,9 @@ protected void setUp() throws Exception { } @Override protected void tearDown() throws Exception { - im.close(); + if (null != im) { + im.close(); + } } int serverCount = defaultServerCount; @@ -147,10 +149,17 @@ private void fakeConfigureServers(int count) throws IOException { qps = new QuorumPeer[count]; qpsDirs = new File[count]; for(int i = 1; i <= count; i++) { - peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress("127.0.0.1", fakeBasePort + i))); + InetSocketAddress peerAddress = new InetSocketAddress("127.0.0.1", + fakeBasePort + i); + InetSocketAddress electionAddr = new InetSocketAddress("127.0.0.1", + serverCount + fakeBasePort + i); + peers.put(Long.valueOf(i), new QuorumServer(i, peerAddress, + electionAddr)); } StringBuilder sb = new StringBuilder(); for(int i = 0; i < count; i++) { + //make that testData exists otherwise it fails on windows + testData.mkdirs(); qpsDirs[i] = File.createTempFile("sysTest", ".tmp", testData); qpsDirs[i].delete(); qpsDirs[i].mkdir(); diff --git a/src/java/systest/org/apache/zookeeper/test/system/SimpleClient.java b/src/java/systest/org/apache/zookeeper/test/system/SimpleClient.java index 44f113ab460..a8cd3e5307b 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/SimpleClient.java +++ b/src/java/systest/org/apache/zookeeper/test/system/SimpleClient.java @@ -53,7 +53,9 @@ public void start() { try { zk = new ZooKeeper(hostPort, 15000, this); zk.getData("/simpleCase", true, this, null); - r.report("Client " + index + " connecting to " + hostPort); + if (null != r) { + r.report("Client " + index + " connecting to " + hostPort); + } } catch (Exception e) { e.printStackTrace(); } From 4d24c7308d305e01f48ba36a072e9c5a907e04cb Mon Sep 17 00:00:00 2001 From: Flavio Paiva Junqueira Date: Wed, 23 Sep 2015 17:18:08 +0000 Subject: [PATCH 301/444] ZOOKEEPER-1506: Re-try DNS hostname -> IP resolution if node connection fails (Robert Thille via fpj) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1704903 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 12 ++++ CHANGES.txt | 3 + .../zookeeper/server/quorum/Learner.java | 3 + .../server/quorum/QuorumCnxManager.java | 11 ++++ .../zookeeper/server/quorum/QuorumPeer.java | 65 ++++++++++++++++++- .../server/quorum/QuorumPeerConfig.java | 30 ++++----- .../zookeeper/test/system/BaseSysTest.java | 8 +-- .../test/system/QuorumPeerInstance.java | 2 +- .../server/quorum/CnxManagerTest.java | 13 ++-- .../quorum/FLEBackwardElectionRoundTest.java | 5 +- .../server/quorum/FLECompatibilityTest.java | 6 +- .../server/quorum/FLEDontCareTest.java | 3 +- .../server/quorum/FLELostMessageTest.java | 5 +- .../zookeeper/server/quorum/Zab1_0Test.java | 4 +- .../zookeeper/test/FLENewEpochTest.java | 5 +- .../zookeeper/test/FLEPredicateTest.java | 5 +- .../apache/zookeeper/test/FLERestartTest.java | 5 +- .../org/apache/zookeeper/test/FLETest.java | 15 ++--- .../zookeeper/test/FLEZeroWeightTest.java | 4 +- .../test/HierarchicalQuorumTest.java | 20 ++---- .../zookeeper/test/LENonTerminateTest.java | 5 +- .../org/apache/zookeeper/test/LETest.java | 4 +- .../org/apache/zookeeper/test/QuorumBase.java | 50 ++++++-------- .../org/apache/zookeeper/test/QuorumUtil.java | 7 +- .../apache/zookeeper/test/TruncateTest.java | 6 +- 25 files changed, 171 insertions(+), 125 deletions(-) diff --git a/.gitignore b/.gitignore index 4dbed1a49b2..2428fad1f20 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,15 @@ src/c/generated/ src/java/generated/ src/java/lib/ant-eclipse-* src/java/lib/ivy-* +src/c/Makefile.in +src/c/aclocal.m4 +src/c/autom4te.cache/ +src/c/compile +src/c/config.guess +src/c/config.h.in +src/c/config.sub +src/c/configure +src/c/depcomp +src/c/install-sh +src/c/ltmain.sh +src/c/missing diff --git a/CHANGES.txt b/CHANGES.txt index 671d6c829d2..f88d3f829a9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -116,6 +116,9 @@ BUGFIXES: ZOOKEEPER-2256: Zookeeper is not using specified JMX port in zkEnv.sh (Arshad Mohammad via rakeshr) + + ZOOKEEPER-1506: Re-try DNS hostname -> IP resolution if node connection + fails (Robert Thille via fpj) IMPROVEMENTS: diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index 0af88449b35..c73a8ee4716 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -197,6 +197,9 @@ protected InetSocketAddress findLeader() { Vote current = self.getCurrentVote(); for (QuorumServer s : self.getView().values()) { if (s.id == current.getId()) { + // Ensure we have the leader's correct IP address before + // attempting to connect. + s.recreateSocketAddresses(); addr = s.addr; break; } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index c7e3bbc16d7..20e5f165bd6 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -390,11 +390,22 @@ synchronized void connectOne(long sid){ // detail. LOG.warn("Cannot open channel to " + sid + " at election address " + electionAddr, e); + // Resolve hostname for this server in case the + // underlying ip address has changed. + if (self.getView().containsKey(sid)) { + self.getView().get(sid).recreateSocketAddresses(); + } throw e; } catch (IOException e) { LOG.warn("Cannot open channel to " + sid + " at election address " + electionAddr, e); + // We can't really tell if the server is actually down or it failed + // to connect to the server because the underlying IP address + // changed. Resolve the hostname again just in case. + if (self.getView().containsKey(sid)) { + self.getView().get(sid).recreateSocketAddresses(); + } } } else { LOG.debug("There is a connection already for server " + sid); 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 095df59657a..20d1e81e4ab 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -26,8 +26,10 @@ import java.io.OutputStreamWriter; import java.net.DatagramPacket; import java.net.DatagramSocket; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; +import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; @@ -93,20 +95,20 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider private ZKDatabase zkDb; public static class QuorumServer { - public QuorumServer(long id, InetSocketAddress addr, + private QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr) { this.id = id; this.addr = addr; this.electionAddr = electionAddr; } - public QuorumServer(long id, InetSocketAddress addr) { + private QuorumServer(long id, InetSocketAddress addr) { this.id = id; this.addr = addr; this.electionAddr = null; } - public QuorumServer(long id, InetSocketAddress addr, + private QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr, LearnerType type) { this.id = id; this.addr = addr; @@ -114,10 +116,67 @@ public QuorumServer(long id, InetSocketAddress addr, this.type = type; } + public QuorumServer(long id, String hostname, + Integer port, Integer electionPort, + LearnerType type) { + this.id = id; + this.hostname=hostname; + if (port!=null){ + this.port=port; + } + if (electionPort!=null){ + this.electionPort=electionPort; + } + if (type!=null){ + this.type = type; + } + this.recreateSocketAddresses(); + } + + /** + * Performs a DNS lookup of hostname and (re)creates the this.addr and + * this.electionAddr InetSocketAddress objects as appropriate + * + * If the DNS lookup fails, this.addr and electionAddr remain + * unmodified, unless they were never set. If this.addr is null, then + * it is set with an unresolved InetSocketAddress object. this.electionAddr + * is handled similarly. + */ + public void recreateSocketAddresses() { + InetAddress address = null; + try { + address = InetAddress.getByName(this.hostname); + LOG.info("Resolved hostname: {} to address: {}", this.hostname, address); + this.addr = new InetSocketAddress(address, this.port); + if (this.electionPort > 0){ + this.electionAddr = new InetSocketAddress(address, this.electionPort); + } + } catch (UnknownHostException ex) { + LOG.warn("Failed to resolve address: {}", this.hostname, ex); + // Have we succeeded in the past? + if (this.addr != null) { + // Yes, previously the lookup succeeded. Leave things as they are + return; + } + // The hostname has never resolved. Create our InetSocketAddress(es) as unresolved + this.addr = InetSocketAddress.createUnresolved(this.hostname, this.port); + if (this.electionPort > 0){ + this.electionAddr = InetSocketAddress.createUnresolved(this.hostname, + this.electionPort); + } + } + } + public InetSocketAddress addr; public InetSocketAddress electionAddr; + public String hostname; + + public int port=2888; + + public int electionPort=-1; + public long id; public LearnerType type = LearnerType.PARTICIPANT; 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 0e635f38ec6..8ae820d5d69 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java @@ -185,31 +185,27 @@ public void parseProperties(Properties zkProp) + " does not have the form host:port or host:port:port " + " or host:port:port:type"); } - InetSocketAddress addr = new InetSocketAddress(parts[0], - Integer.parseInt(parts[1])); - if (parts.length == 2) { - servers.put(Long.valueOf(sid), new QuorumServer(sid, addr)); - } else if (parts.length == 3) { - InetSocketAddress electionAddr = new InetSocketAddress( - parts[0], Integer.parseInt(parts[2])); - servers.put(Long.valueOf(sid), new QuorumServer(sid, addr, - electionAddr)); - } else if (parts.length == 4) { - InetSocketAddress electionAddr = new InetSocketAddress( - parts[0], Integer.parseInt(parts[2])); - LearnerType type = LearnerType.PARTICIPANT; + LearnerType type = null; + String hostname = parts[0]; + Integer port = Integer.parseInt(parts[1]); + Integer electionPort = null; + if (parts.length > 2){ + electionPort=Integer.parseInt(parts[2]); + } + if (parts.length > 3){ if (parts[3].toLowerCase().equals("observer")) { type = LearnerType.OBSERVER; - observers.put(Long.valueOf(sid), new QuorumServer(sid, addr, - electionAddr,type)); } else if (parts[3].toLowerCase().equals("participant")) { type = LearnerType.PARTICIPANT; - servers.put(Long.valueOf(sid), new QuorumServer(sid, addr, - electionAddr,type)); } else { throw new ConfigException("Unrecognised peertype: " + value); } } + if (type == LearnerType.OBSERVER){ + observers.put(Long.valueOf(sid), new QuorumServer(sid, hostname, port, electionPort, type)); + } else { + servers.put(Long.valueOf(sid), new QuorumServer(sid, hostname, port, electionPort, type)); + } } else if (key.startsWith("group")) { int dot = key.indexOf('.'); long gid = Long.parseLong(key.substring(dot + 1)); diff --git a/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java b/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java index cca1596c9ad..3d963d26d7d 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java +++ b/src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java @@ -149,12 +149,8 @@ private void fakeConfigureServers(int count) throws IOException { qps = new QuorumPeer[count]; qpsDirs = new File[count]; for(int i = 1; i <= count; i++) { - InetSocketAddress peerAddress = new InetSocketAddress("127.0.0.1", - fakeBasePort + i); - InetSocketAddress electionAddr = new InetSocketAddress("127.0.0.1", - serverCount + fakeBasePort + i); - peers.put(Long.valueOf(i), new QuorumServer(i, peerAddress, - electionAddr)); + peers.put(Long.valueOf(i), new QuorumServer( + i, "127.0.0.1", fakeBasePort + i, serverCount + fakeBasePort + i, null)); } StringBuilder sb = new StringBuilder(); for(int i = 0; i < count; i++) { diff --git a/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java b/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java index e052101317f..95aebc8e644 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java +++ b/src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java @@ -163,7 +163,7 @@ public void configure(String params) { peers = new HashMap(); for(int i = 0; i < parts.length; i++) { String subparts[] = parts[i].split(":"); - peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress(subparts[0], Integer.parseInt(subparts[1])))); + peers.put(Long.valueOf(i), new QuorumServer(i, subparts[0], Integer.parseInt(subparts[1]), 0, null)); } try { if (LOG.isDebugEnabled()) { diff --git a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java index 13874df8e86..831d3ed202d 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java @@ -66,9 +66,9 @@ public void setUp() throws Exception { peerQuorumPort[i] = PortAssignment.unique(); peerClientPort[i] = PortAssignment.unique(); peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(peerQuorumPort[i]), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", + peerQuorumPort[i], + PortAssignment.unique(), null)); peerTmpdir[i] = ClientBase.createTmpDir(); } } @@ -164,15 +164,14 @@ public void testCnxManager() throws Exception { public void testCnxManagerTimeout() throws Exception { Random rand = new Random(); byte b = (byte) rand.nextInt(); + int finalOctet = b & 0xFF; int deadPort = PortAssignment.unique(); - String deadAddress = new String("10.1.1." + b); + String deadAddress = new String("192.0.2." + finalOctet); LOG.info("This is the dead address I'm trying: " + deadAddress); peers.put(Long.valueOf(2), - new QuorumServer(2, - new InetSocketAddress(deadAddress, deadPort), - new InetSocketAddress(deadAddress, PortAssignment.unique()))); + new QuorumServer(2, deadAddress, deadPort, PortAssignment.unique(), null)); peerTmpdir[2] = ClientBase.createTmpDir(); QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java index 3eeb0d32d7a..c1259d12895 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java @@ -93,9 +93,8 @@ public void testBackwardElectionRound() throws Exception { for(int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(clientport), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", clientport, + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java index cf5c0e08ee7..72e4fc97db9 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java @@ -273,9 +273,9 @@ void populate() throws Exception { for (int i = 0; i < count; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", + PortAssignment.unique(), + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java index 826d91f70a4..a4c0cb0bd64 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java @@ -74,8 +74,7 @@ public void setUp() peers = new HashMap(); for(int i = 0; i < 5; i++) { peers.put(Long.valueOf(i), - new QuorumServer(Long.valueOf(i), - new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); + new QuorumServer(Long.valueOf(i), "127.0.0.1", PortAssignment.unique(), 0, null)); } peer = new QuorumPeer(peers, tmpdir, diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java index 52c40c4e973..39a53cad397 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java @@ -71,9 +71,8 @@ public void testLostMessage() throws Exception { for(int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(clientport), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", clientport, + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 6b5d410d104..ab8ce420997 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -1394,8 +1394,8 @@ private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, peer.initLimit = 2; peer.tickTime = 2000; peer.quorumPeers = new HashMap(); - peer.quorumPeers.put(1L, new QuorumServer(0, new InetSocketAddress(33221))); - peer.quorumPeers.put(1L, new QuorumServer(1, new InetSocketAddress(33223))); + peer.quorumPeers.put(1L, new QuorumServer(0, "0.0.0.0", 33221, 0, null)); + peer.quorumPeers.put(1L, new QuorumServer(1, "0.0.0.0", 33223, 0, null)); peer.setQuorumVerifier(new QuorumMaj(3)); peer.setCnxnFactory(new NullServerCnxnFactory()); File version2 = new File(tmpDir, "version-2"); diff --git a/src/java/test/org/apache/zookeeper/test/FLENewEpochTest.java b/src/java/test/org/apache/zookeeper/test/FLENewEpochTest.java index 1e1abe0d155..1731e6e801a 100644 --- a/src/java/test/org/apache/zookeeper/test/FLENewEpochTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLENewEpochTest.java @@ -157,9 +157,8 @@ public void testLENewEpoch() throws Exception { LOG.info("TestLE: " + getTestName()+ ", " + count); for(int i = 0; i < count; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", PortAssignment.unique(), + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } diff --git a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java index 1712da2add4..80885050f5d 100644 --- a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java @@ -62,9 +62,8 @@ public void testPredicate() throws IOException { */ for(int i = 0; i < 3; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", PortAssignment.unique(), + PortAssignment.unique(), null)); } /* diff --git a/src/java/test/org/apache/zookeeper/test/FLERestartTest.java b/src/java/test/org/apache/zookeeper/test/FLERestartTest.java index f092563fe79..a7cecf64d27 100644 --- a/src/java/test/org/apache/zookeeper/test/FLERestartTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLERestartTest.java @@ -178,9 +178,8 @@ public void testLERestart() throws Exception { LOG.info("TestLE: " + getTestName()+ ", " + count); for(int i = 0; i < count; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", PortAssignment.unique(), + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } diff --git a/src/java/test/org/apache/zookeeper/test/FLETest.java b/src/java/test/org/apache/zookeeper/test/FLETest.java index d9e4e7976e5..6281a57d9e4 100644 --- a/src/java/test/org/apache/zookeeper/test/FLETest.java +++ b/src/java/test/org/apache/zookeeper/test/FLETest.java @@ -269,9 +269,8 @@ public void testLE() throws Exception { LOG.info("TestLE: " + getTestName()+ ", " + count); for(int i = 0; i < count; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", PortAssignment.unique(), + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } @@ -366,9 +365,8 @@ public void testJoin() throws Exception { ArrayList peerList = new ArrayList(); for(sid = 0; sid < 3; sid++) { peers.put(Long.valueOf(sid), - new QuorumServer(sid, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(sid, "0.0.0.0", PortAssignment.unique(), + PortAssignment.unique(), null)); tmpdir[sid] = ClientBase.createTmpDir(); port[sid] = PortAssignment.unique(); } @@ -423,9 +421,8 @@ public void testJoinInconsistentEnsemble() throws Exception { ArrayList peerList = new ArrayList(); for(sid = 0; sid < 3; sid++) { peers.put(Long.valueOf(sid), - new QuorumServer(sid, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(sid, "0.0.0.0", PortAssignment.unique(), + PortAssignment.unique(), null)); tmpdir[sid] = ClientBase.createTmpDir(); port[sid] = PortAssignment.unique(); } diff --git a/src/java/test/org/apache/zookeeper/test/FLEZeroWeightTest.java b/src/java/test/org/apache/zookeeper/test/FLEZeroWeightTest.java index f3d1316dd3c..e8a8cf79272 100644 --- a/src/java/test/org/apache/zookeeper/test/FLEZeroWeightTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLEZeroWeightTest.java @@ -153,9 +153,7 @@ public void testZeroWeightQuorum() throws Exception { LOG.info("TestZeroWeightQuorum: " + getTestName()+ ", " + count); for(int i = 0; i < count; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress(PortAssignment.unique()), - new InetSocketAddress(PortAssignment.unique()))); + new QuorumServer(i, "0.0.0.0", PortAssignment.unique(), PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } diff --git a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java index b4f22798559..c6573f46292 100644 --- a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java @@ -128,23 +128,13 @@ void startServers(boolean withObservers) throws Exception { int initLimit = 3; int syncLimit = 3; HashMap peers = new HashMap(); - peers.put(Long.valueOf(1), new QuorumServer(1, - new InetSocketAddress("127.0.0.1", port1 + 1000), - new InetSocketAddress("127.0.0.1", leport1 + 1000))); - peers.put(Long.valueOf(2), new QuorumServer(2, - new InetSocketAddress("127.0.0.1", port2 + 1000), - new InetSocketAddress("127.0.0.1", leport2 + 1000))); - peers.put(Long.valueOf(3), new QuorumServer(3, - new InetSocketAddress("127.0.0.1", port3 + 1000), - new InetSocketAddress("127.0.0.1", leport3 + 1000))); - peers.put(Long.valueOf(4), new QuorumServer(4, - new InetSocketAddress("127.0.0.1", port4 + 1000), - new InetSocketAddress("127.0.0.1", leport4 + 1000), + peers.put(Long.valueOf(1), new QuorumServer(1, "127.0.0.1", port1 + 1000, leport1 + 1000, null)); + peers.put(Long.valueOf(2), new QuorumServer(2, "127.0.0.1", port2 + 1000, leport2 + 1000, null)); + peers.put(Long.valueOf(3), new QuorumServer(3, "127.0.0.1", port3 + 1000, leport3 + 1000, null)); + peers.put(Long.valueOf(4), new QuorumServer(4, "127.0.0.1", port4 + 1000, leport4 + 1000, withObservers ? QuorumPeer.LearnerType.OBSERVER : QuorumPeer.LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(5), new QuorumServer(5, - new InetSocketAddress("127.0.0.1", port5 + 1000), - new InetSocketAddress("127.0.0.1", leport5 + 1000), + peers.put(Long.valueOf(5), new QuorumServer(5, "127.0.0.1", port5 + 1000, leport5 + 1000, withObservers ? QuorumPeer.LearnerType.OBSERVER : QuorumPeer.LearnerType.PARTICIPANT)); diff --git a/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java b/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java index dbf8ba9f0a0..de266988f69 100644 --- a/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java +++ b/src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java @@ -300,9 +300,8 @@ public void testNonTermination() throws Exception { for(int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress("127.0.0.1", clientport), - new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); + new QuorumServer(i, "127.0.0.1", clientport, + PortAssignment.unique(), null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } diff --git a/src/java/test/org/apache/zookeeper/test/LETest.java b/src/java/test/org/apache/zookeeper/test/LETest.java index 78d68f028f4..0fd34302282 100644 --- a/src/java/test/org/apache/zookeeper/test/LETest.java +++ b/src/java/test/org/apache/zookeeper/test/LETest.java @@ -98,9 +98,7 @@ public void testLE() throws Exception { votes = new Vote[count]; for(int i = 0; i < count; i++) { peers.put(Long.valueOf(i), - new QuorumServer(i, - new InetSocketAddress("127.0.0.1", - PortAssignment.unique()))); + new QuorumServer(i, "127.0.0.1", PortAssignment.unique(), 0, null)); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } diff --git a/src/java/test/org/apache/zookeeper/test/QuorumBase.java b/src/java/test/org/apache/zookeeper/test/QuorumBase.java index dc08d7c5673..49cff350f3c 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumBase.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumBase.java @@ -118,25 +118,20 @@ void startServers(boolean withObservers) throws Exception { int initLimit = 3; int syncLimit = 3; HashMap peers = new HashMap(); - peers.put(Long.valueOf(1), new QuorumServer(1, - new InetSocketAddress("127.0.0.1", port1 + 1000), - new InetSocketAddress("127.0.0.1", portLE1 + 1000), + peers.put(Long.valueOf(1), new QuorumServer(1, "127.0.0.1", port1 + 1000, + portLE1 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(2), new QuorumServer(2, - new InetSocketAddress("127.0.0.1", port2 + 1000), - new InetSocketAddress("127.0.0.1", portLE2 + 1000), + peers.put(Long.valueOf(2), new QuorumServer(2, "127.0.0.1", port2 + 1000, + portLE2 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(3), new QuorumServer(3, - new InetSocketAddress("127.0.0.1", port3 + 1000), - new InetSocketAddress("127.0.0.1", portLE3 + 1000), + peers.put(Long.valueOf(3), new QuorumServer(3, "127.0.0.1", port3 + 1000, + portLE3 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(4), new QuorumServer(4, - new InetSocketAddress("127.0.0.1", port4 + 1000), - new InetSocketAddress("127.0.0.1", portLE4 + 1000), + peers.put(Long.valueOf(4), new QuorumServer(4, "127.0.0.1", port4 + 1000, + portLE4 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(5), new QuorumServer(5, - new InetSocketAddress("127.0.0.1", port5 + 1000), - new InetSocketAddress("127.0.0.1", portLE5 + 1000), + peers.put(Long.valueOf(5), new QuorumServer(5, "127.0.0.1", port5 + 1000, + portLE5 + 1000, LearnerType.PARTICIPANT)); if (withObservers) { @@ -232,25 +227,20 @@ public void setupServer(int i) throws IOException { if(peers == null){ peers = new HashMap(); - peers.put(Long.valueOf(1), new QuorumServer(1, - new InetSocketAddress("127.0.0.1", port1 + 1000), - new InetSocketAddress("127.0.0.1", portLE1 + 1000), + peers.put(Long.valueOf(1), new QuorumServer(1, "127.0.0.1", port1 + 1000, + portLE1 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(2), new QuorumServer(2, - new InetSocketAddress("127.0.0.1", port2 + 1000), - new InetSocketAddress("127.0.0.1", portLE2 + 1000), + peers.put(Long.valueOf(2), new QuorumServer(2, "127.0.0.1", port2 + 1000, + portLE2 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(3), new QuorumServer(3, - new InetSocketAddress("127.0.0.1", port3 + 1000), - new InetSocketAddress("127.0.0.1", portLE3 + 1000), + peers.put(Long.valueOf(3), new QuorumServer(3, "127.0.0.1", port3 + 1000, + portLE3 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(4), new QuorumServer(4, - new InetSocketAddress("127.0.0.1", port4 + 1000), - new InetSocketAddress("127.0.0.1", portLE4 + 1000), + peers.put(Long.valueOf(4), new QuorumServer(4, "127.0.0.1", port4 + 1000, + portLE4 + 1000, LearnerType.PARTICIPANT)); - peers.put(Long.valueOf(5), new QuorumServer(5, - new InetSocketAddress("127.0.0.1", port5 + 1000), - new InetSocketAddress("127.0.0.1", portLE5 + 1000), + peers.put(Long.valueOf(5), new QuorumServer(5, "127.0.0.1", port5 + 1000, + portLE5 + 1000, LearnerType.PARTICIPANT)); } diff --git a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java index 8b0c2a6df21..1f2dbbc7ed8 100644 --- a/src/java/test/org/apache/zookeeper/test/QuorumUtil.java +++ b/src/java/test/org/apache/zookeeper/test/QuorumUtil.java @@ -99,9 +99,10 @@ public QuorumUtil(int n, int syncLimit) throws RuntimeException { ps.clientPort = PortAssignment.unique(); peers.put(i, ps); - peersView.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress( - "127.0.0.1", ps.clientPort + 1000), new InetSocketAddress("127.0.0.1", - PortAssignment.unique() + 1000), LearnerType.PARTICIPANT)); + peersView.put(Long.valueOf(i), + new QuorumServer(i, "127.0.0.1", ps.clientPort + 1000, + PortAssignment.unique() + 1000, + LearnerType.PARTICIPANT)); hostPort += "127.0.0.1:" + ps.clientPort + ((i == ALL) ? "" : ","); } for (int i = 1; i <= ALL; ++i) { diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java index c528681dcdf..66ff63d0ffa 100644 --- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java +++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java @@ -201,9 +201,9 @@ public void testTruncate() throws IOException, InterruptedException, KeeperExcep // Start up two of the quorum and add 10 txns HashMap peers = new HashMap(); - peers.put(Long.valueOf(1), new QuorumServer(1, new InetSocketAddress("127.0.0.1", port1 + 1000))); - peers.put(Long.valueOf(2), new QuorumServer(2, new InetSocketAddress("127.0.0.1", port2 + 1000))); - peers.put(Long.valueOf(3), new QuorumServer(3, new InetSocketAddress("127.0.0.1", port3 + 1000))); + peers.put(Long.valueOf(1), new QuorumServer(1, "127.0.0.1", port1 + 1000, 0, null)); + peers.put(Long.valueOf(2), new QuorumServer(2, "127.0.0.1", port2 + 1000, 0, null)); + peers.put(Long.valueOf(3), new QuorumServer(3, "127.0.0.1", port3 + 1000, 0, null)); QuorumPeer s2 = new QuorumPeer(peers, dataDir2, dataDir2, port2, 0, 2, tickTime, initLimit, syncLimit); s2.start(); From 77199ccb8e7a6a72c29c3ebd5cfdd84ad9145ca6 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Fri, 25 Sep 2015 06:30:38 +0000 Subject: [PATCH 302/444] ZOOKEEPER-2279: QuorumPeer loadDataBase() error message is incorrect(Arshad Mohammad via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1705218 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/server/quorum/QuorumPeer.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f88d3f829a9..371647bca52 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -120,6 +120,9 @@ BUGFIXES: ZOOKEEPER-1506: Re-try DNS hostname -> IP resolution if node connection fails (Robert Thille via fpj) + ZOOKEEPER-2279: QuorumPeer loadDataBase() error message is incorrect + (Arshad Mohammad via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for 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 20d1e81e4ab..2f0f21bcb18 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -551,7 +551,7 @@ private void loadDataBase() { writeLongToFile(ACCEPTED_EPOCH_FILENAME, acceptedEpoch); } if (acceptedEpoch < currentEpoch) { - throw new IOException("The current epoch, " + ZxidUtils.zxidToString(currentEpoch) + " is less than the accepted epoch, " + ZxidUtils.zxidToString(acceptedEpoch)); + throw new IOException("The accepted epoch, " + ZxidUtils.zxidToString(acceptedEpoch) + " is less than the current epoch, " + ZxidUtils.zxidToString(currentEpoch)); } } catch(IOException ie) { LOG.error("Unable to load database on disk", ie); From f386afe13439069a0b1dc915c734fd90988543f4 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Fri, 25 Sep 2015 06:47:17 +0000 Subject: [PATCH 303/444] ZOOKEEPER-1803: Add description for pzxid in programmer's guide(Arshad Mohammad via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1705223 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../documentation/content/xdocs/zookeeperProgrammers.xml | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 371647bca52..22c47fdff62 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -123,6 +123,9 @@ BUGFIXES: ZOOKEEPER-2279: QuorumPeer loadDataBase() error message is incorrect (Arshad Mohammad via rakeshr) + ZOOKEEPER-1803: Add description for pzxid in programmer's guide + (Arshad Mohammad via rakeshr) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml index 13642538fcd..b75cc851c13 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml @@ -313,6 +313,12 @@ The zxid of the change that last modified this znode. + + pzxid + + The zxid of the change that last modified children of this znode. + + ctime From ddce585502b6974234a9901249c6cfaad61e7400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 27 Sep 2015 02:25:16 +0000 Subject: [PATCH 304/444] ZOOKEEPER-2253: C asserts ordering of ping requests, while Java client does not (Chris Chen via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1705505 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ src/c/src/zookeeper.c | 71 ++++++++++++++--------------------- src/c/tests/TestOperations.cc | 35 +++++++++++++++++ 3 files changed, 66 insertions(+), 43 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 22c47fdff62..8b68898bda0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -126,6 +126,9 @@ BUGFIXES: ZOOKEEPER-1803: Add description for pzxid in programmer's guide (Arshad Mohammad via rakeshr) + ZOOKEEPER-2253: C asserts ordering of ping requests, while Java client does not + (Chris Chen via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 00e62749281..24514be1940 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1167,25 +1167,20 @@ void free_completions(zhandle_t *zh,int callCompletion,int reason) zh->outstanding_sync--; destroy_completion_entry(cptr); } else if (callCompletion) { - if(cptr->xid == PING_XID){ - // Nothing to do with a ping response - destroy_completion_entry(cptr); - } else { - // Fake the response - buffer_list_t *bptr; - h.xid = cptr->xid; - h.zxid = -1; - h.err = reason; - oa = create_buffer_oarchive(); - serialize_ReplyHeader(oa, "header", &h); - bptr = calloc(sizeof(*bptr), 1); - assert(bptr); - bptr->len = get_buffer_len(oa); - bptr->buffer = get_buffer(oa); - close_buffer_oarchive(&oa, 0); - cptr->buffer = bptr; - queue_completion(&zh->completions_to_process, cptr, 0); - } + // Fake the response + buffer_list_t *bptr; + h.xid = cptr->xid; + h.zxid = -1; + h.err = reason; + oa = create_buffer_oarchive(); + serialize_ReplyHeader(oa, "header", &h); + bptr = calloc(sizeof(*bptr), 1); + assert(bptr); + bptr->len = get_buffer_len(oa); + bptr->buffer = get_buffer(oa); + close_buffer_oarchive(&oa, 0); + cptr->buffer = bptr; + queue_completion(&zh->completions_to_process, cptr, 0); } } a_list.completion = NULL; @@ -1526,7 +1521,6 @@ static struct timeval get_timeval(int interval) rc = serialize_RequestHeader(oa, "header", &h); enter_critical(zh); gettimeofday(&zh->last_ping, 0); - rc = rc < 0 ? rc : add_void_completion(zh, h.xid, 0, 0); rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), get_buffer_len(oa)); leave_critical(zh); @@ -2079,12 +2073,8 @@ static void deserialize_response(int type, int xid, int failed, int rc, completi case COMPLETION_VOID: LOG_DEBUG(("Calling COMPLETION_VOID for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc)); - if (xid == PING_XID) { - // We want to skip the ping - } else { - assert(cptr->c.void_result); - cptr->c.void_result(rc, cptr->data); - } + assert(cptr->c.void_result); + cptr->c.void_result(rc, cptr->data); break; case COMPLETION_MULTI: LOG_DEBUG(("Calling COMPLETION_MULTI for xid=%#x failed=%d rc=%d", @@ -2200,7 +2190,15 @@ int zookeeper_process(zhandle_t *zh, int events) // fprintf(stderr, "Got %#x for %#x\n", hdr.zxid, hdr.xid); } - if (hdr.xid == WATCHER_EVENT_XID) { + if (hdr.xid == PING_XID) { + // Ping replies can arrive out-of-order + int elapsed = 0; + struct timeval now; + gettimeofday(&now, 0); + elapsed = calculate_interval(&zh->last_ping, &now); + LOG_DEBUG(LOGCALLBACK(zh), "Got ping response in %d ms", elapsed); + free_buffer(bptr); + } else if (hdr.xid == WATCHER_EVENT_XID) { struct WatcherEvent evt; int type = 0; char *path = NULL; @@ -2267,22 +2265,9 @@ int zookeeper_process(zhandle_t *zh, int events) activateWatcher(zh, cptr->watcher, rc); if (cptr->c.void_result != SYNCHRONOUS_MARKER) { - if(hdr.xid == PING_XID){ - int elapsed = 0; - struct timeval now; - gettimeofday(&now, 0); - elapsed = calculate_interval(&zh->last_ping, &now); - LOG_DEBUG(("Got ping response in %d ms", elapsed)); - - // Nothing to do with a ping response - free_buffer(bptr); - destroy_completion_entry(cptr); - } else { - LOG_DEBUG(("Queueing asynchronous response")); - - cptr->buffer = bptr; - queue_completion(&zh->completions_to_process, cptr, 0); - } + LOG_DEBUG(LOGCALLBACK(zh), "Queueing asynchronous response"); + cptr->buffer = bptr; + queue_completion(&zh->completions_to_process, cptr, 0); } else { struct sync_completion *sc = (struct sync_completion*)cptr->data; diff --git a/src/c/tests/TestOperations.cc b/src/c/tests/TestOperations.cc index b0370e9af05..27d92709bd8 100644 --- a/src/c/tests/TestOperations.cc +++ b/src/c/tests/TestOperations.cc @@ -29,6 +29,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture CPPUNIT_TEST_SUITE(Zookeeper_operations); #ifndef THREADED CPPUNIT_TEST(testPing); + CPPUNIT_TEST(testUnsolicitedPing); CPPUNIT_TEST(testTimeoutCausedByWatches1); CPPUNIT_TEST(testTimeoutCausedByWatches2); #else @@ -305,6 +306,40 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_); } + // ZOOKEEPER-2253: Permit unsolicited pings + void testUnsolicitedPing() + { + const int TIMEOUT=9; // timeout in secs + Mock_gettimeofday timeMock; + PingCountingServer zkServer; + // must call zookeeper_close() while all the mocks are in scope + CloseFinally guard(&zh); + + // receive timeout is in milliseconds + zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); + CPPUNIT_ASSERT(zh!=0); + // simulate connected state + forceConnected(zh); + + int fd=0; + int interest=0; + timeval tv; + + int rc=zookeeper_interest(zh,&fd,&interest,&tv); + CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); + + // verify no ping sent + CPPUNIT_ASSERT(zkServer.pingCount_==0); + + // we're going to receive a unsolicited PING response; ensure + // that the client has updated its last_recv timestamp + timeMock.tick(tv); + zkServer.addRecvResponse(new PingResponse); + rc=zookeeper_process(zh,interest); + CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); + CPPUNIT_ASSERT(timeMock==zh->last_recv); + } + // simulate a watch arriving right before a ping is due // assert the ping is sent nevertheless void testTimeoutCausedByWatches1() From 66e83f6729f3b2163c1824ebcbb1e2e55966122d Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Sat, 3 Oct 2015 21:04:11 +0000 Subject: [PATCH 305/444] ZOOKEEPER-2268: Zookeeper doc creation fails on windows (Arshad Mohammad via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1706630 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ build.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8b68898bda0..7818a734548 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -129,6 +129,9 @@ BUGFIXES: ZOOKEEPER-2253: C asserts ordering of ping requests, while Java client does not (Chris Chen via rgs) + ZOOKEEPER-2268: Zookeeper doc creation fails on windows + (Arshad Mohammad via cnauroth) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/build.xml b/build.xml index be64cac7a85..e80fd63828e 100644 --- a/build.xml +++ b/build.xml @@ -442,7 +442,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - From 3828141ff8c795d3a15e4bdb57d21e85d6846e39 Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Wed, 21 Oct 2015 22:12:15 +0000 Subject: [PATCH 306/444] ZOOKEEPER-2296: compilation broken for 3.4 (Raul Gutierrez Segales via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1709929 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7818a734548..9121c072ff4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -132,6 +132,9 @@ BUGFIXES: ZOOKEEPER-2268: Zookeeper doc creation fails on windows (Arshad Mohammad via cnauroth) + ZOOKEEPER-2296: compilation broken for 3.4 + (Raul Gutierrez Segales via cnauroth) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index 24514be1940..e577d327e41 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -2196,7 +2196,7 @@ int zookeeper_process(zhandle_t *zh, int events) struct timeval now; gettimeofday(&now, 0); elapsed = calculate_interval(&zh->last_ping, &now); - LOG_DEBUG(LOGCALLBACK(zh), "Got ping response in %d ms", elapsed); + LOG_DEBUG(("Got ping response in %d ms", elapsed)); free_buffer(bptr); } else if (hdr.xid == WATCHER_EVENT_XID) { struct WatcherEvent evt; @@ -2265,7 +2265,7 @@ int zookeeper_process(zhandle_t *zh, int events) activateWatcher(zh, cptr->watcher, rc); if (cptr->c.void_result != SYNCHRONOUS_MARKER) { - LOG_DEBUG(LOGCALLBACK(zh), "Queueing asynchronous response"); + LOG_DEBUG(("Queueing asynchronous response")); cptr->buffer = bptr; queue_completion(&zh->completions_to_process, cptr, 0); } else { From fd14f4a5661953fc4fa4e7844053f887f3bd3dda Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Fri, 30 Oct 2015 23:57:38 +0000 Subject: [PATCH 307/444] ZOOKEEPER-1029: C client bug in zookeeper_init (if bad hostname is given) (fpj via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1711566 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ src/c/src/mt_adaptor.c | 42 ++++++++++--------- src/c/src/st_adaptor.c | 30 ++++++++++---- src/c/src/zk_adaptor.h | 16 ++++---- src/c/src/zookeeper.c | 91 ++++++++++++++++++++++-------------------- 5 files changed, 105 insertions(+), 77 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9121c072ff4..81a5e100705 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -135,6 +135,9 @@ BUGFIXES: ZOOKEEPER-2296: compilation broken for 3.4 (Raul Gutierrez Segales via cnauroth) + ZOOKEEPER-1029: C client bug in zookeeper_init (if bad hostname is given) + (fpj via cnauroth) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/c/src/mt_adaptor.c b/src/c/src/mt_adaptor.c index 974063fda66..7dc78789f13 100644 --- a/src/c/src/mt_adaptor.c +++ b/src/c/src/mt_adaptor.c @@ -44,30 +44,30 @@ #include #endif -void zoo_lock_auth(zhandle_t *zh) +int zoo_lock_auth(zhandle_t *zh) { - pthread_mutex_lock(&zh->auth_h.lock); + return pthread_mutex_lock(&zh->auth_h.lock); } -void zoo_unlock_auth(zhandle_t *zh) +int zoo_unlock_auth(zhandle_t *zh) { - pthread_mutex_unlock(&zh->auth_h.lock); + return pthread_mutex_unlock(&zh->auth_h.lock); } -void lock_buffer_list(buffer_head_t *l) +int lock_buffer_list(buffer_head_t *l) { - pthread_mutex_lock(&l->lock); + return pthread_mutex_lock(&l->lock); } -void unlock_buffer_list(buffer_head_t *l) +int unlock_buffer_list(buffer_head_t *l) { - pthread_mutex_unlock(&l->lock); + return pthread_mutex_unlock(&l->lock); } -void lock_completion_list(completion_head_t *l) +int lock_completion_list(completion_head_t *l) { - pthread_mutex_lock(&l->lock); + return pthread_mutex_lock(&l->lock); } -void unlock_completion_list(completion_head_t *l) +int unlock_completion_list(completion_head_t *l) { pthread_cond_broadcast(&l->cond); - pthread_mutex_unlock(&l->lock); + return pthread_mutex_unlock(&l->lock); } struct sync_completion *alloc_sync_completion(void) { @@ -515,16 +515,22 @@ __attribute__((constructor)) int32_t get_xid() return fetch_and_add(&xid,1); } -void enter_critical(zhandle_t* zh) +int enter_critical(zhandle_t* zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; - if(adaptor) - pthread_mutex_lock(&adaptor->zh_lock); + if (adaptor) { + return pthread_mutex_lock(&adaptor->zh_lock); + } else { + return 0; + } } -void leave_critical(zhandle_t* zh) +int leave_critical(zhandle_t* zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; - if(adaptor) - pthread_mutex_unlock(&adaptor->zh_lock); + if (adaptor) { + return pthread_mutex_unlock(&adaptor->zh_lock); + } else { + return 0; + } } diff --git a/src/c/src/st_adaptor.c b/src/c/src/st_adaptor.c index 7609edf8c0b..23573b3aa90 100644 --- a/src/c/src/st_adaptor.c +++ b/src/c/src/st_adaptor.c @@ -24,23 +24,29 @@ #include #include -void zoo_lock_auth(zhandle_t *zh) +int zoo_lock_auth(zhandle_t *zh) { + return 0; } -void zoo_unlock_auth(zhandle_t *zh) +int zoo_unlock_auth(zhandle_t *zh) { + return 0; } -void lock_buffer_list(buffer_head_t *l) +int lock_buffer_list(buffer_head_t *l) { + return 0; } -void unlock_buffer_list(buffer_head_t *l) +int unlock_buffer_list(buffer_head_t *l) { + return 0; } -void lock_completion_list(completion_head_t *l) +int lock_completion_list(completion_head_t *l) { + return 0; } -void unlock_completion_list(completion_head_t *l) +int unlock_completion_list(completion_head_t *l) { + return 0; } struct sync_completion *alloc_sync_completion(void) { @@ -95,5 +101,13 @@ int32_t get_xid() } return xid++; } -void enter_critical(zhandle_t* zh){} -void leave_critical(zhandle_t* zh){} + +int enter_critical(zhandle_t* zh) +{ + return 0; +} + +int leave_critical(zhandle_t* zh) +{ + return 0; +} diff --git a/src/c/src/zk_adaptor.h b/src/c/src/zk_adaptor.h index 6aed38fab22..b99077963fc 100644 --- a/src/c/src/zk_adaptor.h +++ b/src/c/src/zk_adaptor.h @@ -75,10 +75,10 @@ typedef struct _completion_head { #endif } completion_head_t; -void lock_buffer_list(buffer_head_t *l); -void unlock_buffer_list(buffer_head_t *l); -void lock_completion_list(completion_head_t *l); -void unlock_completion_list(completion_head_t *l); +int lock_buffer_list(buffer_head_t *l); +int unlock_buffer_list(buffer_head_t *l); +int lock_completion_list(completion_head_t *l); +int unlock_completion_list(completion_head_t *l); struct sync_completion { int rc; @@ -243,12 +243,12 @@ void process_completions(zhandle_t *zh); int flush_send_queue(zhandle_t*zh, int timeout); char* sub_string(zhandle_t *zh, const char* server_path); void free_duplicate_path(const char* free_path, const char* path); -void zoo_lock_auth(zhandle_t *zh); -void zoo_unlock_auth(zhandle_t *zh); +int zoo_lock_auth(zhandle_t *zh); +int zoo_unlock_auth(zhandle_t *zh); // critical section guards -void enter_critical(zhandle_t* zh); -void leave_critical(zhandle_t* zh); +int enter_critical(zhandle_t* zh); +int leave_critical(zhandle_t* zh); // zhandle object reference counting void api_prolog(zhandle_t* zh); int api_epilog(zhandle_t *zh, int rc); diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index e577d327e41..f9e0ce470cc 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -1150,52 +1150,57 @@ void free_completions(zhandle_t *zh,int callCompletion,int reason) void_completion_t auth_completion = NULL; auth_completion_list_t a_list, *a_tmp; - lock_completion_list(&zh->sent_requests); - tmp_list = zh->sent_requests; - zh->sent_requests.head = 0; - zh->sent_requests.last = 0; - unlock_completion_list(&zh->sent_requests); - while (tmp_list.head) { - completion_list_t *cptr = tmp_list.head; + if (lock_completion_list(&zh->sent_requests) == 0) { + tmp_list = zh->sent_requests; + zh->sent_requests.head = 0; + zh->sent_requests.last = 0; + unlock_completion_list(&zh->sent_requests); + + while (tmp_list.head) { + completion_list_t *cptr = tmp_list.head; - tmp_list.head = cptr->next; - if (cptr->c.data_result == SYNCHRONOUS_MARKER) { - struct sync_completion - *sc = (struct sync_completion*)cptr->data; - sc->rc = reason; - notify_sync_completion(sc); - zh->outstanding_sync--; - destroy_completion_entry(cptr); - } else if (callCompletion) { - // Fake the response - buffer_list_t *bptr; - h.xid = cptr->xid; - h.zxid = -1; - h.err = reason; - oa = create_buffer_oarchive(); - serialize_ReplyHeader(oa, "header", &h); - bptr = calloc(sizeof(*bptr), 1); - assert(bptr); - bptr->len = get_buffer_len(oa); - bptr->buffer = get_buffer(oa); - close_buffer_oarchive(&oa, 0); - cptr->buffer = bptr; - queue_completion(&zh->completions_to_process, cptr, 0); + tmp_list.head = cptr->next; + if (cptr->c.data_result == SYNCHRONOUS_MARKER) { + struct sync_completion + *sc = (struct sync_completion*)cptr->data; + sc->rc = reason; + notify_sync_completion(sc); + zh->outstanding_sync--; + destroy_completion_entry(cptr); + } else if (callCompletion) { + // Fake the response + buffer_list_t *bptr; + h.xid = cptr->xid; + h.zxid = -1; + h.err = reason; + oa = create_buffer_oarchive(); + serialize_ReplyHeader(oa, "header", &h); + bptr = calloc(sizeof(*bptr), 1); + assert(bptr); + bptr->len = get_buffer_len(oa); + bptr->buffer = get_buffer(oa); + close_buffer_oarchive(&oa, 0); + cptr->buffer = bptr; + queue_completion(&zh->completions_to_process, cptr, 0); + } } } - a_list.completion = NULL; - a_list.next = NULL; - zoo_lock_auth(zh); - get_auth_completions(&zh->auth_h, &a_list); - zoo_unlock_auth(zh); - a_tmp = &a_list; - // chain call user's completion function - while (a_tmp->completion != NULL) { - auth_completion = a_tmp->completion; - auth_completion(reason, a_tmp->auth_data); - a_tmp = a_tmp->next; - if (a_tmp == NULL) - break; + if (zoo_lock_auth(zh) == 0) { + a_list.completion = NULL; + a_list.next = NULL; + + get_auth_completions(&zh->auth_h, &a_list); + zoo_unlock_auth(zh); + + a_tmp = &a_list; + // chain call user's completion function + while (a_tmp->completion != NULL) { + auth_completion = a_tmp->completion; + auth_completion(reason, a_tmp->auth_data); + a_tmp = a_tmp->next; + if (a_tmp == NULL) + break; + } } free_auth_completion(&a_list); } From 1ea4a2259333b858ce0dc236c7a4a7c5ea6dd0d6 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Sat, 31 Oct 2015 22:20:46 +0000 Subject: [PATCH 308/444] ZOOKEEPER-2142: JMX ObjectName is incorrect for observers (Edward Ribeiro via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1711696 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/server/ObserverBean.java | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 81a5e100705..32f82405501 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -138,6 +138,9 @@ BUGFIXES: ZOOKEEPER-1029: C client bug in zookeeper_init (if bad hostname is given) (fpj via cnauroth) + ZOOKEEPER-2142: JMX ObjectName is incorrect for observers (Edward Ribeiro + via michim) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/ObserverBean.java b/src/java/main/org/apache/zookeeper/server/ObserverBean.java index 4e0e82a8ea6..72d724e0b23 100644 --- a/src/java/main/org/apache/zookeeper/server/ObserverBean.java +++ b/src/java/main/org/apache/zookeeper/server/ObserverBean.java @@ -34,6 +34,10 @@ public ObserverBean(Observer observer, ZooKeeperServer zks) { this.observer = observer; } + public String getName() { + return "Observer"; + } + public int getPendingRevalidationCount() { return this.observer.getPendingRevalidationsCount(); } From 1d3f065c7fce7b09d7950cbb25e4f9f52cb9cd48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 8 Nov 2015 22:25:17 +0000 Subject: [PATCH 309/444] ZOOKEEPER-1853: zkCli.sh can't issue a CREATE command containing spaces in the data (Jun Gong via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1713308 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../org/apache/zookeeper/ZooKeeperMain.java | 26 +++++---- .../org/apache/zookeeper/ZooKeeperTest.java | 54 +++++++++++++++++++ 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 32f82405501..458d6a7d3cf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -141,6 +141,9 @@ BUGFIXES: ZOOKEEPER-2142: JMX ObjectName is incorrect for observers (Edward Ribeiro via michim) + ZOOKEEPER-1853: zkCli.sh can't issue a CREATE command containing + spaces in the data (Jun Gong via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index 39fc465a30d..baf1c494016 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -41,6 +41,8 @@ import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * The command line client to ZooKeeper. @@ -153,6 +155,8 @@ static class MyCommandOptions { private Map options = new HashMap(); private List cmdArgs = null; private String command = null; + public static final Pattern ARGS_PATTERN = Pattern.compile("\\s*([^\"\']\\S*|\"[^\"]*\"|'[^']*')\\s*"); + public static final Pattern QUOTED_PATTERN = Pattern.compile("^([\'\"])(.*)(\\1)$"); public MyCommandOptions() { options.put("server", "localhost:2181"); @@ -224,18 +228,22 @@ public boolean parseOptions(String[] args) { * @return true if parsing succeeded. */ public boolean parseCommand( String cmdstring ) { - StringTokenizer cmdTokens = new StringTokenizer(cmdstring, " "); - String[] args = new String[cmdTokens.countTokens()]; - int tokenIndex = 0; - while (cmdTokens.hasMoreTokens()) { - args[tokenIndex] = cmdTokens.nextToken(); - tokenIndex++; + Matcher matcher = ARGS_PATTERN.matcher(cmdstring); + + List args = new LinkedList(); + while (matcher.find()) { + String value = matcher.group(1); + if (QUOTED_PATTERN.matcher(value).matches()) { + // Strip off the surrounding quotes + value = value.substring(1, value.length() - 1); + } + args.add(value); } - if (args.length == 0){ + if (args.isEmpty()){ return false; } - command = args[0]; - cmdArgs = Arrays.asList(args); + command = args.get(0); + cmdArgs = args; return true; } } diff --git a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java index b438e30249a..d75a817e6be 100644 --- a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java +++ b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java @@ -201,4 +201,58 @@ public void testCliCommandsNotEchoingUsage() throws Exception { } } + @Test + public void testParseWithQuotes() throws Exception { + final ZooKeeper zk = createClient(); + ZooKeeperMain zkMain = new ZooKeeperMain(zk); + for (String quoteChar : new String[] {"'", "\""}) { + String cmdstring = String.format("create /node %1$squoted data%1$s", quoteChar); + zkMain.cl.parseCommand(cmdstring); + Assert.assertEquals("quotes combine arguments", zkMain.cl.getNumArguments(), 3); + Assert.assertEquals("create is not taken as first argument", zkMain.cl.getCmdArgument(0), "create"); + Assert.assertEquals("/node is not taken as second argument", zkMain.cl.getCmdArgument(1), "/node"); + Assert.assertEquals("quoted data is not taken as third argument", zkMain.cl.getCmdArgument(2), "quoted data"); + } + } + + @Test + public void testParseWithMixedQuotes() throws Exception { + final ZooKeeper zk = createClient(); + ZooKeeperMain zkMain = new ZooKeeperMain(zk); + for (String[] quoteChars : new String[][] {{"'", "\""}, {"\"", "'"}}) { + String outerQuotes = quoteChars[0]; + String innerQuotes = quoteChars[1]; + String cmdstring = String.format("create /node %1$s%2$squoted data%2$s%1$s", outerQuotes, innerQuotes); + zkMain.cl.parseCommand(cmdstring); + Assert.assertEquals("quotes combine arguments", zkMain.cl.getNumArguments(), 3); + Assert.assertEquals("create is not taken as first argument", zkMain.cl.getCmdArgument(0), "create"); + Assert.assertEquals("/node is not taken as second argument", zkMain.cl.getCmdArgument(1), "/node"); + Assert.assertEquals("quoted data is not taken as third argument", zkMain.cl.getCmdArgument(2), innerQuotes + "quoted data" + innerQuotes); + } + } + + @Test + public void testParseWithEmptyQuotes() throws Exception { + final ZooKeeper zk = createClient(); + ZooKeeperMain zkMain = new ZooKeeperMain(zk); + String cmdstring = "create /node ''"; + zkMain.cl.parseCommand(cmdstring); + Assert.assertEquals("empty quotes should produce arguments", zkMain.cl.getNumArguments(), 3); + Assert.assertEquals("create is not taken as first argument", zkMain.cl.getCmdArgument(0), "create"); + Assert.assertEquals("/node is not taken as second argument", zkMain.cl.getCmdArgument(1), "/node"); + Assert.assertEquals("empty string is not taken as third argument", zkMain.cl.getCmdArgument(2), ""); + } + + @Test + public void testParseWithMultipleQuotes() throws Exception { + final ZooKeeper zk = createClient(); + ZooKeeperMain zkMain = new ZooKeeperMain(zk); + String cmdstring = "create /node '' ''"; + zkMain.cl.parseCommand(cmdstring); + Assert.assertEquals("expected 5 arguments", zkMain.cl.getNumArguments(), 4); + Assert.assertEquals("create is not taken as first argument", zkMain.cl.getCmdArgument(0), "create"); + Assert.assertEquals("/node is not taken as second argument", zkMain.cl.getCmdArgument(1), "/node"); + Assert.assertEquals("empty string is not taken as third argument", zkMain.cl.getCmdArgument(2), ""); + Assert.assertEquals("empty string is not taken as fourth argument", zkMain.cl.getCmdArgument(3), ""); + } } From 815f5c394183617051e6626d52ff4edf80e5bd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 8 Nov 2015 22:30:06 +0000 Subject: [PATCH 310/444] ZOOKEEPER-2227: stmk four-letter word fails execution at server while reading trace mask argument (Chris Nauroth via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1713309 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 ++ .../zookeeper/server/NIOServerCnxn.java | 1 + .../zookeeper/server/NettyServerCnxn.java | 5 +- .../zookeeper/test/FourLetterWordsTest.java | 47 +++++++++++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 458d6a7d3cf..7736cc85133 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -144,6 +144,9 @@ BUGFIXES: ZOOKEEPER-1853: zkCli.sh can't issue a CREATE command containing spaces in the data (Jun Gong via rgs) + ZOOKEEPER-2227: stmk four-letter word fails execution at server while reading + trace mask argument (Chris Nauroth via rgs) + IMPROVEMENTS: ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index d832ad93f3b..0ed1c644d39 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -856,6 +856,7 @@ private boolean checkFourLetterWord(final SelectionKey k, final int len) tmask.start(); return true; } else if (len == setTraceMaskCmd) { + incomingBuffer = ByteBuffer.allocate(8); int rc = sock.read(incomingBuffer); if (rc < 0) { throw new IOException("Read error"); diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index cf43c8b22d8..35c85185a66 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -644,10 +644,9 @@ private boolean checkFourLetterWord(final Channel channel, tmask.start(); return true; } else if (len == setTraceMaskCmd) { - ByteBuffer mask = ByteBuffer.allocate(4); + ByteBuffer mask = ByteBuffer.allocate(8); message.readBytes(mask); - - bb.flip(); + mask.flip(); long traceMask = mask.getLong(); ZooTrace.setTextTraceLevel(traceMask); SetTraceMaskCommand setMask = new SetTraceMaskCommand(pwriter, traceMask); diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java index 0f81b65f4e4..029b7e83dd9 100644 --- a/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java @@ -19,15 +19,20 @@ package org.apache.zookeeper.test; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.IOException; import java.io.StringReader; import java.util.regex.Pattern; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.common.IOUtils; import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +40,9 @@ public class FourLetterWordsTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(FourLetterWordsTest.class); + @Rule + public Timeout timeout = new Timeout(30000); + /** Test the various four letter words */ @Test public void testFourLetterWords() throws Exception { @@ -187,4 +195,43 @@ public void testValidateSocketTimeout() throws Exception { String resp = sendRequest("isro", 2000); Assert.assertTrue(resp.contains("rw")); } + + @Test + public void testSetTraceMask() throws Exception { + String gtmkResp = sendRequest("gtmk"); + Assert.assertNotNull(gtmkResp); + gtmkResp = gtmkResp.trim(); + Assert.assertFalse(gtmkResp.isEmpty()); + long formerMask = Long.valueOf(gtmkResp); + try { + verify(buildSetTraceMaskRequest(0), "0"); + verify("gtmk", "0"); + } finally { + // Restore former value. + sendRequest(buildSetTraceMaskRequest(formerMask)); + } + } + + /** + * Builds a SetTraceMask request to be sent to the server, consisting of + * "stmk" followed by the 8-byte long representation of the trace mask. + * + * @param mask trace mask to set + * @return built request + * @throws IOException if there is an I/O error + */ + private String buildSetTraceMaskRequest(long mask) throws IOException { + ByteArrayOutputStream baos = null; + DataOutputStream dos = null; + try { + baos = new ByteArrayOutputStream(); + dos = new DataOutputStream(baos); + dos.writeBytes("stmk"); + dos.writeLong(mask); + } finally { + IOUtils.closeStream(dos); + IOUtils.closeStream(baos); + } + return new String(baos.toByteArray()); + } } From b9bce4e755259b8a3f440e06005956937725065f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 8 Nov 2015 23:11:34 +0000 Subject: [PATCH 311/444] ZOOKEEPER-2315: Change client connect zk service timeout log level from Info to Warn level (Lin Yiqun via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1713317 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ .../main/org/apache/zookeeper/ClientCnxn.java | 22 ++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7736cc85133..f25442fb3ef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -182,6 +182,9 @@ IMPROVEMENTS: ZOOKEEPER-2245: SimpleSysTest test cases fails (Arshad Mohammad via rakeshr) + ZOOKEEPER-2315: Change client connect zk service timeout log level from Info + to Warn level (Lin Yiqun via rgs) + NEW FEATURES: ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index d5575485246..08934b0f856 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -1099,11 +1099,14 @@ public void run() { } if (to <= 0) { - throw new SessionTimeoutException( - "Client session timed out, have not heard from server in " - + clientCnxnSocket.getIdleRecv() + "ms" - + " for sessionid 0x" - + Long.toHexString(sessionId)); + String warnInfo; + warnInfo = "Client session timed out, have not heard from server in " + + clientCnxnSocket.getIdleRecv() + + "ms" + + " for sessionid 0x" + + Long.toHexString(sessionId); + LOG.warn(warnInfo); + throw new SessionTimeoutException(warnInfo); } if (state.isConnected()) { //1000(1 second) is to prevent race condition missing to send the second ping @@ -1275,9 +1278,12 @@ void onConnected(int _negotiatedSessionTimeout, long _sessionId, Watcher.Event.EventType.None, Watcher.Event.KeeperState.Expired, null)); eventThread.queueEventOfDeath(); - throw new SessionExpiredException( - "Unable to reconnect to ZooKeeper service, session 0x" - + Long.toHexString(sessionId) + " has expired"); + + String warnInfo; + warnInfo = "Unable to reconnect to ZooKeeper service, session 0x" + + Long.toHexString(sessionId) + " has expired"; + LOG.warn(warnInfo); + throw new SessionExpiredException(warnInfo); } if (!readOnly && isRO) { LOG.error("Read/write client got connected to read-only server"); From 2b6e7ae859094d735816df9339285f3d59960353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 8 Nov 2015 23:26:15 +0000 Subject: [PATCH 312/444] ZOOKEEPER-2240: Make the three-node minimum more explicit in documentation and on website (Shawn Heisey via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1713322 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ docs/zookeeperAdmin.html | 21 +++++++++++++++++++++ docs/zookeeperStarted.html | 29 ++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f25442fb3ef..bdc54aa19b8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -185,6 +185,9 @@ IMPROVEMENTS: ZOOKEEPER-2315: Change client connect zk service timeout log level from Info to Warn level (Lin Yiqun via rgs) + ZOOKEEPER-2240: Make the three-node minimum more explicit in documentation + and on website (Shawn Heisey via rgs) + NEW FEATURES: ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html index 1059346ee0f..5fc74c8cd91 100644 --- a/docs/zookeeperAdmin.html +++ b/docs/zookeeperAdmin.html @@ -437,6 +437,27 @@

            Clustered (Multi-Server) Setup

            only handle the failure of a single machine; if two machines fail, the remaining two machines do not constitute a majority. However, with five machines ZooKeeper can handle the failure of two machines.

            + +
            +
            Note
            +
            +

            As mentioned in the Getting Started guide, a minimum of three servers are + required for a fault tolerant clustered setup, and it is strongly + recommended that you have an odd number of servers.

            +

            Usually three servers is more than enough for a production install, but + for maximum reliability during maintenance, you may wish to install + five servers. With three servers, if you perform maintenance on + one of them, you are vulnerable to a failure on one of the other + two servers during that maintenance. If you have five of them + running, you can take one down for maintenance, and know that + you're still OK if one of the other four suddenly fails.

            +

            Your redundancy considerations should include all aspects of your + environment. If you have three zookeeper servers, but their + network cables are all plugged into the same network switch, then + the failure of that switch will take down your entire ensemble.

            +
            +
            +

            Here are the steps to setting a server that will be part of an ensemble. These steps should be performed on every host in the ensemble:

            diff --git a/docs/zookeeperStarted.html b/docs/zookeeperStarted.html index 9e4f83d59f5..603faa5b06d 100644 --- a/docs/zookeeperStarted.html +++ b/docs/zookeeperStarted.html @@ -533,8 +533,23 @@

            Running Replicated ZooKeeper

            ZooKeeper in replicated mode. A replicated group of servers in the same application is called a quorum, and in replicated mode, all servers in the quorum have copies of the same configuration - file. The file is similar to the one used in standalone mode, but with a - few differences. Here is an example:

            + file.

            + +
            +
            Note
            +
            +

            For replicated mode, a minimum of three servers are required, and it is + strongly recommended that you have an odd number of servers. If you + only have two servers, then you are in a situation where if one of + them fails, there are not enough machines to form a majority quorum. + Two servers is inherently less stable than a single + server, because there are two single points of failure.

            +
            +
            + +

            The required conf/zoo.cfg file for replicated mode is + similar to the one used in standalone mode, but with a few differences. + Here is an example:

             tickTime=2000
             dataDir=/var/lib/zookeeper
            @@ -583,7 +598,15 @@ 

            Running Replicated ZooKeeper

            (in the above replicated example, running on a single localhost, you would still have three config files).

            - + +

            Please be aware that setting up multiple servers on a single machine + will not create any redundancy. If something were to happen + which caused the machine to die, all of the zookeeper servers + would be offline. Full redundancy requires that each server have + its own machine. It must be a completely separate physical server. + Multiple virtual machines on the same physical host are still + vulnerable to the complete failure of that host.

            + From 2bc121b89458601c48dbd35de853548e00cd44d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Mon, 9 Nov 2015 03:41:30 +0000 Subject: [PATCH 313/444] Preparing for release 3.4.7 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1713336 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + NOTICE.txt | 2 +- build.xml | 2 +- docs/releasenotes.html | 368 +++++++++++++++++++++++++++++- src/c/configure.ac | 2 +- src/c/include/winconfig.h | 6 +- src/c/include/zookeeper_version.h | 2 +- 7 files changed, 374 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bdc54aa19b8..56c87378a3b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +Release 3.4.7 - 2015-11-08 + Backward compatible changes: BUGFIXES: diff --git a/NOTICE.txt b/NOTICE.txt index 5689ab64643..fef4732a62b 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2014 The Apache Software Foundation +Copyright 2009-2015 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/build.xml b/build.xml index e80fd63828e..542aa779bb4 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 59cda186867..df8a3e6e785 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -5,7 +5,7 @@ -ZooKeeper 3.4.5 Release Notes +ZooKeeper 3.4.7 Release Notes @@ -202,11 +202,14 @@ PDF -icon
            PDF
            -

            ZooKeeper 3.4.6 Release Notes

            +

            ZooKeeper 3.4.7 Release Notes

            - + +

            Changes Since 3.4.6

            + +
            + + + +Changes Since ZooKeeper 3.4.6 + +

            Sub-task

            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            Changes Since ZooKeeper 3.4.6
            IssueNotes
            ZOOKEEPER-1866ClientBase#createClient is failing frequently
            ZOOKEEPER-1868Server not coming back up in QuorumZxidSyncTest
            ZOOKEEPER-1872QuorumPeer is not shutdown in few cases
            ZOOKEEPER-1904WatcherTest#testWatchAutoResetWithPending is failing
            ZOOKEEPER-1905ZKClients are hitting KeeperException$ConnectionLossException due to wrong usage pattern
            ZOOKEEPER-2047testTruncationNullLog fails on windows
            ZOOKEEPER-2237Port async multi to 3.4 branch
            +
            + +
            + + + +Changes Since ZooKeeper 3.4.6 + +

            Bug

            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            Changes Since ZooKeeper 3.4.6
            IssueNotes
            ZOOKEEPER-602log all exceptions not caught by ZK threads
            ZOOKEEPER-706large numbers of watches can cause session re-establishment to fail
            ZOOKEEPER-1002The Barrier sample code should create a EPHEMERAL znode instead of EPHEMERAL_SEQUENTIAL znode
            ZOOKEEPER-1029C client bug in zookeeper_init (if bad hostname is given)
            ZOOKEEPER-1062Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait
            ZOOKEEPER-1077C client lib doesn't build on Solaris
            ZOOKEEPER-1222getACL should only call DataTree.copyStat when passed in stat is not null
            ZOOKEEPER-1575adding .gitattributes to prevent CRLF and LF mismatches for source and text files
            ZOOKEEPER-1797PurgeTxnLog may delete data logs during roll
            ZOOKEEPER-1803Add description for pzxid in programmer's guide.
            ZOOKEEPER-1833fix windows build
            ZOOKEEPER-1853zkCli.sh can't issue a CREATE command containing spaces in the data
            ZOOKEEPER-1878Inconsistent behavior in autocreation of dataDir and dataLogDir
            ZOOKEEPER-1888ZkCli.cmd commands fail with "'java' is not recognized as an internal or external command"
            ZOOKEEPER-1895update all notice files, copyright, etc... with the new year - 2014
            ZOOKEEPER-1897ZK Shell/Cli not processing commands
            ZOOKEEPER-1900NullPointerException in truncate
            ZOOKEEPER-1901JDK8] Sort children for comparison in AsyncOps tests
            ZOOKEEPER-1906zkpython: invalid data in GetData for empty node
            ZOOKEEPER-1911REST contrib module does not include all required files when packaged
            ZOOKEEPER-1913Invalid manifest files due to bogus revision property value
            ZOOKEEPER-1917Apache Zookeeper logs cleartext admin passwords
            ZOOKEEPER-1926Unit tests should only use build/test/data for data
            ZOOKEEPER-1927zkServer.sh fails to read dataDir (and others) from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID).
            ZOOKEEPER-1939ReconfigRecoveryTest.testNextConfigUnreachable is failing
            ZOOKEEPER-1943quot;src/contrib/zooinspector/NOTICE.txt" isn't complying to ".gitattributes" in branch-3.4
            ZOOKEEPER-1945deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663
            ZOOKEEPER-1949recipes jar not included in the distribution package
            ZOOKEEPER-2026Startup order in ServerCnxnFactory-ies is wrong
            ZOOKEEPER-2033zookeeper follower fails to start after a restart immediately following a new epoch
            ZOOKEEPER-2039Jute compareBytes incorrect comparison index
            ZOOKEEPER-2049Yosemite build failure: htonll conflict
            ZOOKEEPER-2052Unable to delete a node when the node has no children
            ZOOKEEPER-2056Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant
            ZOOKEEPER-2060Trace bug in NettyServerCnxnFactory
            ZOOKEEPER-2064Prevent resource leak in various classes
            ZOOKEEPER-2073Memory leak on zookeeper_close
            ZOOKEEPER-2096C client builds with incorrect error codes in VisualStudio 2010+
            ZOOKEEPER-2114jute generated allocate_* functions are not externally visible
            ZOOKEEPER-2124Allow Zookeeper version string to have underscore '_'
            ZOOKEEPER-2142JMX ObjectName is incorrect for observers
            ZOOKEEPER-2146BinaryInputArchive readString should check length before allocating memory
            ZOOKEEPER-2174JUnit4ZKTestRunner logs test failure for all exceptions even if the test method is annotated with an expected exception.
            ZOOKEEPER-2186QuorumCnxManager#receiveConnection may crash with random input
            ZOOKEEPER-2201Network issues can cause cluster to hang due to near-deadlock
            ZOOKEEPER-2211PurgeTxnLog does not correctly purge when snapshots and logs are at different locations
            ZOOKEEPER-2213Empty path in Set crashes server and prevents restart
            ZOOKEEPER-2224Four letter command hangs when network is slow
            ZOOKEEPER-2227stmk four-letter word fails execution at server while reading trace mask argument.
            ZOOKEEPER-2235License update
            ZOOKEEPER-2239JMX State from LocalPeerBean incorrect
            ZOOKEEPER-2245SimpleSysTest test cases fails
            ZOOKEEPER-2256Zookeeper is not using specified JMX port in zkEnv.sh
            ZOOKEEPER-2268Zookeeper doc creation fails on windows
            ZOOKEEPER-2279QuorumPeer loadDataBase() error message is incorrect
            ZOOKEEPER-2296compilation broken for 3.4
            +
            + +
            + + + +Changes Since ZooKeeper 3.4.6 + +

            Improvement

            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            Changes Since ZooKeeper 3.4.6
            IssueNotes
            ZOOKEEPER-657Cut down the running time of ZKDatabase corruption.
            ZOOKEEPER-1402Upload Zookeeper package to Maven Central
            ZOOKEEPER-1506Re-try DNS hostname -> IP resolution if node connection fails
            ZOOKEEPER-1574mismatched CR/LF endings in text files
            ZOOKEEPER-1746AsyncCallback.*Callback don't have any Javadoc
            ZOOKEEPER-1907Improve Thread handling
            ZOOKEEPER-1948Enable JMX remote monitoring
            ZOOKEEPER-2040Server to log underlying cause of SASL connection problems
            ZOOKEEPER-2126Improve exit log messsage of EventThread and SendThread by adding SessionId
            ZOOKEEPER-2179Typo in Watcher.java
            ZOOKEEPER-2194Let DataNode.getChildren() return an unmodifiable view of its children set
            ZOOKEEPER-2205Log type of unexpected quorum packet in learner handler loop
            ZOOKEEPER-2240Make the three-node minimum more explicit in documentation and on website
            ZOOKEEPER-2315Change client connect zk service timeout log level from Info to Warn level
            +
            +

            Changes Since 3.4.5

            diff --git a/src/c/configure.ac b/src/c/configure.ac index b980ac06c37..f1bc7161a6c 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.6,[user@zookeeper.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.7,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index d3c80625a08..cc923965402 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -117,7 +117,7 @@ #define PACKAGE_NAME "zookeeper C client" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "zookeeper C client 3.4.0 win32" +#define PACKAGE_STRING "zookeeper C client 3.4.7 win32" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "c-client-src" @@ -126,7 +126,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.4.0" +#define PACKAGE_VERSION "3.4.7" /* poll() second argument type */ #define POLL_NFDS_TYPE @@ -138,7 +138,7 @@ #define TIME_WITH_SYS_TIME /* Version number of package */ -#define VERSION "3.4.0" +#define VERSION "3.4.7" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 3e1c636849e..0e67e0f4c03 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 6 +#define ZOO_PATCH_VERSION 7 #ifdef __cplusplus } From a6c72648f460baea7d12992a75b37bab4c7b02b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 21 Nov 2015 19:10:31 +0000 Subject: [PATCH 314/444] Updating version in build.xml git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1715553 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 542aa779bb4..dfbbcacae01 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 01182bebab3372fb0946a2529c65a5c74ad4ba7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 21 Nov 2015 20:13:37 +0000 Subject: [PATCH 315/444] ZOOKEEPER-1929: std::length_error on update children (Charles Strahan via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1715564 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 7 +++++++ src/contrib/zkfuse/src/zkadapter.cc | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 56c87378a3b..da47088ecaf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,10 @@ +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-1929: std::length_error on update children + (Charles Strahan via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/contrib/zkfuse/src/zkadapter.cc b/src/contrib/zkfuse/src/zkadapter.cc index 886051d97ba..7dfb9078fc3 100644 --- a/src/contrib/zkfuse/src/zkadapter.cc +++ b/src/contrib/zkfuse/src/zkadapter.cc @@ -845,7 +845,10 @@ ZooKeeperAdapter::getNodeData(const string &path, string("Unable to get data of node ") + path, rc ); } else { - return string( buffer, buffer + len ); + if (len == -1) { + len = 0; + } + return string( buffer, len ); } } From e9e6db318ee52f042237fed3fead6beea6be5a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Fri, 4 Dec 2015 04:19:51 +0000 Subject: [PATCH 316/444] ZOOKEEPER-2211: PurgeTxnLog does not correctly purge when snapshots and logs are at different locations (Arshad Mohammad via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1717894 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../apache/zookeeper/server/PurgeTxnLog.java | 90 +++++++++++++++---- .../apache/zookeeper/server/PurgeTxnTest.java | 89 ++++++++++++++++++ 3 files changed, 163 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index da47088ecaf..ed6850e5beb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,9 @@ BUGFIXES: ZOOKEEPER-1929: std::length_error on update children (Charles Strahan via rgs) + ZOOKEEPER-2211: PurgeTxnLog does not correctly purge when snapshots and + logs are at different locations (Arshad Mohammad via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java index 86c78ba75bb..25b949aaf7e 100644 --- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java @@ -26,8 +26,6 @@ import java.util.Arrays; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.Util; @@ -40,14 +38,16 @@ * and the corresponding logs. */ public class PurgeTxnLog { - private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnLog.class); + + private static final String COUNT_ERR_MSG = "count should be greater than or equal to 3"; static void printUsage(){ + System.out.println("Usage:"); System.out.println("PurgeTxnLog dataLogDir [snapDir] -n count"); System.out.println("\tdataLogDir -- path to the txn log directory"); System.out.println("\tsnapDir -- path to the snapshot directory"); - System.out.println("\tcount -- the number of old snaps/logs you want to keep"); - System.exit(1); + System.out.println("\tcount -- the number of old snaps/logs you want " + + "to keep, value should be greater than or equal to 3"); } private static final String PREFIX_SNAPSHOT = "snapshot"; @@ -66,7 +66,7 @@ static void printUsage(){ */ public static void purge(File dataDir, File snapDir, int num) throws IOException { if (num < 3) { - throw new IllegalArgumentException("count should be greater than 3"); + throw new IllegalArgumentException(COUNT_ERR_MSG); } FileTxnSnapLog txnLog = new FileTxnSnapLog(dataDir, snapDir); @@ -119,22 +119,74 @@ public boolean accept(File f){ } /** - * @param args PurgeTxnLog dataLogDir - * dataLogDir -- txn log directory - * -n num (number of snapshots to keep) + * @param args dataLogDir [snapDir] -n count + * dataLogDir -- path to the txn log directory + * snapDir -- path to the snapshot directory + * count -- the number of old snaps/logs you want to keep, value should be greater than or equal to 3
            */ public static void main(String[] args) throws IOException { - if(args.length<3 || args.length>4) - printUsage(); - int i = 0; - File dataDir=new File(args[0]); - File snapDir=dataDir; - if(args.length==4){ - i++; - snapDir=new File(args[i]); + if (args.length < 3 || args.length > 4) { + printUsageThenExit(); + } + File dataDir = validateAndGetFile(args[0]); + File snapDir = dataDir; + int num = -1; + String countOption = ""; + if (args.length == 3) { + countOption = args[1]; + num = validateAndGetCount(args[2]); + } else { + snapDir = validateAndGetFile(args[1]); + countOption = args[2]; + num = validateAndGetCount(args[3]); + } + if (!"-n".equals(countOption)) { + printUsageThenExit(); } - i++; i++; - int num = Integer.parseInt(args[i]); purge(dataDir, snapDir, num); } + + /** + * validates file existence and returns the file + * + * @param path + * @return File + */ + private static File validateAndGetFile(String path) { + File file = new File(path); + if (!file.exists()) { + System.err.println("Path '" + file.getAbsolutePath() + + "' does not exist. "); + printUsageThenExit(); + } + return file; + } + + /** + * Returns integer if parsed successfully and it is valid otherwise prints + * error and usage and then exits + * + * @param number + * @return + */ + private static int validateAndGetCount(String number) { + int result = 0; + try { + result = Integer.parseInt(number); + if (result < 3) { + System.err.println(COUNT_ERR_MSG); + printUsageThenExit(); + } + } catch (NumberFormatException e) { + System.err + .println("'" + number + "' can not be parsed to integer."); + printUsageThenExit(); + } + return result; + } + + private static void printUsageThenExit() { + printUsage(); + System.exit(1); + } } diff --git a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java index 4a685ac93b0..3e00e0091ed 100644 --- a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java +++ b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java @@ -18,6 +18,8 @@ package org.apache.zookeeper.server; +import static org.junit.Assert.assertEquals; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -295,6 +297,93 @@ public void testSnapFilesLessThanToRetain() throws Exception { verifyFilesAfterPurge(logs, true); } + /** + * PurgeTxnLog is called with dataLogDir snapDir -n count This test case + * verify these values are parsed properly and functionality works fine + */ + @Test + public void testPurgeTxnLogWithDataDir() + throws Exception { + tmpDir = ClientBase.createTmpDir(); + File dataDir = new File(tmpDir, "dataDir"); + File dataLogDir = new File(tmpDir, "dataLogDir"); + + File dataDirVersion2 = new File(dataDir, "version-2"); + dataDirVersion2.mkdirs(); + File dataLogDirVersion2 = new File(dataLogDir, "version-2"); + dataLogDirVersion2.mkdirs(); + + // create dummy log and transaction file + int totalFiles = 20; + + // create transaction and snapshot files in different-different + // directories + for (int i = 0; i < totalFiles; i++) { + // simulate log file + File logFile = new File(dataLogDirVersion2, "log." + + Long.toHexString(i)); + logFile.createNewFile(); + // simulate snapshot file + File snapFile = new File(dataDirVersion2, "snapshot." + + Long.toHexString(i)); + snapFile.createNewFile(); + } + + int numberOfFilesToKeep = 10; + // scenario where four parameter are passed + String[] args = new String[] { dataLogDir.getAbsolutePath(), + dataDir.getAbsolutePath(), "-n", + Integer.toString(numberOfFilesToKeep) }; + PurgeTxnLog.main(args); + + assertEquals(numberOfFilesToKeep, dataDirVersion2.listFiles().length); + assertEquals(numberOfFilesToKeep, dataLogDirVersion2.listFiles().length); + ClientBase.recursiveDelete(tmpDir); + + } + + /** + * PurgeTxnLog is called with dataLogDir -n count This test case verify + * these values are parsed properly and functionality works fine + */ + @Test + public void testPurgeTxnLogWithoutDataDir() + throws Exception { + tmpDir = ClientBase.createTmpDir(); + File dataDir = new File(tmpDir, "dataDir"); + File dataLogDir = new File(tmpDir, "dataLogDir"); + + File dataDirVersion2 = new File(dataDir, "version-2"); + dataDirVersion2.mkdirs(); + File dataLogDirVersion2 = new File(dataLogDir, "version-2"); + dataLogDirVersion2.mkdirs(); + + // create dummy log and transaction file + int totalFiles = 20; + + // create transaction and snapshot files in data directory + for (int i = 0; i < totalFiles; i++) { + // simulate log file + File logFile = new File(dataLogDirVersion2, "log." + + Long.toHexString(i)); + logFile.createNewFile(); + // simulate snapshot file + File snapFile = new File(dataLogDirVersion2, "snapshot." + + Long.toHexString(i)); + snapFile.createNewFile(); + } + + int numberOfFilesToKeep = 10; + // scenario where only three parameter are passed + String[] args = new String[] { dataLogDir.getAbsolutePath(), "-n", + Integer.toString(numberOfFilesToKeep) }; + PurgeTxnLog.main(args); + assertEquals(numberOfFilesToKeep + numberOfFilesToKeep, + dataLogDirVersion2.listFiles().length); + ClientBase.recursiveDelete(tmpDir); + + } + private void createDataDirFiles(AtomicInteger offset, int limit, File version_2, List snaps, List logs) throws IOException { From aa001687c348368336f7a053393344ecb0cff27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sun, 6 Dec 2015 19:32:55 +0000 Subject: [PATCH 317/444] ZOOKEEPER-2311: assert in setup_random (Marshall McMullen via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1718209 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/zookeeper.c | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ed6850e5beb..1c3a138a9bb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,9 @@ BUGFIXES: ZOOKEEPER-2211: PurgeTxnLog does not correctly purge when snapshots and logs are at different locations (Arshad Mohammad via rgs) + ZOOKEEPER-2311: assert in setup_random + (Marshall McMullen via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index f9e0ce470cc..1ba90afa2fc 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -424,8 +424,22 @@ static void setup_random() if (fd == -1) { seed = getpid(); } else { - int rc = read(fd, &seed, sizeof(seed)); - assert(rc == sizeof(seed)); + int seed_len = 0; + + /* Enter a loop to fill in seed with random data from /dev/urandom. + * This is done in a loop so that we can safely handle short reads + * which can happen due to signal interruptions. + */ + while (seed_len < sizeof(seed)) { + /* Assert we either read something or we were interrupted due to a + * signal (errno == EINTR) in which case we need to retry. + */ + int rc = read(fd, &seed + seed_len, sizeof(seed) - seed_len); + assert(rc > 0 || errno == EINTR); + if (rc > 0) { + seed_len += rc; + } + } close(fd); } srandom(seed); From 9aa90ac21bf04ca13e767e27c8f4c35538210839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Wed, 9 Dec 2015 04:10:37 +0000 Subject: [PATCH 318/444] ZOOKEEPER-2295: TGT refresh time logic is wrong (Arshad Mohammad via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1718761 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/java/main/org/apache/zookeeper/Login.java | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1c3a138a9bb..9070318d6de 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,9 @@ BUGFIXES: ZOOKEEPER-2311: assert in setup_random (Marshall McMullen via rgs) + ZOOKEEPER-2295: TGT refresh time logic is wrong + (Arshad Mohammad via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index 31ac39a2878..a214c9c9a0f 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -155,7 +155,6 @@ public void run() { if ((nextRefresh > expiry) || ((now + MIN_TIME_BEFORE_RELOGIN) > expiry)) { // expiry is before next scheduled refresh). - LOG.info("refreshing now because expiry is before next scheduled refresh time."); nextRefresh = now; } else { if (nextRefresh < (now + MIN_TIME_BEFORE_RELOGIN)) { @@ -176,7 +175,9 @@ public void run() { return; } } - if (now < nextRefresh) { + if (now == nextRefresh) { + LOG.info("refreshing now because expiry is before next scheduled refresh time."); + } else if (now < nextRefresh) { Date until = new Date(nextRefresh); LOG.info("TGT refresh sleeping until: " + until.toString()); try { @@ -191,7 +192,7 @@ public void run() { + " clock sync between this host and KDC - (KDC's clock is likely ahead of this host)." + " Manual intervention will be required for this client to successfully authenticate." + " Exiting refresh thread."); - return; + break; } if (isUsingTicketCache) { String cmd = "/usr/bin/kinit"; From 0172a4f326575999c08957659464083811faee35 Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Wed, 9 Dec 2015 23:26:45 +0000 Subject: [PATCH 319/444] ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME path (Neha Bathra via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1718985 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ bin/zkEnv.cmd | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9070318d6de..e0f10b1d68b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -150,6 +150,9 @@ BUGFIXES: ZOOKEEPER-2268: Zookeeper doc creation fails on windows (Arshad Mohammad via cnauroth) + ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME + path (Neha Bathra via cnauroth) + ZOOKEEPER-2296: compilation broken for 3.4 (Raul Gutierrez Segales via cnauroth) diff --git a/bin/zkEnv.cmd b/bin/zkEnv.cmd index 0816957fd40..41eed11ed5e 100644 --- a/bin/zkEnv.cmd +++ b/bin/zkEnv.cmd @@ -39,9 +39,11 @@ if not defined JAVA_HOME ( goto :eof ) -if not exist %JAVA_HOME%\bin\java.exe ( +set JAVA_HOME=%JAVA_HOME:"=% + +if not exist "%JAVA_HOME%"\bin\java.exe ( echo Error: JAVA_HOME is incorrectly set. goto :eof ) -set JAVA=%JAVA_HOME%\bin\java \ No newline at end of file +set JAVA="%JAVA_HOME%"\bin\java From 8c50ba620c81037d9a342e4f0fc8a4ad77300b3c Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Wed, 9 Dec 2015 23:28:31 +0000 Subject: [PATCH 320/444] ZOOKEEPER-2281: Move to correct section of CHANGES.txt. git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1718986 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e0f10b1d68b..796405760b0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,9 @@ BUGFIXES: ZOOKEEPER-2295: TGT refresh time logic is wrong (Arshad Mohammad via rgs) + ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME + path (Neha Bathra via cnauroth) + Release 3.4.7 - 2015-11-08 Backward compatible changes: @@ -150,9 +153,6 @@ BUGFIXES: ZOOKEEPER-2268: Zookeeper doc creation fails on windows (Arshad Mohammad via cnauroth) - ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME - path (Neha Bathra via cnauroth) - ZOOKEEPER-2296: compilation broken for 3.4 (Raul Gutierrez Segales via cnauroth) From c5f17c0a3bf5b3883ab2130a83cf1c59ca65ee71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 10 Dec 2015 05:33:12 +0000 Subject: [PATCH 321/444] ZOOKEEPER-2340: JMX is disabled even if JMXDISABLE is false (Arshad Mohammad via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1719013 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ bin/zkServer.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 796405760b0..b1a5675a5ce 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -17,6 +17,9 @@ BUGFIXES: ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME path (Neha Bathra via cnauroth) + ZOOKEEPER-2340: JMX is disabled even if JMXDISABLE is false + (Arshad Mohammad via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/bin/zkServer.sh b/bin/zkServer.sh index fbe6b8f5e30..51dbe9f581e 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -43,7 +43,7 @@ then JMXLOCALONLY=false fi -if [ "x$JMXDISABLE" = "x" ] +if [ "x$JMXDISABLE" = "x" ] || [ "$JMXDISABLE" = 'false' ] then echo "ZooKeeper JMX enabled by default" >&2 if [ "x$JMXPORT" = "x" ] From 8c325676106482aa467715c19f5833c0314af646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 10 Dec 2015 06:16:18 +0000 Subject: [PATCH 322/444] ZOOKEEPER-2229: Several four-letter words are undocumented (Chris Nauroth via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1719015 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../content/xdocs/zookeeperAdmin.xml | 107 ++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index b1a5675a5ce..4a9dd92089e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -20,6 +20,9 @@ BUGFIXES: ZOOKEEPER-2340: JMX is disabled even if JMXDISABLE is false (Arshad Mohammad via rgs) + ZOOKEEPER-2229: Several four-letter words are undocumented + (Chris Nauroth via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 5aa14eb0c5a..98d265e77f3 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1042,6 +1042,113 @@ server.3=zoo3:2888:3888 connection. + + + isro + + + New in 3.4.0: Tests if + server is running in read-only mode. The server will respond with + "ro" if in read-only mode or "rw" if not in read-only mode. + + + + + gtmk + + + Gets the current trace mask as a 64-bit signed long value in + decimal format. See stmk for an explanation of + the possible values. + + + + + stmk + + + Sets the current trace mask. The trace mask is 64 bits, + where each bit enables or disables a specific category of trace + logging on the server. Log4J must be configured to enable + TRACE level first in order to see trace logging + messages. The bits of the trace mask correspond to the following + trace logging categories. + + + Trace Mask Bit Values + + + + 0b0000000000 + Unused, reserved for future use. + + + 0b0000000010 + Logs client requests, excluding ping + requests. + + + 0b0000000100 + Unused, reserved for future use. + + + 0b0000001000 + Logs client ping requests. + + + 0b0000010000 + Logs packets received from the quorum peer that is + the current leader, excluding ping requests. + + + 0b0000100000 + Logs addition, removal and validation of client + sessions. + + + 0b0001000000 + Logs delivery of watch events to client + sessions. + + + 0b0010000000 + Logs ping packets received from the quorum peer + that is the current leader. + + + 0b0100000000 + Unused, reserved for future use. + + + 0b1000000000 + Unused, reserved for future use. + + + +
            + + All remaining bits in the 64-bit value are unused and + reserved for future use. Multiple trace logging categories are + specified by calculating the bitwise OR of the documented values. + The default trace mask is 0b0100110010. Thus, by default, trace + logging includes client requests, packets received from the + leader and sessions. + + To set a different trace mask, send a request containing the + stmk four-letter word followed by the trace + mask represented as a 64-bit signed long value. This example uses + the Perl pack function to construct a trace + mask that enables all trace logging categories described above and + convert it to a 64-bit signed long value with big-endian byte + order. The result is appended to stmk and sent + to the server using netcat. The server responds with the new + trace mask in decimal format. + + $ perl -e "print 'stmk', pack('q>', 0b0011111010)" | nc localhost 2181 +250 + +
            +
            From 09cd5db55446a4b390f82e3548b929f19e33430d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Thu, 14 Jan 2016 05:21:41 +0000 Subject: [PATCH 323/444] ZOOKEEPER-2347: Deadlock shutting down zookeeper (Rakesh R via rgs) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1724535 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + .../zookeeper/server/ZooKeeperServer.java | 28 +-- .../server/ZooKeeperServerMainTest.java | 196 +++++++++++++++++- 3 files changed, 213 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4a9dd92089e..5b75cbd10b3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -23,6 +23,9 @@ BUGFIXES: ZOOKEEPER-2229: Several four-letter words are undocumented (Chris Nauroth via rgs) + ZOOKEEPER-2347: Deadlock shutting down zookeeper + (Rakesh R via rgs) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index c03c9e0ff0c..da8b64a64d0 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -30,6 +30,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import javax.security.sasl.SaslException; @@ -107,7 +109,7 @@ public DataTree build() { protected SessionTracker sessionTracker; private FileTxnSnapLog txnLogFactory = null; private ZKDatabase zkDb; - protected long hzxid = 0; + private final AtomicLong hzxid = new AtomicLong(0); public final static Exception ok = new Exception("No prob"); protected RequestProcessor firstProcessor; protected volatile State state = State.INITIAL; @@ -122,7 +124,7 @@ enum State { */ static final private long superSecret = 0XB3415C00L; - int requestsInProcess; + private final AtomicInteger requestsInProcess = new AtomicInteger(0); final List outstandingChanges = new ArrayList(); // this data structure must be accessed under the outstandingChanges lock final HashMap outstandingChangesForPath = @@ -308,16 +310,16 @@ public void takeSnapshot(){ /** * This should be called from a synchronized block on this! */ - synchronized public long getZxid() { - return hzxid; + public long getZxid() { + return hzxid.get(); } - synchronized long getNextZxid() { - return ++hzxid; + long getNextZxid() { + return hzxid.incrementAndGet(); } - synchronized public void setZxid(long zxid) { - hzxid = zxid; + public void setZxid(long zxid) { + hzxid.set(zxid); } long getTime() { @@ -504,16 +506,16 @@ protected void unregisterJMX() { jmxDataTreeBean = null; } - synchronized public void incInProcess() { - requestsInProcess++; + public void incInProcess() { + requestsInProcess.incrementAndGet(); } - synchronized public void decInProcess() { - requestsInProcess--; + public void decInProcess() { + requestsInProcess.decrementAndGet(); } public int getInProcess() { - return requestsInProcess; + return requestsInProcess.get(); } /** diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java index c31b487aa05..8efbd02003a 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java @@ -23,17 +23,29 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; -import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.proto.ReplyHeader; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.persistence.Util; +import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.txn.SetDataTxn; +import org.apache.zookeeper.txn.TxnHeader; +import org.jboss.netty.channel.Channel; import org.junit.Assert; import org.junit.Test; @@ -238,6 +250,188 @@ public void testJMXRegistrationWithNetty() throws Exception { } } + /** + * Test case to verify that ZooKeeper server is able to shutdown properly + * when there are pending request(s) in the RequestProcessor chain. + * + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2347} + */ + @Test(timeout = 30000) + public void testRaceBetweenSyncFlushAndZKShutdown() throws Exception { + File tmpDir = ClientBase.createTmpDir(); + File testDir = File.createTempFile("test", ".dir", tmpDir); + testDir.delete(); + + // Following are the sequence of steps to simulate the deadlock + // situation - SyncRequestProcessor#shutdown holds a lock and waits on + // FinalRequestProcessor to complete a pending operation, which in turn + // also needs the ZooKeeperServer lock + + // 1. start zk server + FileTxnSnapLog ftsl = new FileTxnSnapLog(testDir, testDir); + final SimpleZooKeeperServer zkServer = new SimpleZooKeeperServer(ftsl); + zkServer.startup(); + // 2. Wait for setting up request processor chain. At the end of setup, + // it will add a mock request into the chain + // 3. Also, waiting for FinalRequestProcessor to start processing request + zkServer.waitForFinalProcessRequest(); + // 4. Above step ensures that there is a request in the processor chain. + // Now invoke shutdown, which will acquire zks lock + Thread shutdownThread = new Thread() { + public void run() { + zkServer.shutdown(); + }; + }; + shutdownThread.start(); + // 5. Wait for SyncRequestProcessor to trigger shutdown function. + // This is to ensure that zks lock is acquired + zkServer.waitForSyncReqProcessorShutdown(); + // 6. Now resume FinalRequestProcessor which in turn call + // zks#decInProcess() function and tries to acquire zks lock. + // This results in deadlock + zkServer.resumeFinalProcessRequest(); + // 7. Waiting to finish server shutdown. Testing that + // SyncRequestProcessor#shutdown holds a lock and waits on + // FinalRequestProcessor to complete a pending operation, which in turn + // also needs the ZooKeeperServer lock + shutdownThread.join(); + } + + private class SimpleZooKeeperServer extends ZooKeeperServer { + private SimpleSyncRequestProcessor syncProcessor; + private SimpleFinalRequestProcessor finalProcessor; + + SimpleZooKeeperServer(FileTxnSnapLog ftsl) throws IOException { + super(ftsl, 2000, 2000, 4000, null, new ZKDatabase(ftsl)); + } + + @Override + protected void setupRequestProcessors() { + finalProcessor = new SimpleFinalRequestProcessor(this); + syncProcessor = new SimpleSyncRequestProcessor(this, + finalProcessor); + syncProcessor.start(); + firstProcessor = new PrepRequestProcessor(this, syncProcessor); + ((PrepRequestProcessor) firstProcessor).start(); + + // add request to the chain + addRequestToSyncProcessor(); + } + + private void addRequestToSyncProcessor() { + long zxid = ZxidUtils.makeZxid(3, 7); + TxnHeader hdr = new TxnHeader(1, 1, zxid, 1, + ZooDefs.OpCode.setData); + Record txn = new SetDataTxn("/foo" + zxid, new byte[0], 1); + byte[] buf; + try { + buf = Util.marshallTxnEntry(hdr, txn); + } catch (IOException e) { + LOG.error("IOException while adding request to SyncRequestProcessor", e); + Assert.fail("IOException while adding request to SyncRequestProcessor!"); + return; + } + NettyServerCnxnFactory factory = new NettyServerCnxnFactory(); + final MockNettyServerCnxn nettyCnxn = new MockNettyServerCnxn(null, + this, factory); + Request req = new Request(nettyCnxn, 1, 1, ZooDefs.OpCode.setData, + ByteBuffer.wrap(buf), null); + req.hdr = hdr; + req.txn = txn; + syncProcessor.processRequest(req); + } + + void waitForFinalProcessRequest() throws InterruptedException { + Assert.assertTrue("Waiting for FinalRequestProcessor to start processing request", + finalProcessor.waitForProcessRequestToBeCalled()); + } + + void waitForSyncReqProcessorShutdown() throws InterruptedException { + Assert.assertTrue("Waiting for SyncRequestProcessor to shut down", + syncProcessor.waitForShutdownToBeCalled()); + } + + void resumeFinalProcessRequest() throws InterruptedException { + finalProcessor.resumeProcessRequest(); + } + } + + private class MockNettyServerCnxn extends NettyServerCnxn { + public MockNettyServerCnxn(Channel channel, ZooKeeperServer zks, + NettyServerCnxnFactory factory) { + super(null, null, factory); + } + + @Override + protected synchronized void updateStatsForResponse(long cxid, long zxid, + String op, long start, long end) { + return; + } + + @Override + public synchronized void sendResponse(ReplyHeader h, Record r, + String tag) { + return; + } + } + + private class SimpleFinalRequestProcessor extends FinalRequestProcessor { + private CountDownLatch finalReqProcessCalled = new CountDownLatch(1); + private CountDownLatch resumeFinalReqProcess = new CountDownLatch(1); + private volatile boolean interrupted = false; + public SimpleFinalRequestProcessor(ZooKeeperServer zks) { + super(zks); + } + + @Override + public void processRequest(Request request) { + finalReqProcessCalled.countDown(); + try { + resumeFinalReqProcess.await(ClientBase.CONNECTION_TIMEOUT, + TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + LOG.error("Interrupted while waiting to process request", e); + interrupted = true; // Marked as interrupted + resumeFinalReqProcess.countDown(); + return; + } + super.processRequest(request); + } + + boolean waitForProcessRequestToBeCalled() throws InterruptedException { + return finalReqProcessCalled.await(ClientBase.CONNECTION_TIMEOUT, + TimeUnit.MILLISECONDS); + } + + void resumeProcessRequest() throws InterruptedException { + resumeFinalReqProcess.countDown(); + resumeFinalReqProcess.await(ClientBase.CONNECTION_TIMEOUT, + TimeUnit.MILLISECONDS); + Assert.assertFalse("Interrupted while waiting to process request", + interrupted); + } + } + + private class SimpleSyncRequestProcessor extends SyncRequestProcessor { + private final CountDownLatch shutdownCalled = new CountDownLatch(1); + + public SimpleSyncRequestProcessor(ZooKeeperServer zks, + RequestProcessor nextProcessor) { + super(zks, nextProcessor); + } + + @Override + public void shutdown() { + shutdownCalled.countDown(); + super.shutdown(); + } + + boolean waitForShutdownToBeCalled() throws InterruptedException { + return shutdownCalled.await(ClientBase.CONNECTION_TIMEOUT / 3, + TimeUnit.MILLISECONDS); + } + } + private void deleteFile(File f) throws IOException { if (f.isDirectory()) { for (File c : f.listFiles()) From 79c8a2180d5b6f5530f0c00b7fd0d29bcd68b629 Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Fri, 5 Feb 2016 00:32:06 +0000 Subject: [PATCH 324/444] ZOOKEEPER-2360: Update commons collections version used by tests/releaseaudit (phunt via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1728579 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ ivy.xml | 4 +++- src/contrib/zooinspector/ivy.xml | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5b75cbd10b3..f393906ec84 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,9 @@ BUGFIXES: ZOOKEEPER-2347: Deadlock shutting down zookeeper (Rakesh R via rgs) + ZOOKEEPER-2360: Update commons collections version used by tests/releaseaudit + (phunt via cnauroth) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/ivy.xml b/ivy.xml index 561832ae6d5..1cb39cce39b 100644 --- a/ivy.xml +++ b/ivy.xml @@ -60,6 +60,8 @@ conf="test->default"/> + @@ -71,7 +73,7 @@ + rev="3.2.2" conf="releaseaudit->default"/> diff --git a/src/contrib/zooinspector/ivy.xml b/src/contrib/zooinspector/ivy.xml index 096f05c3f83..bc02946ee6c 100644 --- a/src/contrib/zooinspector/ivy.xml +++ b/src/contrib/zooinspector/ivy.xml @@ -42,7 +42,7 @@ + rev="3.2.2" conf="releaseaudit->default"/> - \ No newline at end of file + From bb5c2a8625e4ba00dcb3f1e77f1449ecf78f7870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 6 Feb 2016 03:16:53 +0000 Subject: [PATCH 325/444] Preparing for release 3.4.8 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1728789 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + NOTICE.txt | 2 +- build.xml | 2 +- docs/releasenotes.html | 64 ++++++++++++++++++++++++++++++- src/c/configure.ac | 2 +- src/c/include/winconfig.h | 6 +-- src/c/include/zookeeper_version.h | 2 +- 7 files changed, 71 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f393906ec84..d952c0d5671 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +Release 3.4.8 - 2016-02-05 + Backward compatible changes: BUGFIXES: diff --git a/NOTICE.txt b/NOTICE.txt index fef4732a62b..5f43319d785 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2015 The Apache Software Foundation +Copyright 2009-2016 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/build.xml b/build.xml index dfbbcacae01..8d5573e5f5e 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/docs/releasenotes.html b/docs/releasenotes.html index df8a3e6e785..26aa542970b 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -5,7 +5,7 @@ -ZooKeeper 3.4.7 Release Notes +ZooKeeper 3.4.8 Release Notes @@ -202,11 +202,14 @@ PDF -icon
            PDF
            -

            ZooKeeper 3.4.7 Release Notes

            +

            ZooKeeper 3.4.8 Release Notes

            + +

            Changes Since 3.4.7

            + +
            + + + +Changes Since ZooKeeper 3.4.7 + +

            Bug

            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            Changes Since ZooKeeper 3.4.7
            IssueNotes
            ZOOKEEPER-1929std::length_error on update children
            ZOOKEEPER-2211PurgeTxnLog does not correctly purge when snapshots and logs are at different locations
            ZOOKEEPER-2229Several four-letter words are undocumented.
            ZOOKEEPER-2281ZK Server startup fails if there are spaces in the JAVA_HOME path
            ZOOKEEPER-2295TGT refresh time logic is wrong
            ZOOKEEPER-2311assert in setup_random
            ZOOKEEPER-2340JMX is disabled even if JMXDISABLE is false
            ZOOKEEPER-2347Deadlock shutting down zookeeper
            ZOOKEEPER-2360Update commons collections version used by tests/releaseaudit
            +
            + +

            Changes Since 3.4.6

            diff --git a/src/c/configure.ac b/src/c/configure.ac index f1bc7161a6c..83c665297eb 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.7,[user@zookeeper.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.8,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index cc923965402..06c377e9317 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -117,7 +117,7 @@ #define PACKAGE_NAME "zookeeper C client" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "zookeeper C client 3.4.7 win32" +#define PACKAGE_STRING "zookeeper C client 3.4.8 win32" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "c-client-src" @@ -126,7 +126,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.4.7" +#define PACKAGE_VERSION "3.4.8" /* poll() second argument type */ #define POLL_NFDS_TYPE @@ -138,7 +138,7 @@ #define TIME_WITH_SYS_TIME /* Version number of package */ -#define VERSION "3.4.7" +#define VERSION "3.4.8" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 0e67e0f4c03..92eee134eef 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 7 +#define ZOO_PATCH_VERSION 8 #ifdef __cplusplus } From c792ba9d6466f9d1834e0b04e26b9117deea22c0 Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Mon, 8 Feb 2016 21:32:52 +0000 Subject: [PATCH 326/444] ZOOKEEPER-2243: Supported platforms is completely out of date (cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1729261 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../content/xdocs/zookeeperAdmin.xml | 87 ++++++++++++++++--- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d952c0d5671..8dd1e7bdfe6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -31,6 +31,8 @@ BUGFIXES: ZOOKEEPER-2360: Update commons collections version used by tests/releaseaudit (phunt via cnauroth) + ZOOKEEPER-2243: Supported platforms is completely out of date (cnauroth) + Release 3.4.7 - 2015-11-08 Backward compatible changes: diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 98d265e77f3..2589d1adde3 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -74,29 +74,90 @@
            Supported Platforms + ZooKeeper consists of multiple components. Some components are + supported broadly, and other components are supported only on a smaller + set of platforms. + - GNU/Linux is supported as a development and production - platform for both server and client. - - - Sun Solaris is supported as a development and production - platform for both server and client. + Client is the Java client + library, used by applications to connect to a ZooKeeper ensemble. + - FreeBSD is supported as a development and production - platform for clients only. Java NIO selector support in - the FreeBSD JVM is broken. + Server is the Java server + that runs on the ZooKeeper ensemble nodes. - Win32 is supported as a development - platform only for both server and client. + Native Client is a client + implemented in C, similar to the Java client, used by applications + to connect to a ZooKeeper ensemble. - MacOSX is supported as a development - platform only for both server and client. + Contrib refers to multiple + optional add-on components. + + The following matrix describes the level of support committed for + running each component on different operating system platforms. + + + Support Matrix + + + + Operating System + Client + Server + Native Client + Contrib + + + + + GNU/Linux + Development and Production + Development and Production + Development and Production + Development and Production + + + Solaris + Development and Production + Development and Production + Not Supported + Not Supported + + + FreeBSD + Development and Production + Development and Production + Not Supported + Not Supported + + + Windows + Development and Production + Development and Production + Not Supported + Not Supported + + + Mac OS X + Development Only + Development Only + Not Supported + Not Supported + + + +
            + + For any operating system not explicitly mentioned as supported in + the matrix, components may or may not work. The ZooKeeper community + will fix obvious bugs that are reported for other platforms, but there + is no full support.
            From 97a20865b2a6f8e114cde17528c6c9937305971d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Guti=C3=A9rrez=20Segal=C3=A9s?= Date: Sat, 20 Feb 2016 20:37:20 +0000 Subject: [PATCH 327/444] Update version in build.xml git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1731453 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 8d5573e5f5e..0faaac72539 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 85b5118e18df8ea1f060e0f4b21d9bddb968d772 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Wed, 2 Mar 2016 06:48:42 +0000 Subject: [PATCH 328/444] ZOOKEEPER-2375: Prevent multiple initialization of login object in each ZooKeeperSaslClient instance (yuemeng via rakeshr) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1733223 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 8 ++++++++ .../zookeeper/client/ZooKeeperSaslClient.java | 18 +++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8dd1e7bdfe6..a83ff1e26c0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,11 @@ +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-2375: Prevent multiple initialization of login object in each + ZooKeeperSaslClient instance (yuemeng via rakeshr) + + Release 3.4.8 - 2016-02-05 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index dbc10801949..a91e0eceedc 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -214,17 +214,21 @@ public void processResult(int rc, String path, Object ctx, byte data[], Stat sta } } - synchronized private SaslClient createSaslClient(final String servicePrincipal, + private SaslClient createSaslClient(final String servicePrincipal, final String loginContext) throws LoginException { try { if (login == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("JAAS loginContext is: " + loginContext); + synchronized (ZooKeeperSaslClient.class) { + if (login == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("JAAS loginContext is: " + loginContext); + } + // note that the login object is static: it's shared amongst all zookeeper-related connections. + // in order to ensure the login is initialized only once, it must be synchronized the code snippet. + login = new Login(loginContext, new ClientCallbackHandler(null)); + login.startThreadIfNeeded(); + } } - // note that the login object is static: it's shared amongst all zookeeper-related connections. - // createSaslClient() must be declared synchronized so that login is initialized only once. - login = new Login(loginContext, new ClientCallbackHandler(null)); - login.startThreadIfNeeded(); } Subject subject = login.getSubject(); SaslClient saslClient; From ab196d19bd39a988e252d1af4be78d2709e148ca Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 3 Mar 2016 17:27:15 +0000 Subject: [PATCH 329/444] ZOOKEEPER-2294 Ant target generate-clover-reports is broken (charlie via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1733501 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.xml b/build.xml index 0faaac72539..047900be95a 100644 --- a/build.xml +++ b/build.xml @@ -1454,12 +1454,14 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + + + - + From f646e77e2f8fdcf38b8e6bd8e10a0361ad6e8616 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 3 Mar 2016 17:55:25 +0000 Subject: [PATCH 330/444] ZOOKEEPER-2378 upgrade ivy to recent version (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1733507 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 047900be95a..485ca249cad 100644 --- a/build.xml +++ b/build.xml @@ -105,7 +105,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From c070ad5dedce55909e23158b559acfea8e9e37d2 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 3 Mar 2016 17:57:32 +0000 Subject: [PATCH 331/444] ZOOKEEPER-2373 Licenses section missing from pom file (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1733510 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.xml b/build.xml index 485ca249cad..ab254b2c9ef 100644 --- a/build.xml +++ b/build.xml @@ -723,7 +723,8 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + pomfile="${dist.maven.dir}/${final.name}.pom" + templatefile="${basedir}/src/pom.template"> @@ -830,7 +831,8 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + pomfile="${dist.maven.dir}/${name}.pom" + templatefile="${basedir}/src/pom.template"> From 7f29a5eef594b7b9e27a84d91245db125320fff6 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 3 Mar 2016 21:17:05 +0000 Subject: [PATCH 332/444] ZOOKEEPER-2373 Licenses section missing from pom file - part 2 missed an added file (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1733527 13f79535-47bb-0310-9956-ffa450edef68 --- src/pom.template | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/pom.template diff --git a/src/pom.template b/src/pom.template new file mode 100644 index 00000000000..a02c0b3d235 --- /dev/null +++ b/src/pom.template @@ -0,0 +1,41 @@ +SKIP_LINE *************************************************************** +SKIP_LINE * Licensed to the Apache Software Foundation (ASF) under one +SKIP_LINE * or more contributor license agreements. See the NOTICE file +SKIP_LINE * distributed with this work for additional information +SKIP_LINE * regarding copyright ownership. The ASF licenses this file +SKIP_LINE * to you under the Apache License, Version 2.0 (the +SKIP_LINE * "License"); you may not use this file except in compliance +SKIP_LINE * with the License. You may obtain a copy of the License at +SKIP_LINE * +SKIP_LINE * http://www.apache.org/licenses/LICENSE-2.0 +SKIP_LINE * +SKIP_LINE * Unless required by applicable law or agreed to in writing, +SKIP_LINE * software distributed under the License is distributed on an +SKIP_LINE * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +SKIP_LINE * KIND, either express or implied. See the License for the +SKIP_LINE * specific language governing permissions and limitations +SKIP_LINE * under the License. +SKIP_LINE *************************************************************** + +${ivy.pom.license} +${ivy.pom.header} + + + 4.0.0 + ${ivy.pom.groupId} + ${ivy.pom.artifactId} + ${ivy.pom.packaging} + ${ivy.pom.version} + ${ivy.pom.name} + ${ivy.pom.description} + ${ivy.pom.url} + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + From 8baed1db3ccd28cc0e8137ec9301c54e4e268a77 Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Sat, 5 Mar 2016 00:05:52 +0000 Subject: [PATCH 333/444] ZOOKEEPER-2379: recent commit broke findbugs qabot check (rakeshr via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1733681 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 ++ .../main/org/apache/zookeeper/client/ZooKeeperSaslClient.java | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index a83ff1e26c0..f6568a499ba 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,8 @@ BUGFIXES: ZOOKEEPER-2375: Prevent multiple initialization of login object in each ZooKeeperSaslClient instance (yuemeng via rakeshr) + ZOOKEEPER-2379: recent commit broke findbugs qabot check + (rakeshr via cnauroth) Release 3.4.8 - 2016-02-05 diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index a91e0eceedc..21ef0fa2725 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -64,6 +64,7 @@ public class ZooKeeperSaslClient { public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.clientconfig"; public static final String ENABLE_CLIENT_SASL_KEY = "zookeeper.sasl.client"; public static final String ENABLE_CLIENT_SASL_DEFAULT = "true"; + private static volatile boolean initializedLogin = false; /** * Returns true if the SASL client is enabled. By default, the client @@ -217,7 +218,7 @@ public void processResult(int rc, String path, Object ctx, byte data[], Stat sta private SaslClient createSaslClient(final String servicePrincipal, final String loginContext) throws LoginException { try { - if (login == null) { + if (!initializedLogin) { synchronized (ZooKeeperSaslClient.class) { if (login == null) { if (LOG.isDebugEnabled()) { @@ -227,6 +228,7 @@ private SaslClient createSaslClient(final String servicePrincipal, // in order to ensure the login is initialized only once, it must be synchronized the code snippet. login = new Login(loginContext, new ClientCallbackHandler(null)); login.startThreadIfNeeded(); + initializedLogin = true; } } } From 42bb8d50ce03ff1340216d7e059a644ec88bdef5 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 11 Mar 2016 06:35:33 +0000 Subject: [PATCH 334/444] ZOOKEEPER-2133 zkperl: Segmentation fault if getting a node with null value (Botond Hejj via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1734500 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/contrib/zkperl/ZooKeeper.xs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f6568a499ba..cef4ef3b22a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,9 @@ BUGFIXES: ZOOKEEPER-2379: recent commit broke findbugs qabot check (rakeshr via cnauroth) + ZOOKEEPER-2133 zkperl: Segmentation fault if getting a node with + null value (Botond Hejj via phunt) + Release 3.4.8 - 2016-02-05 Backward compatible changes: diff --git a/src/contrib/zkperl/ZooKeeper.xs b/src/contrib/zkperl/ZooKeeper.xs index f65e076b2a3..4b6067b1024 100644 --- a/src/contrib/zkperl/ZooKeeper.xs +++ b/src/contrib/zkperl/ZooKeeper.xs @@ -1713,7 +1713,7 @@ zk_get(zkh, path, ...) old_watch, new_watch); } - if (ret == ZOK) { + if (ret == ZOK && buf_len != -1) { ST(0) = sv_newmortal(); #ifdef SV_HAS_TRAILING_NUL buf[buf_len] = '\0'; From 0fbe03bc7c1f7829a346ff9aaaccd96b2794d860 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Fri, 11 Mar 2016 06:48:30 +0000 Subject: [PATCH 335/444] Added some CHANGES details that I missed in recent commits. (phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1734503 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index cef4ef3b22a..4ee652fbab9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,13 @@ BUGFIXES: ZOOKEEPER-2379: recent commit broke findbugs qabot check (rakeshr via cnauroth) + ZOOKEEPER-2294 Ant target generate-clover-reports is broken + (charlie via phunt) + + ZOOKEEPER-2378 upgrade ivy to recent version (phunt) + + ZOOKEEPER-2373 Licenses section missing from pom file (phunt) + ZOOKEEPER-2133 zkperl: Segmentation fault if getting a node with null value (Botond Hejj via phunt) From a10188b89f7253e8a383ba986928854646375932 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Mon, 14 Mar 2016 06:59:34 +0000 Subject: [PATCH 336/444] ZOOKEEPER-2283 traceFile property is not used in the ZooKeeper, it should be removed from documentation (Arshad Mohammad via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1734883 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 + docs/bookkeeperConfig.pdf | Bin 13824 -> 13824 bytes docs/bookkeeperOverview.pdf | Bin 147596 -> 147596 bytes docs/bookkeeperProgrammer.pdf | Bin 24991 -> 24991 bytes docs/bookkeeperStarted.pdf | Bin 17130 -> 17130 bytes docs/bookkeeperStream.pdf | Bin 13215 -> 13215 bytes docs/index.pdf | Bin 13525 -> 13525 bytes docs/javaExample.pdf | Bin 33852 -> 33852 bytes docs/linkmap.pdf | Bin 12472 -> 12472 bytes docs/recipes.pdf | Bin 31072 -> 31072 bytes docs/releasenotes.html | 1256 +---------------- docs/releasenotes.pdf | Bin 118640 -> 118640 bytes docs/zookeeperAdmin.html | 288 +++- docs/zookeeperAdmin.pdf | Bin 74144 -> 77992 bytes docs/zookeeperHierarchicalQuorums.pdf | Bin 6660 -> 6660 bytes docs/zookeeperInternals.pdf | Bin 48872 -> 48872 bytes docs/zookeeperJMX.pdf | Bin 16498 -> 16498 bytes docs/zookeeperObservers.pdf | Bin 12884 -> 12884 bytes docs/zookeeperOver.pdf | Bin 302521 -> 302521 bytes docs/zookeeperProgrammers.html | 72 +- docs/zookeeperProgrammers.pdf | Bin 133888 -> 134856 bytes docs/zookeeperQuotas.pdf | Bin 11269 -> 11269 bytes docs/zookeeperStarted.html | 29 +- docs/zookeeperStarted.pdf | Bin 27581 -> 27581 bytes docs/zookeeperTutorial.pdf | Bin 30557 -> 30557 bytes .../content/xdocs/zookeeperAdmin.xml | 16 - 26 files changed, 345 insertions(+), 1319 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4ee652fbab9..7b0e5b3ddfa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,9 @@ BUGFIXES: ZOOKEEPER-2133 zkperl: Segmentation fault if getting a node with null value (Botond Hejj via phunt) + ZOOKEEPER-2283 traceFile property is not used in the ZooKeeper, it + should be removed from documentation (Arshad Mohammad via phunt) + Release 3.4.8 - 2016-02-05 Backward compatible changes: diff --git a/docs/bookkeeperConfig.pdf b/docs/bookkeeperConfig.pdf index d840c3f15c946c1d6a6d0665ae817916e35f6c3c..83ab5c3aee9e965a12310b91c713cccfd07e0450 100644 GIT binary patch delta 183 zcmZq3X~>x{k=x9`*wEO>*wn(nbmN@sJOXCA2FALE#vw+=R;CtK2Bw>vc_%Pph)!1K z*ToRroWSp>7H?_h>Sk%`YG&zZXy9bxV(Mh(>T2p{=wfEzYG7$#X=bNjLr_U9mz^C~ aaYx{k=xkNz`)$l%-Gz}WaFIcJOakLh6cI@<{^e=R>tO5h9;Yvc_%Pph)!1K z*ToRroWSp>7H@2B?r33ZXklvP?C5A<>SAbY;cRSbVq{`!W?*9J=xnE8Lr_U9mz^C~ aaY-M;GIKJq OQ?MbVWO}{>lPmxXVi=1krRj2NPm zIr()l1e@LX+uisXx4ZE(Ju`52cCvJG1R88==;~-@X>8``XyNSW>g49+>}F`=Eey;z&biJbV5VzetZQf-Vq|P(YGGwyzPXuq0wad# zWMzI`48hF_{0{=-EzI03ot&K=EeuR8P0bC>9W4zkOk4~tolSxEm|3{lDcBHH63b<0 c$5mXCSX5F`l$yq6WNd6|#-*z2>hHz{0DI~#LI3~& delta 185 zcmbP#m~sAL#t9R-jSUS9%ni+q%?&L!&biJbV61Csple_rVrXV%Y;I*}vALOd0wad# zWMzI`48hF_{0{=-EsdRwja*$_olIPfO%2UWjUAngUEM6r4NXi;UCo^w?G$VXDv9N? dv*Ri*Nh~UrS diff --git a/docs/bookkeeperStarted.pdf b/docs/bookkeeperStarted.pdf index c4489f96e45606eb03e452c492c9519dcd10a9ee..31f41e693f48324db30b7931feb6358f3e26f916 100644 GIT binary patch delta 161 zcmaFW%J{04al%AyGXrBoVlzw|7#UlcT38tvZ*JzDz=$C_ zS(#rKLvV8fzqYZnvzeo*iKU~7tGSt@xub=%lbMN=i>0BFsj-2Zg|mgLoq`P^C6muN G$p8R2k|;9( delta 161 zcmaFW%J{04al%AyV?zT2b3-#@b0fozbFT9U80#7u=o*-Z7@Aobn_C$fZf@qCz=$C_ zS(#rKLvV8fzqYZnqlvMFtEH2%k)x@zlewX(qotFxfvb~|lZlgwk%5JQoq`P^C6muN G$p8Q=@h9g1 diff --git a/docs/bookkeeperStream.pdf b/docs/bookkeeperStream.pdf index ca7b7ef8b6a4235657352db05bab25c27059276f..c25c939625f7ff6ad790679968b84c38edacad7f 100644 GIT binary patch delta 182 zcmbQAK0kdz7q^*#v7xb%v8jcD`NpXicm&LJ4UBaSjYEu#txPSf49qu|@=jpH5S=W_ zuZtnLIf(zga-6BNs|nCVXA>t=V^dR0QxjJsXH#=iLjzYAb8|yiI|UnaLP}z}?CiLT ZOA?DpDvDCmxD1Vqj10I`RbBnvxB!iMEW!W) delta 182 zcmbQAK0kdz7q_vYfq}W9nX$Q%!N#cjeSs9yK85wNe%{ze+Lv(U9 zzb=O0<{A9=RGiHmEzL{~U5y+qOw8RZOdTzO3@2k#7XvdR3kwTFV><;KLP{p@G?4)S DdjBY_ diff --git a/docs/javaExample.pdf b/docs/javaExample.pdf index ab9599ed6dc6c01c2fdec8b890ffc1fe11d46424..2359dbd51daf3d339c7c38caeb449c7a72d12b3a 100644 GIT binary patch delta 161 zcmdnf!L+A?X+k@JEvyX8Hs|wBV8jre z%+IfjA-LIxe`1NVftjP5i?g$#tFwi%rJK2#ftiJilcl4xlZ&~Fg_DV;oq`P^C6g6e FWB?y%C^i59 delta 161 zcmdnf!L+A?X+k@J&8-YAH|O(CV8jre z%+IfjA-LIxe`1NVv!$c6g^8o1lc9-)ldFNTv9YD4o1?3XiL;5Lv4OL*oq`P^C6g6e FWB?e)C;|Wg diff --git a/docs/linkmap.pdf b/docs/linkmap.pdf index f78d12e84d626c01889cd585ce8a76b8ca8c64ed..eb7375c595479c462836542836607129069e7c5b 100644 GIT binary patch delta 159 zcmdmyxFc~wKew5Iv7xb%v8lPK#l~4zc?8UK4UBaSjYEu#txV0WOf5Fo^G;yI5S=W~ zuZtnLIfnnQg0rKMtD&Q*qpPcvi?NZZrIERbo4J{hg{zsdrKPj8rIDS24Iw3y=NQNU E00qY<_y7O^ delta 159 zcmdmyxFc~wKew@=fq}W9nX$Q{<;Gc8c?67g4GnY+%tH*#tc=aA3@ta;^G;yI5S=W~ zuZtnLIfnnQg0qRStA#7jL`OqcM*}xQV^b$%GdBYlBSS+s3r9mIV><;KLP{pjF^~ZO D@KPtB diff --git a/docs/recipes.pdf b/docs/recipes.pdf index 177b8aaeaf3f256bde6c0e1bffdde5b1867bb73f..6297a8c5bb98567aa0cc89c000615bcded47090b 100644 GIT binary patch delta 185 zcmaFxiSfZF#t9R+%?ykUjg5>=EeuRI&c4PYV5VzetZQf-Vq|P(YGGwyy19{e0wad# zWJP{m48hHD{EIW=4Nc9Q3=J%d-AvqEO^l744J{3g%$*(GEL==XEeu@@>=bMWDv9N? dv*Ri*Nh~U -ZooKeeper 3.4.8 Release Notes +ZooKeeper 3.4.5 Release Notes @@ -202,1243 +202,50 @@ PDF -icon
            PDF
            -

            ZooKeeper 3.4.8 Release Notes

            +

            ZooKeeper 3.4.5 Release Notes

            - - - - - -

            -These release notes include new developer and user facing incompatibilities, features, and major improvements. -

            - - - - - -

            Changes Since 3.4.7

            - -
            - - - -Changes Since ZooKeeper 3.4.7 - -

            Bug

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.7
            IssueNotes
            ZOOKEEPER-1929std::length_error on update children
            ZOOKEEPER-2211PurgeTxnLog does not correctly purge when snapshots and logs are at different locations
            ZOOKEEPER-2229Several four-letter words are undocumented.
            ZOOKEEPER-2281ZK Server startup fails if there are spaces in the JAVA_HOME path
            ZOOKEEPER-2295TGT refresh time logic is wrong
            ZOOKEEPER-2311assert in setup_random
            ZOOKEEPER-2340JMX is disabled even if JMXDISABLE is false
            ZOOKEEPER-2347Deadlock shutting down zookeeper
            ZOOKEEPER-2360Update commons collections version used by tests/releaseaudit
            -
            - - - -

            Changes Since 3.4.6

            - -
            - - - -Changes Since ZooKeeper 3.4.6 - -

            Sub-task

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.6
            IssueNotes
            ZOOKEEPER-1866ClientBase#createClient is failing frequently
            ZOOKEEPER-1868Server not coming back up in QuorumZxidSyncTest
            ZOOKEEPER-1872QuorumPeer is not shutdown in few cases
            ZOOKEEPER-1904WatcherTest#testWatchAutoResetWithPending is failing
            ZOOKEEPER-1905ZKClients are hitting KeeperException$ConnectionLossException due to wrong usage pattern
            ZOOKEEPER-2047testTruncationNullLog fails on windows
            ZOOKEEPER-2237Port async multi to 3.4 branch
            -
            - -
            - - - -Changes Since ZooKeeper 3.4.6 - -

            Bug

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.6
            IssueNotes
            ZOOKEEPER-602log all exceptions not caught by ZK threads
            ZOOKEEPER-706large numbers of watches can cause session re-establishment to fail
            ZOOKEEPER-1002The Barrier sample code should create a EPHEMERAL znode instead of EPHEMERAL_SEQUENTIAL znode
            ZOOKEEPER-1029C client bug in zookeeper_init (if bad hostname is given)
            ZOOKEEPER-1062Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait
            ZOOKEEPER-1077C client lib doesn't build on Solaris
            ZOOKEEPER-1222getACL should only call DataTree.copyStat when passed in stat is not null
            ZOOKEEPER-1575adding .gitattributes to prevent CRLF and LF mismatches for source and text files
            ZOOKEEPER-1797PurgeTxnLog may delete data logs during roll
            ZOOKEEPER-1803Add description for pzxid in programmer's guide.
            ZOOKEEPER-1833fix windows build
            ZOOKEEPER-1853zkCli.sh can't issue a CREATE command containing spaces in the data
            ZOOKEEPER-1878Inconsistent behavior in autocreation of dataDir and dataLogDir
            ZOOKEEPER-1888ZkCli.cmd commands fail with "'java' is not recognized as an internal or external command"
            ZOOKEEPER-1895update all notice files, copyright, etc... with the new year - 2014
            ZOOKEEPER-1897ZK Shell/Cli not processing commands
            ZOOKEEPER-1900NullPointerException in truncate
            ZOOKEEPER-1901JDK8] Sort children for comparison in AsyncOps tests
            ZOOKEEPER-1906zkpython: invalid data in GetData for empty node
            ZOOKEEPER-1911REST contrib module does not include all required files when packaged
            ZOOKEEPER-1913Invalid manifest files due to bogus revision property value
            ZOOKEEPER-1917Apache Zookeeper logs cleartext admin passwords
            ZOOKEEPER-1926Unit tests should only use build/test/data for data
            ZOOKEEPER-1927zkServer.sh fails to read dataDir (and others) from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID).
            ZOOKEEPER-1939ReconfigRecoveryTest.testNextConfigUnreachable is failing
            ZOOKEEPER-1943quot;src/contrib/zooinspector/NOTICE.txt" isn't complying to ".gitattributes" in branch-3.4
            ZOOKEEPER-1945deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663
            ZOOKEEPER-1949recipes jar not included in the distribution package
            ZOOKEEPER-2026Startup order in ServerCnxnFactory-ies is wrong
            ZOOKEEPER-2033zookeeper follower fails to start after a restart immediately following a new epoch
            ZOOKEEPER-2039Jute compareBytes incorrect comparison index
            ZOOKEEPER-2049Yosemite build failure: htonll conflict
            ZOOKEEPER-2052Unable to delete a node when the node has no children
            ZOOKEEPER-2056Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant
            ZOOKEEPER-2060Trace bug in NettyServerCnxnFactory
            ZOOKEEPER-2064Prevent resource leak in various classes
            ZOOKEEPER-2073Memory leak on zookeeper_close
            ZOOKEEPER-2096C client builds with incorrect error codes in VisualStudio 2010+
            ZOOKEEPER-2114jute generated allocate_* functions are not externally visible
            ZOOKEEPER-2124Allow Zookeeper version string to have underscore '_'
            ZOOKEEPER-2142JMX ObjectName is incorrect for observers
            ZOOKEEPER-2146BinaryInputArchive readString should check length before allocating memory
            ZOOKEEPER-2174JUnit4ZKTestRunner logs test failure for all exceptions even if the test method is annotated with an expected exception.
            ZOOKEEPER-2186QuorumCnxManager#receiveConnection may crash with random input
            ZOOKEEPER-2201Network issues can cause cluster to hang due to near-deadlock
            ZOOKEEPER-2211PurgeTxnLog does not correctly purge when snapshots and logs are at different locations
            ZOOKEEPER-2213Empty path in Set crashes server and prevents restart
            ZOOKEEPER-2224Four letter command hangs when network is slow
            ZOOKEEPER-2227stmk four-letter word fails execution at server while reading trace mask argument.
            ZOOKEEPER-2235License update
            ZOOKEEPER-2239JMX State from LocalPeerBean incorrect
            ZOOKEEPER-2245SimpleSysTest test cases fails
            ZOOKEEPER-2256Zookeeper is not using specified JMX port in zkEnv.sh
            ZOOKEEPER-2268Zookeeper doc creation fails on windows
            ZOOKEEPER-2279QuorumPeer loadDataBase() error message is incorrect
            ZOOKEEPER-2296compilation broken for 3.4
            -
            - -
            - - - -Changes Since ZooKeeper 3.4.6 - -

            Improvement

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.6
            IssueNotes
            ZOOKEEPER-657Cut down the running time of ZKDatabase corruption.
            ZOOKEEPER-1402Upload Zookeeper package to Maven Central
            ZOOKEEPER-1506Re-try DNS hostname -> IP resolution if node connection fails
            ZOOKEEPER-1574mismatched CR/LF endings in text files
            ZOOKEEPER-1746AsyncCallback.*Callback don't have any Javadoc
            ZOOKEEPER-1907Improve Thread handling
            ZOOKEEPER-1948Enable JMX remote monitoring
            ZOOKEEPER-2040Server to log underlying cause of SASL connection problems
            ZOOKEEPER-2126Improve exit log messsage of EventThread and SendThread by adding SessionId
            ZOOKEEPER-2179Typo in Watcher.java
            ZOOKEEPER-2194Let DataNode.getChildren() return an unmodifiable view of its children set
            ZOOKEEPER-2205Log type of unexpected quorum packet in learner handler loop
            ZOOKEEPER-2240Make the three-node minimum more explicit in documentation and on website
            ZOOKEEPER-2315Change client connect zk service timeout log level from Info to Warn level
            -
            - - -

            Changes Since 3.4.5

            -
            - - - -Changes Since ZooKeeper 3.4.5 - - -

            Sub-task -

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.5
            IssueNotes
            ZOOKEEPER-1414 - -QuorumPeerMainTest.testQuorum, testBadPackets are failing intermittently -
            ZOOKEEPER-1459 - -Standalone ZooKeeperServer is not closing the transaction log files on shutdown -
            ZOOKEEPER-1558 - -Leader should not snapshot uncommitted state -
            ZOOKEEPER-1808 - Add version to FLE notifications for 3.4 branch -
            ZOOKEEPER-1817 - Fix don't care for b3.4 -
            ZOOKEEPER-1834 - Catch IOException in FileTxnLog -
            ZOOKEEPER-1837 - Fix JMXEnv checks (potential race conditions) -
            ZOOKEEPER-1838 - ZooKeeper shutdown hangs indefinitely at NioServerSocketChannelFactory.releaseExternalResources -
            ZOOKEEPER-1841 - problem in QuorumTest -
            ZOOKEEPER-1849 - Need to properly tear down tests in various cases -
            ZOOKEEPER-1852 - ServerCnxnFactory instance is not properly cleanedup -
            ZOOKEEPER-1854 - ClientBase ZooKeeper server clean-up -
            ZOOKEEPER-1857 - PrepRequestProcessotTest doesn't shutdown ZooKeeper server -
            ZOOKEEPER-1858 - JMX checks - potential race conditions while stopping and starting server -
            ZOOKEEPER-1867 - Bug in ZkDatabaseCorruptionTest -
            ZOOKEEPER-1873 - Unnecessarily InstanceNotFoundException is coming when unregister failed jmxbeans -
            -
            - - -
            - - - -Changes Since ZooKeeper 3.4.5 - -

            Bug -

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.5
            ZOOKEEPER-87 - Follower does not shut itself down if its too far behind the leader. -
            ZOOKEEPER-732 - Improper translation of error into Python exception -
            ZOOKEEPER-753 - update log4j dependency from 1.2.15 to 1.2.16 in branch 3.4 -
            ZOOKEEPER-877 - zkpython does not work with python3.1 -
            ZOOKEEPER-1057 - zookeeper c-client, connection to offline server fails to successfully fallback to second zk host -
            ZOOKEEPER-1179 - NettyServerCnxn does not properly close socket on 4 letter word requests -
            ZOOKEEPER-1238 - when the linger time was changed for NIO the patch missed Netty -
            ZOOKEEPER-1334 - Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF is flawed -
            ZOOKEEPER-1379 - 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary -
            ZOOKEEPER-1382 - Zookeeper server holds onto dead/expired session ids in the watch data structures -
            ZOOKEEPER-1387 - Wrong epoch file created -
            ZOOKEEPER-1388 - Client side 'PathValidation' is missing for the multi-transaction api. -
            ZOOKEEPER-1448 - Node+Quota creation in transaction log can crash leader startup -
            ZOOKEEPER-1462 - Read-only server does not initialize database properly -
            ZOOKEEPER-1474 - Cannot build Zookeeper with IBM Java: use of Sun MXBean classes -
            ZOOKEEPER-1478 - Small bug in QuorumTest.testFollowersStartAfterLeader( ) -
            ZOOKEEPER-1495 - ZK client hangs when using a function not available on the server. -
            ZOOKEEPER-1513 - "Unreasonable length" exception while starting a server. -
            ZOOKEEPER-1535 - ZK Shell/Cli re-executes last command on exit -
            ZOOKEEPER-1548 - Cluster fails election loop in new and interesting way -
            ZOOKEEPER-1551 - Observers ignore txns that come after snapshot and UPTODATE -
            ZOOKEEPER-1553 - Findbugs configuration is missing some dependencies -
            ZOOKEEPER-1554 - Can't use zookeeper client without SASL -
            ZOOKEEPER-1557 - jenkins jdk7 test failure in testBadSaslAuthNotifiesWatch -
            ZOOKEEPER-1562 - Memory leaks in zoo_multi API -
            ZOOKEEPER-1573 - Unable to load database due to missing parent node -
            ZOOKEEPER-1578 - org.apache.zookeeper.server.quorum.Zab1_0Test failed due to hard code with 33556 port -
            ZOOKEEPER-1581 - change copyright in notice to 2012 -
            ZOOKEEPER-1596 - Zab1_0Test should ensure that the file is closed -
            ZOOKEEPER-1597 - Windows build failing -
            ZOOKEEPER-1599 - 3.3 server cannot join 3.4 quorum -
            ZOOKEEPER-1603 - StaticHostProviderTest testUpdateClientMigrateOrNot hangs -
            ZOOKEEPER-1606 - intermittent failures in ZkDatabaseCorruptionTest on jenkins -
            ZOOKEEPER-1610 - Some classes are using == or != to compare Long/String objects instead of .equals() -
            ZOOKEEPER-1613 - The documentation still points to 2008 in the copyright notice -
            ZOOKEEPER-1622 - session ids will be negative in the year 2022 -
            ZOOKEEPER-1624 - PrepRequestProcessor abort multi-operation incorrectly -
            ZOOKEEPER-1629 - testTransactionLogCorruption occasionally fails -
            ZOOKEEPER-1632 - fix memory leaks in cli_st -
            ZOOKEEPER-1633 - Introduce a protocol version to connection initiation message -
            ZOOKEEPER-1642 - Leader loading database twice -
            ZOOKEEPER-1645 - ZooKeeper OSGi package imports not complete -
            ZOOKEEPER-1646 - mt c client tests fail on Ubuntu Raring -
            ZOOKEEPER-1647 - OSGi package import/export changes not applied to bin-jar -
            ZOOKEEPER-1648 - Fix WatcherTest in JDK7 -
            ZOOKEEPER-1653 - zookeeper fails to start because of inconsistent epoch -
            ZOOKEEPER-1657 - Increased CPU usage by unnecessary SASL checks -
            ZOOKEEPER-1663 - scripts don't work when path contains spaces -
            ZOOKEEPER-1667 - Watch event isn't handled correctly when a client reestablish to a server -
            ZOOKEEPER-1696 - Fail to run zookeeper client on Weblogic application server -
            ZOOKEEPER-1697 - large snapshots can cause continuous quorum failure -
            ZOOKEEPER-1702 - ZooKeeper client may write operation packets before receiving successful response to connection request, can cause TCP RST -
            ZOOKEEPER-1706 - Typo in Double Barriers example -
            ZOOKEEPER-1711 - ZooKeeper server binds to all ip addresses for leader election and broadcast -
            ZOOKEEPER-1713 - wrong time calculation in zkfuse.cc -
            ZOOKEEPER-1714 - perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used -
            ZOOKEEPER-1719 - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 -
            ZOOKEEPER-1731 - Unsynchronized access to ServerCnxnFactory.connectionBeans results in deadlock -
            ZOOKEEPER-1732 - ZooKeeper server unable to join established ensemble -
            ZOOKEEPER-1733 - FLETest#testLE is flaky on windows boxes -
            ZOOKEEPER-1744 - clientPortAddress breaks "zkServer.sh status" -
            ZOOKEEPER-1750 - Race condition producing NPE in NIOServerCnxn.toString -
            ZOOKEEPER-1751 - ClientCnxn#run could miss the second ping or connection get dropped before a ping -
            ZOOKEEPER-1753 - ClientCnxn is not properly releasing the resources, which are used to ping RwServer -
            ZOOKEEPER-1754 - Read-only server allows to create znode -
            ZOOKEEPER-1755 - Concurrent operations of four letter 'dump' ephemeral command and killSession causing NPE -
            ZOOKEEPER-1756 - zookeeper_interest() in C client can return a timeval of 0 -
            ZOOKEEPER-1764 - ZooKeeper attempts at SASL eventhough it shouldn't -
            ZOOKEEPER-1765 - Update code conventions link on "How to contribute" page -
            ZOOKEEPER-1770 - NullPointerException in SnapshotFormatter -
            ZOOKEEPER-1774 - QuorumPeerMainTest fails consistently with "complains about host" assertion failure -
            ZOOKEEPER-1775 - Ephemeral nodes not present in one of the members of the ensemble -
            ZOOKEEPER-1776 - Ephemeral nodes not present in one of the members of the ensemble -
            ZOOKEEPER-1781 - ZooKeeper Server fails if snapCount is set to 1 -
            ZOOKEEPER-1786 - ZooKeeper data model documentation is incorrect -
            ZOOKEEPER-1790 - Deal with special ObserverId in QuorumCnxManager.receiveConnection -
            ZOOKEEPER-1798 - Fix race condition in testNormalObserverRun -
            ZOOKEEPER-1799 - SaslAuthFailDesignatedClientTest.testAuth fails frequently on SUSE -
            ZOOKEEPER-1805 - "Don't care" value in ZooKeeper election breaks rolling upgrades -
            ZOOKEEPER-1811 - The ZooKeeperSaslClient service name principal is hardcoded to "zookeeper" -
            ZOOKEEPER-1812 - ZooInspector reconnection always fails if first connection fails -
            ZOOKEEPER-1821 - very ugly warning when compiling load_gen.c -
            ZOOKEEPER-1839 - Deadlock in NettyServerCnxn -
            ZOOKEEPER-1844 - TruncateTest fails on windows -
            ZOOKEEPER-1845 - FLETest.testLE fails on windows -
            ZOOKEEPER-1850 - cppunit test testNonexistingHost in TestZookeeperInit is failing on Unbuntu -
            +Changes Since ZooKeeper 3.4.2 + +
          • +Changes Since ZooKeeper 3.4.1 +
          • +
          • +Changes Since ZooKeeper 3.4.0 +
          • +
          • +Changes Since ZooKeeper 3.3.0 +
          • +
            - - -
            - - - -Changes Since ZooKeeper 3.4.5 - -

            Improvement -

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.5
            ZOOKEEPER-1019 - zkfuse doesn't list dependency on boost in README -
            ZOOKEEPER-1096 - Leader communication should listen on specified IP, not wildcard address -
            ZOOKEEPER-1324 - Remove Duplicate NEWLEADER packets from the Leader to the Follower. -
            ZOOKEEPER-1552 - Enable sync request processor in Observer -
            ZOOKEEPER-1564 - Allow JUnit test build with IBM Java -
            ZOOKEEPER-1583 - Document maxClientCnxns in conf/zoo_sample.cfg -
            ZOOKEEPER-1584 - Adding mvn-install target for deploying the zookeeper artifacts to .m2 repository. -
            ZOOKEEPER-1598 - Ability to support more digits in the version string -
            ZOOKEEPER-1615 - minor typos in ZooKeeper Programmer's Guide web page -
            ZOOKEEPER-1627 - Add org.apache.zookeeper.common to exported packages in OSGi MANIFEST headers -
            ZOOKEEPER-1666 - Avoid Reverse DNS lookup if the hostname in connection string is literal IP address. -
            ZOOKEEPER-1715 - Upgrade netty version -
            ZOOKEEPER-1758 - Add documentation for zookeeper.observer.syncEnabled flag -
            ZOOKEEPER-1771 - ZooInspector authentication -
            + - -
            - - + -Changes Since ZooKeeper 3.4.5 -

            Task -

            +

            +These release notes include new developer and user facing incompatibilities, features, and major improvements. +

            + - - - -
            Changes Since ZooKeeper 3.4.5
            ZOOKEEPER-1430 - add maven deploy support to the build -
            -
            +

            Changes Since 3.4.4

            @@ -5188,6 +3995,7 @@

            Changes Since ZooKeeper 3.3.0

            +
            @@ -5211,7 +4019,7 @@

            Changes Since ZooKeeper 3.3.0

            + + + + + + . + WEB-INF/cocoon.xconf + ../tmp/cocoon-work + ../site + + + + + + + + + + + + + + + index.html + + + + + + + */* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/docs/src/documentation/content/xdocs/releasenotes.xml b/src/docs/src/documentation/content/xdocs/releasenotes.xml deleted file mode 100644 index 08257e3ee77..00000000000 --- a/src/docs/src/documentation/content/xdocs/releasenotes.xml +++ /dev/null @@ -1,2875 +0,0 @@ - - - - -
            - ZooKeeper 3.4.5 Release Notes - - - - 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. - - - - -These release notes include new developer and user facing incompatibilities, features, and major improvements. - - - - Changes - -
            -Changes Since 3.4.4 - -Changes Since ZooKeeper 3.4.4 - - - - - Issue - Notes - - - - - - - ZOOKEEPER-1550 - - -ZooKeeperSaslClient does not finish anonymous login on OpenJDK - - - - - - ZOOKEEPER-1376 - - -zkServer.sh does not correctly check for $SERVER_JVMFLAGS - - - - - - ZOOKEEPER-1560 - - -Zookeeper client hangs on creation of large nodes - - - - - -
            -
            -
            -Changes Since 3.4.3 - -Changes Since ZooKeeper 3.4.3 - - - - - Issue - Notes - - - - - - - ZOOKEEPER-1496 - - -Ephemeral node not getting cleared even after client has exited - - - - - - ZOOKEEPER-1048 - - -addauth command does not work in cli_mt/cli_st - - - - - - ZOOKEEPER-1163 - - -Memory leak in zk_hashtable.c:do_insert_watcher_object() - - - - - - ZOOKEEPER-1210 - - -Can't build ZooKeeper RPM with RPM > - - - - - - ZOOKEEPER-1236 - - -Security uses proprietary Sun APIs - - - - - - ZOOKEEPER-1256 - - -ClientPortBindTest is failing on Mac OS X - - - - - - ZOOKEEPER-1277 - - -servers stop serving when lower 32bits of zxid roll over - - - - - - ZOOKEEPER-1307 - - -zkCli.sh is exiting when an Invalid ACL exception is thrown from setACL command through client - - - - - - ZOOKEEPER-1318 - - -In Python binding, get_children (and get and exists, and probably others) with expired session doesn't raise exception properly - - - - - - ZOOKEEPER-1339 - - -C clien doesn't build with --enable-debug - - - - - - ZOOKEEPER-1344 - - -ZooKeeper client multi-update command is not considering the Chroot request - - - - - - ZOOKEEPER-1354 - - -AuthTest.testBadAuthThenSendOtherCommands fails intermittently - - - - - - ZOOKEEPER-1361 - - -Leader.lead iterates over 'learners' set without proper synchronisation - - - - - - ZOOKEEPER-1380 - - -zkperl: _zk_release_watch doesn't remove items properly from the watch list - - - - - - ZOOKEEPER-1384 - - -test-cppunit overrides LD_LIBRARY_PATH and fails if gcc is in non-standard location - - - - - - ZOOKEEPER-1386 - - -avoid flaky URL redirection in "ant javadoc" : replace "http://java.sun.com/javase/6/docs/api/" with "http://download.oracle.com/javase/6/docs/api/" - - - - - - ZOOKEEPER-1395 - - -node-watcher double-free redux - - - - - - ZOOKEEPER-1403 - - -zkCli.sh script quoting issue - - - - - - ZOOKEEPER-1406 - - -dpkg init scripts don't restart - missing check_priv_sep_dir - - - - - - ZOOKEEPER-1412 - - -java client watches inconsistently triggered on reconnect - - - - - - ZOOKEEPER-1419 - - -Leader election never settles for a 5-node cluster - - - - - - ZOOKEEPER-1427 - - -Writing to local files is done non-atomically - - - - - - ZOOKEEPER-1431 - - -zkpython: async calls leak memory - - - - - - ZOOKEEPER-1437 - - -Client uses session before SASL authentication complete - - - - - - ZOOKEEPER-1463 - - -external inline function is not compatible with C99 - - - - - - ZOOKEEPER-1465 - - -Cluster availability following new leader election takes a long time with large datasets - is correlated to dataset size - - - - - - ZOOKEEPER-1466 - - -QuorumCnxManager.shutdown missing synchronization - - - - - - ZOOKEEPER-1471 - - -Jute generates invalid C++ code - - - - - - ZOOKEEPER-1480 - - -ClientCnxn(1161) can't get the current zk server add, so that - Session 0x for server null, unexpected error - - - - - - ZOOKEEPER-1483 - - -Fix leader election recipe documentation - - - - - - ZOOKEEPER-1489 - - -Data loss after truncate on transaction log - - - - - - ZOOKEEPER-1490 - - -If the configured log directory does not exist zookeeper will not start. Better to create the directory and start - - - - - - ZOOKEEPER-1493 - - -C Client: zookeeper_process doesn't invoke completion callback if zookeeper_close has been called - - - - - - ZOOKEEPER-1494 - - -C client: socket leak after receive timeout in zookeeper_interest() - - - - - - ZOOKEEPER-1496 - - -Ephemeral node not getting cleared even after client has exited - - - - - - ZOOKEEPER-1501 - - -Nagios plugin always returns OK when it cannot connect to zookeeper - - - - - - ZOOKEEPER-1514 - - -FastLeaderElection - leader ignores the round information when joining a quorum - - - - - - ZOOKEEPER-1521 - - -LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits - - - - - - ZOOKEEPER-1522 - - -intermittent failures in Zab test due to NPE in recursiveDelete test function - - - - - - ZOOKEEPER-1536 - - -c client : memory leak in winport.c - - - - - - ZOOKEEPER-1321 - - -Add number of client connections metric in JMX and srvr - - - - - - ZOOKEEPER-1377 - - -add support for dumping a snapshot file content (similar to LogFormatter) - - - - - - ZOOKEEPER-1389 - - -it would be nice if start-foreground used exec $JAVA in order to get rid of the intermediate shell process - - - - - - ZOOKEEPER-1390 - - -some expensive debug code not protected by a check for debug - - - - - - ZOOKEEPER-1433 - - -improve ZxidRolloverTest (test seems flakey) - - - - - - ZOOKEEPER-1454 - - -Document how to run autoreconf if cppunit is installed in a non-standard directory - - - - - - ZOOKEEPER-1481 - - -allow the C cli to run exists with a watcher - - - - - - ZOOKEEPER-1497 - - -Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) - - - - - - ZOOKEEPER-1503 - - -remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest - - - - - - ZOOKEEPER-1510 - - -Should not log SASL errors for non-secure usage - - - - - - ZOOKEEPER-1450 - - -Backport ZOOKEEPER-1294 fix to 3.4 and 3.3 - - - - -
            -
            - -
            -Changes Since ZooKeeper 3.4.2 - - -Changes Since ZooKeeper 3.4.2 - - - - - Issue - Notes - - - - - - - ZOOKEEPER-1089 - - -zkServer.sh status does not work due to invalid option of nc. - - - - - - ZOOKEEPER-1345 - - -Add a .gitignore file with general exclusions and Eclipse project files excluded. - - - - - - ZOOKEEPER-1343 - - -getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch. - - - - - - ZOOKEEPER-850 - - - replaces log4j with slf4j code (also in contrib for bookkeeper, zooinspector, - rest,loggraph), added slf4j dependencies into several ivy.xml files. - You must add slf4j-api-1.6.1.jar and slf4j-log4j12-1.6.1.jar (bridge from sl4j - to log4j) to the classpath, if not using the standard scripts. - log4j remains as the final logger yet, there is still work to do: - remove programmatic access to the log4j api from certain classes - (which add appenders or configure log4j at runtime), or move them to contrib - - - - - - ZOOKEEPER-1358 - - -In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) - doesn't sleep by checking that the latency of this call is less than 10sec - - - - - - ZOOKEEPER-1351 - - -Invalid test verification in MultiTransactionTest. - - - - - - ZOOKEEPER-973 - - -bind() could fail on Leader because it does not setReuseAddress on its ServerSocket. - - - - - - ZOOKEEPER-1367 - - - Data inconsistencies and unexpired ephemeral nodes after cluster restart. - - - - - - ZOOKEEPER-1353 - - - C client test suite fails consistently. - - - - - - ZOOKEEPER-1373 - - - Hardcoded SASL login context name clashes with Hadoop security - configuration override. - - - - - - ZOOKEEPER-1352 - - - server.InvalidSnapshotTest is using connection timeouts that - are too short. - - - - - - ZOOKEEPER-1336 - - - javadoc for multi is confusing, references functionality that doesn't - seem to exis - - - - - - ZOOKEEPER-1340 - - - multi problem - typical user operations are - generating ERROR level messages in the server. - - - - - - ZOOKEEPER-1374 - - - C client multi-threaded test suite fails to compile on ARM architectures. - - - - - - ZOOKEEPER-1337 - - - multi's "Transaction" class is missing tests. - - - - - - ZOOKEEPER-1338 - - - class cast exceptions may be thrown by multi ErrorResult class (invalid equals) - - - - - - ZOOKEEPER-1322 - - -Cleanup/fix logging in Quorum code. - - - - - - ZOOKEEPER-1327 - - -There are still remnants of hadoop urls. - - - - -
            -
            - -
            -Changes Since ZooKeeper 3.4.1 - - -Changes Since ZooKeeper 3.4.1 - - - - - Issue - Notes - - - - - - - ZOOKEEPER-1333 - - -NPE in FileTxnSnapLog when restarting a cluster. - - - - - - ZOOKEEPER-1323 - - -c client doesn't compile on freebsd - - - - -
            -
            - -
            -Changes Since ZooKeeper 3.4.0 - - -Changes Since ZooKeeper 3.4.0 - - - - - Issue - Notes - - - - - - - ZOOKEEPER-1311 - - -ZooKeeper test jar is broken - - - - - - ZOOKEEPER-1305 - - -zookeeper.c:prepend_string func can dereference null ptr - - - - - - ZOOKEEPER-1316 - - -zookeeper_init leaks memory if chroot is just '/' - - - - - - ZOOKEEPER-1315 - - - zookeeper_init always reports sessionPasswd=hidden - - - - - - ZOOKEEPER-1317 - - - Possible segfault in zookeeper_init. - - - - - - ZOOKEEPER-1319 - - - Missing data after restarting+expanding a cluster. - - - - - - ZOOKEEPER-1269 - - - Multi deserialization issues. - - - - -
            -
            - -
            -Changes Since ZooKeeper 3.3.0 - - -Changes Since ZooKeeper 3.3.0 - - - - - Issue - Notes - - - - - - Sub-Tasks - - - - - - - - - ZOOKEEPER-1239 - - - add logging/stats to identify fsync stalls. - - - - - - ZOOKEEPER-1208 - - -Ephemeral node not removed after the client session is long gone. - - - - - - ZOOKEEPER-784 - - - server-side functionality for read-only mode. This is not thoroughly tested. - Avoid using it in production. This is also at risk of being removed from - the feature set later. - - - - - - ZOOKEEPER-798 - - - Fixup loggraph for FLE changes - - - - - - ZOOKEEPER-839 - - - deleteRecursive does not belong to the other methods - - - - - - ZOOKEEPER-908 - - - Remove code duplication and inconsistent naming in ClientCnxn.Packet creation - - - - - - ZOOKEEPER-909 - - - Extract NIO specific code from ClientCnxn - - - - - - ZOOKEEPER-966 - - - Client side for multi - - - - - - ZOOKEEPER-967 - - - Server side decoding and function dispatch - - - - - - ZOOKEEPER-968 - - - Database multi-update - - - - - - ZOOKEEPER-1042 - - - Generate zookeeper test jar for maven installation - - - - - - ZOOKEEPER-1081 - - - modify leader/follower code to correctly deal with new leader - - - - - - ZOOKEEPER-1082 - - - modify leader election to correctly take into account current epoch - - - - - - ZOOKEEPER-1150 - - - fix for this patch to compile on windows... - - - - - - ZOOKEEPER-1160 - - - test timeouts are too small - - - - - - ZOOKEEPER-1201 - - - Clean SaslServerCallbackHandler.java - - - - - Bug Fixes - - - - - - - - - ZOOKEEPER-1268 - - - problems with read only mode, intermittent test failures and ERRORs in the log. - - - - - - ZOOKEEPER-1271 - - - testEarlyLeaderAbandonment failing on solaris - clients not retrying connection. - - - - - - ZOOKEEPER-1192 - - -Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished. - - - - - - - ZOOKEEPER-1246 - - - Dead code in PrepRequestProcessor catch Exception block. - - - - - - ZOOKEEPER-1264 - - - FollowerResyncConcurrencyTest failing intermittently. - - - - - - ZOOKEEPER-1270 - - - testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. - - - - - - ZOOKEEPER-1291 - - - AcceptedEpoch not updated at leader before it proposes the epoch to followers. - - - - - - ZOOKEEPER-1282 - - -Learner.java not following Zab 1.0 protocol - setCurrentEpoch should be done upon receipt of NEWLEADER - (before acking it) and not upon receipt of UPTODATE. - - - - - - ZOOKEEPER-335 - - - zookeeper servers should commit the new leader txn to their logs. - - - - - - ZOOKEEPER-418 - - - Need nifty zookeeper browser - - - - - - ZOOKEEPER-603 - - - zkpython should do a better job of freeing memory under error conditions - - - - - - ZOOKEEPER-662 - - - Too many CLOSE_WAIT socket state on a server - - - - - - ZOOKEEPER-690 - - - AsyncTestHammer test fails on hudson. - - - - - - ZOOKEEPER-719 - - - Add throttling to BookKeeper client - - - - - - ZOOKEEPER-720 - - - Use zookeeper-{version}-sources.jar instead of zookeeper-{version}-src.jar to publish sources in the Maven repository - - - - - - ZOOKEEPER-722 - - - zkServer.sh uses sh's builtin echo on BSD, behaves incorrectly. - - - - - - ZOOKEEPER-731 - - - Zookeeper#delete , #create - async versions miss a verb in the javadoc - - - - - - ZOOKEEPER-734 - - - QuorumPeerTestBase.java and ZooKeeperServerMainTest.java do not handle windows path correctly - - - - - - ZOOKEEPER-735 - - - cppunit test testipv6 assumes that the machine is ipv6 enabled. - - - - - - ZOOKEEPER-737 - - - some 4 letter words may fail with netcat (nc) - - - - - - ZOOKEEPER-738 - - - zookeeper.jute.h fails to compile with -pedantic - - - - - - ZOOKEEPER-741 - - - root level create on REST proxy fails - - - - - - ZOOKEEPER-742 - - - Deallocatng None on writes - - - - - - ZOOKEEPER-746 - - - learner outputs session id to log in dec (should be hex) - - - - - - ZOOKEEPER-749 - - - OSGi metadata not included in binary only jar - - - - - - ZOOKEEPER-750 - - - move maven artifacts into "dist-maven" subdir of the release (package target) - - - - - - ZOOKEEPER-758 - - - zkpython segfaults on invalid acl with missing key - - - - - - ZOOKEEPER-763 - - - Deadlock on close w/ zkpython / c client - - - - - - ZOOKEEPER-764 - - - Observer elected leader due to inconsistent voting view - - - - - - ZOOKEEPER-766 - - - forrest recipes docs don't mention the lock/queue recipe implementations available in the release - - - - - - ZOOKEEPER-769 - - - Leader can treat observers as quorum members - - - - - - ZOOKEEPER-772 - - - zkpython segfaults when watcher from async get children is invoked. - - - - - - ZOOKEEPER-774 - - - Recipes tests are slightly outdated: they do not compile against JUnit 4.8 - - - - - - ZOOKEEPER-777 - - - setting acl on a non existant node should return no node error - - - - - - ZOOKEEPER-782 - - - Incorrect C API documentation for Watches - - - - - - ZOOKEEPER-783 - - - committedLog in ZKDatabase is not properly synchronized - - - - - - ZOOKEEPER-787 - - - groupId in deployed pom is wrong - - - - - - ZOOKEEPER-790 - - - Last processed zxid set prematurely while establishing leadership - - - - - - ZOOKEEPER-792 - - - zkpython memory leak - - - - - - ZOOKEEPER-794 - - - Callbacks are not invoked when the client is closed - - - - - - ZOOKEEPER-795 - - - eventThread isn't shutdown after a connection "session expired" event coming - - - - - - ZOOKEEPER-796 - - - zkServer.sh should support an external PIDFILE variable - - - - - - ZOOKEEPER-800 - - - zoo_add_auth returns ZOK if zookeeper handle is in ZOO_CLOSED_STATE - - - - - - ZOOKEEPER-804 - - - c unit tests failing due to "assertion cptr failed" - - - - - - ZOOKEEPER-813 - - - maven install is broken due to incorrect organisation - - - - - - ZOOKEEPER-814 - - - monitoring scripts are missing apache license headers - - - - - - ZOOKEEPER-820 - - - update c unit tests to ensure "zombie" java server processes don't cause failure - - - - - - ZOOKEEPER-822 - - - Leader election taking a long time to complete - - - - - - ZOOKEEPER-831 - - - BookKeeper: Throttling improved for reads - - - - - - ZOOKEEPER-844 - - - handle auth failure in java client - - - - - - ZOOKEEPER-846 - - - zookeeper client doesn't shut down cleanly on the close call - - - - - - ZOOKEEPER-854 - - - BookKeeper does not compile due to changes in the ZooKeeper code - - - - - - ZOOKEEPER-855 - - - clientPortBindAddress should be clientPortAddress - - - - - - ZOOKEEPER-861 - - - Missing the test SSL certificate used for running junit tests. - - - - - - ZOOKEEPER-867 - - - ClientTest is failing on hudson - fd cleanup - - - - - - ZOOKEEPER-870 - - - Zookeeper trunk build broken. - - - - - - ZOOKEEPER-874 - - - FileTxnSnapLog.restore does not call listener - - - - - - ZOOKEEPER-880 - - - QuorumCnxManager$SendWorker grows without bounds - - - - - - ZOOKEEPER-881 - - - ZooKeeperServer.loadData loads database twice - - - - - - ZOOKEEPER-882 - - - Startup loads last transaction from snapshot - - - - - - ZOOKEEPER-884 - - - Remove LedgerSequence references from BookKeeper documentation and comments in tests - - - - - - ZOOKEEPER-888 - - - c-client / zkpython: Double free corruption on node watcher - - - - - - ZOOKEEPER-893 - - - ZooKeeper high cpu usage when invalid requests - - - - - - ZOOKEEPER-897 - - - C Client seg faults during close - - - - - - ZOOKEEPER-898 - - - C Client might not cleanup correctly during close - - - - - - ZOOKEEPER-902 - - - Fix findbug issue in trunk "Malicious code vulnerability" - - - - - - ZOOKEEPER-904 - - - super digest is not actually acting as a full superuser - - - - - - ZOOKEEPER-913 - - - Version parser fails to parse "3.3.2-dev" from build.xml. - - - - - - ZOOKEEPER-917 - - - Leader election selected incorrect leader - - - - - - ZOOKEEPER-919 - - - Ephemeral nodes remains in one of ensemble after deliberate SIGKILL - - - - - - ZOOKEEPER-921 - - - zkPython incorrectly checks for existence of required ACL elements - - - - - - ZOOKEEPER-937 - - - test -e not available on solaris /bin/sh - - - - - - ZOOKEEPER-957 - - - zkCleanup.sh doesn't do anything - - - - - - ZOOKEEPER-958 - - - Flag to turn off autoconsume in hedwig c++ client - - - - - - ZOOKEEPER-961 - - - Watch recovery after disconnection when connection string contains a prefix - - - - - - ZOOKEEPER-962 - - - leader/follower coherence issue when follower is receiving a DIFF - - - - - - ZOOKEEPER-963 - - - Make Forrest work with JDK6 - - - - - - ZOOKEEPER-965 - - - Need a multi-update command to allow multiple znodes to be updated safely - - - - - - ZOOKEEPER-975 - - - new peer goes in LEADING state even if ensemble is online - - - - - - ZOOKEEPER-976 - - - ZooKeeper startup script doesn't use JAVA_HOME - - - - - - ZOOKEEPER-981 - - - Hang in zookeeper_close() in the multi-threaded C client - - - - - - ZOOKEEPER-983 - - - running zkServer.sh start remotely using ssh hangs - - - - - - ZOOKEEPER-985 - - - Test BookieRecoveryTest fails on trunk. - - - - - - ZOOKEEPER-1006 - - - QuorumPeer "Address already in use" -- regression in 3.3.3 - - - - - - ZOOKEEPER-1007 - - - iarchive leak in C client - - - - - - ZOOKEEPER-1013 - - - zkServer.sh usage message should mention all startup options - - - - - - ZOOKEEPER-1027 - - - chroot not transparent in zoo_create() - - - - - - ZOOKEEPER-1028 - - - In python bindings, zookeeper.set2() should return a stat dict but instead returns None - - - - - - ZOOKEEPER-1033 - - - c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src - - - - - - ZOOKEEPER-1034 - - - perl bindings should automatically find the zookeeper c-client headers - - - - - - ZOOKEEPER-1046 - - - Creating a new sequential node results in a ZNODEEXISTS error - - - - - - ZOOKEEPER-1049 - - - Session expire/close flooding renders heartbeats to delay significantly - - - - - - ZOOKEEPER-1051 - - - SIGPIPE in Zookeeper 0.3.* when send'ing after cluster disconnection - - - - - - ZOOKEEPER-1052 - - - Findbugs warning in QuorumPeer.ResponderThread.run() - - - - - - ZOOKEEPER-1055 - - - check for duplicate ACLs in addACL() and create() - - - - - - ZOOKEEPER-1058 - - - fix typo in opToString for getData - - - - - - ZOOKEEPER-1059 - - - stat command isses on non-existing node causes NPE - - - - - - ZOOKEEPER-1060 - - - QuorumPeer takes a long time to shutdown - - - - - - ZOOKEEPER-1061 - - - Zookeeper stop fails if start called twice - - - - - - ZOOKEEPER-1063 - - - Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes - - - - - - ZOOKEEPER-1068 - - - Documentation and default config suggest incorrect location for Zookeeper state - - - - - - ZOOKEEPER-1069 - - - Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log - - - - - - ZOOKEEPER-1073 - - - address a documentation issue in ZOOKEEPER-1030 - - - - - - ZOOKEEPER-1074 - - - zkServer.sh is missing nohup/sleep, which are necessary for remote invocation - - - - - - ZOOKEEPER-1076 - - - some quorum tests are unnecessarily extending QuorumBase - - - - - - ZOOKEEPER-1083 - - - Javadoc for WatchedEvent not being generated - - - - - - ZOOKEEPER-1086 - - - zookeeper test jar has non mavenised dependency. - - - - - - ZOOKEEPER-1087 - - - ForceSync VM arguement not working when set to "no" - - - - - - ZOOKEEPER-1088 - - - delQuota does not remove the quota node and subesquent setquota calls for that path will fail - - - - - - ZOOKEEPER-1090 - - - Race condition while taking snapshot can lead to not restoring data tree correctly - - - - - - ZOOKEEPER-1091 - - - when the chrootPath of ClientCnxn is not null and the Watches of zooKeeper is not null and the method primeConnection(SelectionKey k) of ClientCnxn Occurred again for some reason ,then the wrong watcher clientPath is sended to server - - - - - - ZOOKEEPER-1097 - - - Quota is not correctly rehydrated on snapshot reload - - - - - - ZOOKEEPER-1101 - - - Upload zookeeper-test maven artifacts to maven repository. - - - - - - ZOOKEEPER-1108 - - - Various bugs in zoo_add_auth in C - - - - - - ZOOKEEPER-1109 - - - Zookeeper service is down when SyncRequestProcessor meets any exception. - - - - - - ZOOKEEPER-1111 - - - JMXEnv uses System.err instead of logging - - - - - - ZOOKEEPER-1119 - - - zkServer stop command incorrectly reading comment lines in zoo.cfg - - - - - - ZOOKEEPER-1124 - - - Multiop submitted to non-leader always fails due to timeout - - - - - - ZOOKEEPER-1136 - - - NEW_LEADER should be queued not sent to match the Zab 1.0 protocol on the twiki - - - - - - ZOOKEEPER-1138 - - - release audit failing for a number of new files - - - - - - ZOOKEEPER-1139 - - - jenkins is reporting two warnings, fix these - - - - - - ZOOKEEPER-1140 - - - server shutdown is not stopping threads - - - - - - ZOOKEEPER-1141 - - - zkpython fails tests under python 2.4 - - - - - - ZOOKEEPER-1142 - - - incorrect stat output - - - - - - ZOOKEEPER-1144 - - - ZooKeeperServer not starting on leader due to a race condition - - - - - - ZOOKEEPER-1145 - - - ObserverTest.testObserver fails at particular point after several runs of ant junt.run -Dtestcase - - - - - - ZOOKEEPER-1146 - - - significant regression in client (c/python) performance - - - - - - ZOOKEEPER-1152 - - - Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer - - - - - - ZOOKEEPER-1154 - - - Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election - - - - - - ZOOKEEPER-1156 - - - Log truncation truncating log too much - can cause data loss - - - - - - ZOOKEEPER-1165 - - - better eclipse support in tests - - - - - - ZOOKEEPER-1168 - - - ZooKeeper fails to run with IKVM - - - - - - ZOOKEEPER-1171 - - - fix build for java 7 - - - - - - ZOOKEEPER-1174 - - - FD leak when network unreachable - - - - - - ZOOKEEPER-1181 - - - Fix problems with Kerberos TGT renewal - - - - - - ZOOKEEPER-1185 - - - Send AuthFailed event to client if SASL authentication fails - - - - - - ZOOKEEPER-1189 - - - For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. - - - - - - ZOOKEEPER-1190 - - - ant package is not including many of the bin scripts in the package (zkServer.sh for example) - - - - - - ZOOKEEPER-1195 - - - SASL authorizedID being incorrectly set: should use getHostName() rather than getServiceName() - - - - - - ZOOKEEPER-1203 - - - Zookeeper systest is missing Junit Classes - - - - - - ZOOKEEPER-1206 - - - Sequential node creation does not use always use digits in node name given certain Locales. - - - - - - ZOOKEEPER-1212 - - - zkServer.sh stop action is not conformat with LSB para 20.2 Init Script Actions - - - - - - ZOOKEEPER-1237 - - - ERRORs being logged when queued responses are sent after socket has closed. - - - - - Improvements - - - - - - - - - ZOOKEEPER-494 - - - zookeeper should install include headers in /usr/local/include/zookeeper - - - - - - ZOOKEEPER-500 - - - Async methods shouldnt throw exceptions - - - - - - ZOOKEEPER-631 - - - zkpython's C code could do with a style clean-up - - - - - - ZOOKEEPER-636 - - - configure.ac has instructions which override the contents of CFLAGS and CXXFLAGS. - - - - - - ZOOKEEPER-724 - - - Improve junit test integration - log harness information - - - - - - ZOOKEEPER-733 - - - use netty to handle client connections - - - - - - ZOOKEEPER-765 - - - Add python example script - - - - - - ZOOKEEPER-773 - - - Log visualisation - - - - - - ZOOKEEPER-788 - - - Add server id to message logs - - - - - - ZOOKEEPER-789 - - - Improve FLE log messages - - - - - - ZOOKEEPER-797 - - - c client source with AI_ADDRCONFIG cannot be compiled with early glibc - - - - - - ZOOKEEPER-809 - - - Improved REST Interface - - - - - - ZOOKEEPER-821 - - - Add ZooKeeper version information to zkpython - - - - - - ZOOKEEPER-853 - - - Make zookeeper.is_unrecoverable return True or False and not an integer - - - - - - ZOOKEEPER-862 - - - Hedwig created ledgers with hardcoded Bookkeeper ensemble and quorum size. Make these a server config parameter instead. - - - - - - ZOOKEEPER-864 - - - Hedwig C++ client improvements - - - - - - ZOOKEEPER-891 - - - Allow non-numeric version strings - - - - - - ZOOKEEPER-905 - - - enhance zkServer.sh for easier zookeeper automation-izing - - - - - - ZOOKEEPER-926 - - - Fork Hadoop common's test-patch.sh and modify for Zookeeper - - - - - - ZOOKEEPER-977 - - - passing null for path_buffer in zoo_create - - - - - - ZOOKEEPER-980 - - - allow configuration parameters for log4j.properties - - - - - - ZOOKEEPER-993 - - - Code improvements - - - - - - ZOOKEEPER-997 - - - ZkClient ignores command if there are any space in front of it - - - - - - ZOOKEEPER-1018 - - - The connection permutation in get_addrs uses a weak and inefficient shuffle - - - - - - ZOOKEEPER-1025 - - - zkCli is overly sensitive to to spaces. - - - - - - ZOOKEEPER-1030 - - - Increase default for maxClientCnxns - - - - - - ZOOKEEPER-1094 - - - Small improvements to LeaderElection and Vote classes - - - - - - ZOOKEEPER-1095 - - - Simple leader election recipe - - - - - - ZOOKEEPER-1103 - - - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. - - - - - - ZOOKEEPER-1104 - - - CLONE - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. - - - - - - ZOOKEEPER-1143 - - - quorum send & recv workers are missing thread names - - - - - - ZOOKEEPER-1153 - - - Deprecate AuthFLE and LE - - - - - - ZOOKEEPER-1166 - - - Please add a few svn:ignore properties - - - - - - ZOOKEEPER-1169 - - - Fix compiler (eclipse) warnings in (generated) jute code - - - - - - ZOOKEEPER-1243 - - - New 4lw for short simple monitoring ldck - - - - - Features - - - - - - - - - ZOOKEEPER-464 - - - Need procedure to garbage collect ledgers - - - - - - ZOOKEEPER-465 - - - Ledger size in bytes - - - - - - ZOOKEEPER-546 - - - add "diskless" ensemble support - - - - - - ZOOKEEPER-712 - - - Bookie recovery - - - - - - ZOOKEEPER-729 - - - Recursively delete a znode - zkCli.sh rmr /node - - - - - - ZOOKEEPER-744 - - - Add monitoring four-letter word - - - - - - ZOOKEEPER-747 - - - Add C# generation to Jute - - - - - - ZOOKEEPER-775 - - - A large scale pub/sub system - - - - - - ZOOKEEPER-799 - - - Add tools and recipes for monitoring as a contrib - - - - - - ZOOKEEPER-808 - - - Web-based Administrative Interface - - - - - - ZOOKEEPER-859 - - - Native Windows version of C client - - - - - - ZOOKEEPER-938 - - - Support Kerberos authentication of clients. - - - - - - ZOOKEEPER-992 - - - MT Native Version of Windows C Client - - - - - - ZOOKEEPER-999 - - - Create an package integration project - - - - - - ZOOKEEPER-1012 - - - support distinct JVMFLAGS for zookeeper server in zkServer.sh and zookeeper client in zkCli.sh - - - - - - ZOOKEEPER-1020 - - - Implement function in C client to determine which host you're currently connected to. - - - - - - ZOOKEEPER-1107 - - - automating log and snapshot cleaning - - - - - Tasks - - - - - - - - - ZOOKEEPER-754 - - - numerous misspellings "succesfully" - - - - - - ZOOKEEPER-1149 - - - users cannot migrate from 3.4->3.3->3.4 server code against a single datadir - - - - - Tests - - - - - - - - - ZOOKEEPER-239 - - - ZooKeeper System Tests - - - -
            - -
            -
            - From 1b8e963d369254964e5e0b4927d8477d78537a0c Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Wed, 17 Aug 2016 20:01:47 +0000 Subject: [PATCH 351/444] Preparing for release 3.4.9 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1756679 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + build.xml | 2 +- docs/releasenotes.html | 4994 +++++++---------------------- src/NOTICE.txt | 2 +- src/c/configure.ac | 2 +- src/c/include/winconfig.h | 6 +- src/c/include/zookeeper_version.h | 2 +- 7 files changed, 1228 insertions(+), 3782 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0d22603f8b8..8552ba74a0c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +Release 3.4.9 - 2016-08-18 + Backward compatible changes: BUGFIXES: diff --git a/build.xml b/build.xml index ab254b2c9ef..8562000f7e7 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 97f028f8fc2..71d70720c0c 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -5,7 +5,7 @@ -ZooKeeper 3.4.5 Release Notes +ZooKeeper Release Notes @@ -202,3802 +202,1246 @@ PDF -icon
            PDF
            -

            ZooKeeper 3.4.5 Release Notes

            -
            -
            -
            -
            - - - - - -

            -These release notes include new developer and user facing incompatibilities, features, and major improvements. -

            - - + +

            Improvement +

              - -
            • -

              -Changes -

              +
            • [ZOOKEEPER-2240] - Make the three-node minimum more explicit in documentation and on website +
            • +
            • [ZOOKEEPER-2373] - Licenses section missing from pom file +
            • +
            • [ZOOKEEPER-2378] - upgrade ivy to recent version +
            • +
            • [ZOOKEEPER-2514] - Simplify releasenotes creation for 3.4 branch - consistent with newer branches.
            • -
            - -

            Changes Since 3.4.4

            -
            - - - -Changes Since ZooKeeper 3.4.4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.4
            IssueNotes
            - - ZOOKEEPER-1550 - -ZooKeeperSaslClient does not finish anonymous login on OpenJDK -
            - - ZOOKEEPER-1376 - -zkServer.sh does not correctly check for $SERVER_JVMFLAGS -
            - - ZOOKEEPER-1560 - -Zookeeper client hangs on creation of large nodes -
            -
            - - -

            Changes Since 3.4.3

            -
            - - - -Changes Since ZooKeeper 3.4.3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Release Notes - ZooKeeper - Version 3.4.8 + +

            Bug +

            +
              +
            • [ZOOKEEPER-1929] - std::length_error on update children +
            • +
            • [ZOOKEEPER-2211] - PurgeTxnLog does not correctly purge when snapshots and logs are at different locations +
            • +
            • [ZOOKEEPER-2229] - Several four-letter words are undocumented. +
            • +
            • [ZOOKEEPER-2281] - ZK Server startup fails if there are spaces in the JAVA_HOME path +
            • +
            • [ZOOKEEPER-2295] - TGT refresh time logic is wrong +
            • +
            • [ZOOKEEPER-2311] - assert in setup_random +
            • +
            • [ZOOKEEPER-2337] - Fake "invalid" hostnames used in tests are sometimes valid +
            • +
            • [ZOOKEEPER-2340] - JMX is disabled even if JMXDISABLE is false +
            • +
            • [ZOOKEEPER-2347] - Deadlock shutting down zookeeper +
            • +
            • [ZOOKEEPER-2360] - Update commons collections version used by tests/releaseaudit +
            • +
            • [ZOOKEEPER-2391] - setMin/MaxSessionTimeout of ZookeeperServer are implemented in quite a weak way +
            • +
            • [ZOOKEEPER-2412] - leader zk out of memory, and leader db lastZxid is not update when process set data. +
            • +
            • [ZOOKEEPER-2468] - SetQuota and DelQuota +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-2512] - Allow Jetty dependency to use HTTPS and Basic Auth +
            • +
            - + Release Notes - ZooKeeper - Version 3.4.7 + +

            Sub-task +

            +
              +
            • [ZOOKEEPER-1866] - ClientBase#createClient is failing frequently +
            • +
            • [ZOOKEEPER-1868] - Server not coming back up in QuorumZxidSyncTest +
            • +
            • [ZOOKEEPER-1872] - QuorumPeer is not shutdown in few cases +
            • +
            • [ZOOKEEPER-1904] - WatcherTest#testWatchAutoResetWithPending is failing +
            • +
            • [ZOOKEEPER-1905] - ZKClients are hitting KeeperException$ConnectionLossException due to wrong usage pattern +
            • +
            • [ZOOKEEPER-2047] - testTruncationNullLog fails on windows +
            • +
            • [ZOOKEEPER-2237] - Port async multi to 3.4 branch +
            • +
            + +

            Bug +

            +
              +
            • [ZOOKEEPER-602] - log all exceptions not caught by ZK threads +
            • +
            • [ZOOKEEPER-706] - large numbers of watches can cause session re-establishment to fail +
            • +
            • [ZOOKEEPER-1002] - The Barrier sample code should create a EPHEMERAL znode instead of EPHEMERAL_SEQUENTIAL znode +
            • +
            • [ZOOKEEPER-1029] - C client bug in zookeeper_init (if bad hostname is given) +
            • +
            • [ZOOKEEPER-1062] - Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait +
            • +
            • [ZOOKEEPER-1077] - C client lib doesn't build on Solaris +
            • +
            • [ZOOKEEPER-1222] - getACL should only call DataTree.copyStat when passed in stat is not null +
            • +
            • [ZOOKEEPER-1575] - adding .gitattributes to prevent CRLF and LF mismatches for source and text files +
            • +
            • [ZOOKEEPER-1797] - PurgeTxnLog may delete data logs during roll +
            • +
            • [ZOOKEEPER-1803] - Add description for pzxid in programmer's guide. +
            • +
            • [ZOOKEEPER-1833] - fix windows build +
            • +
            • [ZOOKEEPER-1853] - zkCli.sh can't issue a CREATE command containing spaces in the data +
            • +
            • [ZOOKEEPER-1878] - Inconsistent behavior in autocreation of dataDir and dataLogDir +
            • +
            • [ZOOKEEPER-1888] - ZkCli.cmd commands fail with "'java' is not recognized as an internal or external command" +
            • +
            • [ZOOKEEPER-1895] - update all notice files, copyright, etc... with the new year - 2014 +
            • +
            • [ZOOKEEPER-1897] - ZK Shell/Cli not processing commands +
            • +
            • [ZOOKEEPER-1900] - NullPointerException in truncate +
            • +
            • [ZOOKEEPER-1901] - [JDK8] Sort children for comparison in AsyncOps tests +
            • +
            • [ZOOKEEPER-1906] - zkpython: invalid data in GetData for empty node +
            • +
            • [ZOOKEEPER-1911] - REST contrib module does not include all required files when packaged +
            • +
            • [ZOOKEEPER-1913] - Invalid manifest files due to bogus revision property value +
            • +
            • [ZOOKEEPER-1917] - Apache Zookeeper logs cleartext admin passwords +
            • +
            • [ZOOKEEPER-1926] - Unit tests should only use build/test/data for data +
            • +
            • [ZOOKEEPER-1927] - zkServer.sh fails to read dataDir (and others) from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID). +
            • +
            • [ZOOKEEPER-1939] - ReconfigRecoveryTest.testNextConfigUnreachable is failing +
            • +
            • [ZOOKEEPER-1943] - "src/contrib/zooinspector/NOTICE.txt" isn't complying to ".gitattributes" in branch-3.4 +
            • +
            • [ZOOKEEPER-1945] - deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 +
            • +
            • [ZOOKEEPER-1949] - recipes jar not included in the distribution package +
            • +
            • [ZOOKEEPER-2026] - Startup order in ServerCnxnFactory-ies is wrong +
            • +
            • [ZOOKEEPER-2033] - zookeeper follower fails to start after a restart immediately following a new epoch +
            • +
            • [ZOOKEEPER-2039] - Jute compareBytes incorrect comparison index +
            • +
            • [ZOOKEEPER-2049] - Yosemite build failure: htonll conflict +
            • +
            • [ZOOKEEPER-2052] - Unable to delete a node when the node has no children +
            • +
            • [ZOOKEEPER-2056] - Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant +
            • +
            • [ZOOKEEPER-2060] - Trace bug in NettyServerCnxnFactory +
            • +
            • [ZOOKEEPER-2064] - Prevent resource leak in various classes +
            • +
            • [ZOOKEEPER-2073] - Memory leak on zookeeper_close +
            • +
            • [ZOOKEEPER-2096] - C client builds with incorrect error codes in VisualStudio 2010+ +
            • +
            • [ZOOKEEPER-2114] - jute generated allocate_* functions are not externally visible +
            • +
            • [ZOOKEEPER-2124] - Allow Zookeeper version string to have underscore '_' +
            • +
            • [ZOOKEEPER-2142] - JMX ObjectName is incorrect for observers +
            • +
            • [ZOOKEEPER-2146] - BinaryInputArchive readString should check length before allocating memory +
            • +
            • [ZOOKEEPER-2174] - JUnit4ZKTestRunner logs test failure for all exceptions even if the test method is annotated with an expected exception. +
            • +
            • [ZOOKEEPER-2186] - QuorumCnxManager#receiveConnection may crash with random input +
            • +
            • [ZOOKEEPER-2201] - Network issues can cause cluster to hang due to near-deadlock +
            • +
            • [ZOOKEEPER-2213] - Empty path in Set crashes server and prevents restart +
            • +
            • [ZOOKEEPER-2224] - Four letter command hangs when network is slow +
            • +
            • [ZOOKEEPER-2227] - stmk four-letter word fails execution at server while reading trace mask argument. +
            • +
            • [ZOOKEEPER-2235] - License update +
            • +
            • [ZOOKEEPER-2239] - JMX State from LocalPeerBean incorrect +
            • +
            • [ZOOKEEPER-2245] - SimpleSysTest test cases fails +
            • +
            • [ZOOKEEPER-2256] - Zookeeper is not using specified JMX port in zkEnv.sh +
            • +
            • [ZOOKEEPER-2268] - Zookeeper doc creation fails on windows +
            • +
            • [ZOOKEEPER-2279] - QuorumPeer loadDataBase() error message is incorrect +
            • +
            • [ZOOKEEPER-2296] - compilation broken for 3.4 +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-657] - Cut down the running time of ZKDatabase corruption. +
            • +
            • [ZOOKEEPER-1402] - Upload Zookeeper package to Maven Central +
            • +
            • [ZOOKEEPER-1506] - Re-try DNS hostname -> IP resolution if node connection fails +
            • +
            • [ZOOKEEPER-1574] - mismatched CR/LF endings in text files +
            • +
            • [ZOOKEEPER-1746] - AsyncCallback.*Callback don't have any Javadoc +
            • +
            • [ZOOKEEPER-1907] - Improve Thread handling +
            • +
            • [ZOOKEEPER-1948] - Enable JMX remote monitoring +
            • +
            • [ZOOKEEPER-2040] - Server to log underlying cause of SASL connection problems +
            • +
            • [ZOOKEEPER-2126] - Improve exit log messsage of EventThread and SendThread by adding SessionId +
            • +
            • [ZOOKEEPER-2179] - Typo in Watcher.java +
            • +
            • [ZOOKEEPER-2194] - Let DataNode.getChildren() return an unmodifiable view of its children set +
            • +
            • [ZOOKEEPER-2205] - Log type of unexpected quorum packet in learner handler loop +
            • +
            • [ZOOKEEPER-2315] - Change client connect zk service timeout log level from Info to Warn level +
            • +
            + Release Notes - ZooKeeper - Version 3.4.6 + +

            Sub-task +

            +
              +
            • [ZOOKEEPER-1414] - QuorumPeerMainTest.testQuorum, testBadPackets are failing intermittently +
            • +
            • [ZOOKEEPER-1459] - Standalone ZooKeeperServer is not closing the transaction log files on shutdown +
            • +
            • [ZOOKEEPER-1558] - Leader should not snapshot uncommitted state +
            • +
            • [ZOOKEEPER-1808] - Add version to FLE notifications for 3.4 branch +
            • +
            • [ZOOKEEPER-1817] - Fix don't care for b3.4 +
            • +
            • [ZOOKEEPER-1834] - Catch IOException in FileTxnLog +
            • +
            • [ZOOKEEPER-1837] - Fix JMXEnv checks (potential race conditions) +
            • +
            • [ZOOKEEPER-1838] - ZooKeeper shutdown hangs indefinitely at NioServerSocketChannelFactory.releaseExternalResources +
            • +
            • [ZOOKEEPER-1841] - problem in QuorumTest +
            • +
            • [ZOOKEEPER-1849] - Need to properly tear down tests in various cases +
            • +
            • [ZOOKEEPER-1852] - ServerCnxnFactory instance is not properly cleanedup +
            • +
            • [ZOOKEEPER-1854] - ClientBase ZooKeeper server clean-up +
            • +
            • [ZOOKEEPER-1857] - PrepRequestProcessotTest doesn't shutdown ZooKeeper server +
            • +
            • [ZOOKEEPER-1858] - JMX checks - potential race conditions while stopping and starting server +
            • +
            • [ZOOKEEPER-1867] - Bug in ZkDatabaseCorruptionTest +
            • +
            • [ZOOKEEPER-1873] - Unnecessarily InstanceNotFoundException is coming when unregister failed jmxbeans +
            • +
            + +

            Bug +

            +
              +
            • [ZOOKEEPER-87] - Follower does not shut itself down if its too far behind the leader. +
            • +
            • [ZOOKEEPER-732] - Improper translation of error into Python exception +
            • +
            • [ZOOKEEPER-753] - update log4j dependency from 1.2.15 to 1.2.16 in branch 3.4 +
            • +
            • [ZOOKEEPER-805] - four letter words fail with latest ubuntu nc.openbsd +
            • +
            • [ZOOKEEPER-877] - zkpython does not work with python3.1 +
            • +
            • [ZOOKEEPER-978] - ZookeeperServer does not close zk database on shutdwon +
            • +
            • [ZOOKEEPER-1057] - zookeeper c-client, connection to offline server fails to successfully fallback to second zk host +
            • +
            • [ZOOKEEPER-1179] - NettyServerCnxn does not properly close socket on 4 letter word requests +
            • +
            • [ZOOKEEPER-1238] - when the linger time was changed for NIO the patch missed Netty +
            • +
            • [ZOOKEEPER-1334] - Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF is flawed +
            • +
            • [ZOOKEEPER-1379] - 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary +
            • +
            • [ZOOKEEPER-1382] - Zookeeper server holds onto dead/expired session ids in the watch data structures +
            • +
            • [ZOOKEEPER-1387] - Wrong epoch file created +
            • +
            • [ZOOKEEPER-1388] - Client side 'PathValidation' is missing for the multi-transaction api. +
            • +
            • [ZOOKEEPER-1448] - Node+Quota creation in transaction log can crash leader startup +
            • +
            • [ZOOKEEPER-1462] - Read-only server does not initialize database properly +
            • +
            • [ZOOKEEPER-1474] - Cannot build Zookeeper with IBM Java: use of Sun MXBean classes +
            • +
            • [ZOOKEEPER-1478] - Small bug in QuorumTest.testFollowersStartAfterLeader( ) +
            • +
            • [ZOOKEEPER-1495] - ZK client hangs when using a function not available on the server. +
            • +
            • [ZOOKEEPER-1513] - "Unreasonable length" exception while starting a server. +
            • +
            • [ZOOKEEPER-1535] - ZK Shell/Cli re-executes last command on exit +
            • +
            • [ZOOKEEPER-1548] - Cluster fails election loop in new and interesting way +
            • +
            • [ZOOKEEPER-1551] - Observers ignore txns that come after snapshot and UPTODATE +
            • +
            • [ZOOKEEPER-1553] - Findbugs configuration is missing some dependencies +
            • +
            • [ZOOKEEPER-1554] - Can't use zookeeper client without SASL +
            • +
            • [ZOOKEEPER-1557] - jenkins jdk7 test failure in testBadSaslAuthNotifiesWatch +
            • +
            • [ZOOKEEPER-1562] - Memory leaks in zoo_multi API +
            • +
            • [ZOOKEEPER-1573] - Unable to load database due to missing parent node +
            • +
            • [ZOOKEEPER-1578] - org.apache.zookeeper.server.quorum.Zab1_0Test failed due to hard code with 33556 port +
            • +
            • [ZOOKEEPER-1581] - change copyright in notice to 2012 +
            • +
            • [ZOOKEEPER-1596] - Zab1_0Test should ensure that the file is closed +
            • +
            • [ZOOKEEPER-1597] - Windows build failing +
            • +
            • [ZOOKEEPER-1599] - 3.3 server cannot join 3.4 quorum +
            • +
            • [ZOOKEEPER-1603] - StaticHostProviderTest testUpdateClientMigrateOrNot hangs +
            • +
            • [ZOOKEEPER-1606] - intermittent failures in ZkDatabaseCorruptionTest on jenkins +
            • +
            • [ZOOKEEPER-1610] - Some classes are using == or != to compare Long/String objects instead of .equals() +
            • +
            • [ZOOKEEPER-1613] - The documentation still points to 2008 in the copyright notice +
            • +
            • [ZOOKEEPER-1622] - session ids will be negative in the year 2022 +
            • +
            • [ZOOKEEPER-1624] - PrepRequestProcessor abort multi-operation incorrectly +
            • +
            • [ZOOKEEPER-1629] - testTransactionLogCorruption occasionally fails +
            • +
            • [ZOOKEEPER-1632] - fix memory leaks in cli_st +
            • +
            • [ZOOKEEPER-1633] - Introduce a protocol version to connection initiation message +
            • +
            • [ZOOKEEPER-1642] - Leader loading database twice +
            • +
            • [ZOOKEEPER-1645] - ZooKeeper OSGi package imports not complete +
            • +
            • [ZOOKEEPER-1646] - mt c client tests fail on Ubuntu Raring +
            • +
            • [ZOOKEEPER-1647] - OSGi package import/export changes not applied to bin-jar +
            • +
            • [ZOOKEEPER-1648] - Fix WatcherTest in JDK7 +
            • +
            • [ZOOKEEPER-1653] - zookeeper fails to start because of inconsistent epoch +
            • +
            • [ZOOKEEPER-1657] - Increased CPU usage by unnecessary SASL checks +
            • +
            • [ZOOKEEPER-1663] - scripts don't work when path contains spaces +
            • +
            • [ZOOKEEPER-1667] - Watch event isn't handled correctly when a client reestablish to a server +
            • +
            • [ZOOKEEPER-1696] - Fail to run zookeeper client on Weblogic application server +
            • +
            • [ZOOKEEPER-1697] - large snapshots can cause continuous quorum failure +
            • +
            • [ZOOKEEPER-1702] - ZooKeeper client may write operation packets before receiving successful response to connection request, can cause TCP RST +
            • +
            • [ZOOKEEPER-1706] - Typo in Double Barriers example +
            • +
            • [ZOOKEEPER-1711] - ZooKeeper server binds to all ip addresses for leader election and broadcast +
            • +
            • [ZOOKEEPER-1713] - wrong time calculation in zkfuse.cc +
            • +
            • [ZOOKEEPER-1714] - perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used +
            • +
            • [ZOOKEEPER-1719] - zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 +
            • +
            • [ZOOKEEPER-1731] - Unsynchronized access to ServerCnxnFactory.connectionBeans results in deadlock +
            • +
            • [ZOOKEEPER-1732] - ZooKeeper server unable to join established ensemble +
            • +
            • [ZOOKEEPER-1733] - FLETest#testLE is flaky on windows boxes +
            • +
            • [ZOOKEEPER-1744] - clientPortAddress breaks "zkServer.sh status" +
            • +
            • [ZOOKEEPER-1745] - Wrong Import-Package in the META-INF/MANIFEST.MF of zookeeper 3.4.5 bundle +
            • +
            • [ZOOKEEPER-1750] - Race condition producing NPE in NIOServerCnxn.toString +
            • +
            • [ZOOKEEPER-1751] - ClientCnxn#run could miss the second ping or connection get dropped before a ping +
            • +
            • [ZOOKEEPER-1753] - ClientCnxn is not properly releasing the resources, which are used to ping RwServer +
            • +
            • [ZOOKEEPER-1754] - Read-only server allows to create znode +
            • +
            • [ZOOKEEPER-1755] - Concurrent operations of four letter 'dump' ephemeral command and killSession causing NPE +
            • +
            • [ZOOKEEPER-1756] - zookeeper_interest() in C client can return a timeval of 0 +
            • +
            • [ZOOKEEPER-1764] - ZooKeeper attempts at SASL eventhough it shouldn't +
            • +
            • [ZOOKEEPER-1765] - Update code conventions link on "How to contribute" page +
            • +
            • [ZOOKEEPER-1770] - NullPointerException in SnapshotFormatter +
            • +
            • [ZOOKEEPER-1774] - QuorumPeerMainTest fails consistently with "complains about host" assertion failure +
            • +
            • [ZOOKEEPER-1775] - Ephemeral nodes not present in one of the members of the ensemble +
            • +
            • [ZOOKEEPER-1776] - Ephemeral nodes not present in one of the members of the ensemble +
            • +
            • [ZOOKEEPER-1781] - ZooKeeper Server fails if snapCount is set to 1 +
            • +
            • [ZOOKEEPER-1786] - ZooKeeper data model documentation is incorrect +
            • +
            • [ZOOKEEPER-1790] - Deal with special ObserverId in QuorumCnxManager.receiveConnection +
            • +
            • [ZOOKEEPER-1798] - Fix race condition in testNormalObserverRun +
            • +
            • [ZOOKEEPER-1799] - SaslAuthFailDesignatedClientTest.testAuth fails frequently on SUSE +
            • +
            • [ZOOKEEPER-1805] - "Don't care" value in ZooKeeper election breaks rolling upgrades +
            • +
            • [ZOOKEEPER-1811] - The ZooKeeperSaslClient service name principal is hardcoded to "zookeeper" +
            • +
            • [ZOOKEEPER-1812] - ZooInspector reconnection always fails if first connection fails +
            • +
            • [ZOOKEEPER-1821] - very ugly warning when compiling load_gen.c +
            • +
            • [ZOOKEEPER-1839] - Deadlock in NettyServerCnxn +
            • +
            • [ZOOKEEPER-1844] - TruncateTest fails on windows +
            • +
            • [ZOOKEEPER-1845] - FLETest.testLE fails on windows +
            • +
            • [ZOOKEEPER-1850] - cppunit test testNonexistingHost in TestZookeeperInit is failing on Unbuntu +
            • +
            • [ZOOKEEPER-2015] - I found memory leak in zk client for c++ +
            • +
            • [ZOOKEEPER-2236] - Zookeeper truncates file to 0bytes +
            • +
            • [ZOOKEEPER-2327] - "ConnectionLoss for /dog" +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-1019] - zkfuse doesn't list dependency on boost in README +
            • +
            • [ZOOKEEPER-1096] - Leader communication should listen on specified IP, not wildcard address +
            • +
            • [ZOOKEEPER-1324] - Remove Duplicate NEWLEADER packets from the Leader to the Follower. +
            • +
            • [ZOOKEEPER-1552] - Enable sync request processor in Observer +
            • +
            • [ZOOKEEPER-1564] - Allow JUnit test build with IBM Java +
            • +
            • [ZOOKEEPER-1583] - Document maxClientCnxns in conf/zoo_sample.cfg +
            • +
            • [ZOOKEEPER-1584] - Adding mvn-install target for deploying the zookeeper artifacts to .m2 repository. +
            • +
            • [ZOOKEEPER-1598] - Ability to support more digits in the version string +
            • +
            • [ZOOKEEPER-1615] - minor typos in ZooKeeper Programmer's Guide web page +
            • +
            • [ZOOKEEPER-1627] - Add org.apache.zookeeper.common to exported packages in OSGi MANIFEST headers +
            • +
            • [ZOOKEEPER-1666] - Avoid Reverse DNS lookup if the hostname in connection string is literal IP address. +
            • +
            • [ZOOKEEPER-1715] - Upgrade netty version +
            • +
            • [ZOOKEEPER-1758] - Add documentation for zookeeper.observer.syncEnabled flag +
            • +
            • [ZOOKEEPER-1771] - ZooInspector authentication +
            • +
            • [ZOOKEEPER-2313] - Refactor ZooKeeperServerBean and its subclasses (LeaderBean, ObserverBean, FollowerBean) +
            • +
            + +

            Task +

            + + +

            Test +

            +
              +
            • [ZOOKEEPER-1980] - how to draw the figure"ZooKeeper Throughput as the Read-Write Ratio Varies" ? +
            • +
            -
            Changes Since ZooKeeper 3.4.3
            IssueNotes
            - - ZOOKEEPER-1496 - -Ephemeral node not getting cleared even after client has exited -
            - - ZOOKEEPER-1048 - -addauth command does not work in cli_mt/cli_st -
            - - ZOOKEEPER-1163 - -Memory leak in zk_hashtable.c:do_insert_watcher_object() -
            - - ZOOKEEPER-1210 - -Can't build ZooKeeper RPM with RPM > -
            - - ZOOKEEPER-1236 - -Security uses proprietary Sun APIs -
            - - ZOOKEEPER-1256 - -ClientPortBindTest is failing on Mac OS X -
            - - ZOOKEEPER-1277 - -servers stop serving when lower 32bits of zxid roll over -
            - - ZOOKEEPER-1307 - -zkCli.sh is exiting when an Invalid ACL exception is thrown from setACL command through client -
            - - ZOOKEEPER-1318 - -In Python binding, get_children (and get and exists, and probably others) with expired session doesn't raise exception properly -
            - - ZOOKEEPER-1339 - -C clien doesn't build with --enable-debug -
            - - ZOOKEEPER-1344 - -ZooKeeper client multi-update command is not considering the Chroot request -
            - - ZOOKEEPER-1354 - -AuthTest.testBadAuthThenSendOtherCommands fails intermittently -
            - - ZOOKEEPER-1361 - -Leader.lead iterates over 'learners' set without proper synchronisation -
            - - ZOOKEEPER-1380 - -zkperl: _zk_release_watch doesn't remove items properly from the watch list -
            - - ZOOKEEPER-1384 - -test-cppunit overrides LD_LIBRARY_PATH and fails if gcc is in non-standard location -
            - - ZOOKEEPER-1386 - -avoid flaky URL redirection in "ant javadoc" : replace "http://java.sun.com/javase/6/docs/api/" with "http://download.oracle.com/javase/6/docs/api/" -
            - - ZOOKEEPER-1395 - -node-watcher double-free redux -
            - - ZOOKEEPER-1403 - -zkCli.sh script quoting issue -
            - - ZOOKEEPER-1406 - -dpkg init scripts don't restart - missing check_priv_sep_dir -
            - - ZOOKEEPER-1412 - -java client watches inconsistently triggered on reconnect -
            - - ZOOKEEPER-1419 - -Leader election never settles for a 5-node cluster -
            - - ZOOKEEPER-1427 - -Writing to local files is done non-atomically -
            - - ZOOKEEPER-1431 - -zkpython: async calls leak memory -
            - - ZOOKEEPER-1437 - -Client uses session before SASL authentication complete -
            - - ZOOKEEPER-1463 - -external inline function is not compatible with C99 -
            - - ZOOKEEPER-1465 - -Cluster availability following new leader election takes a long time with large datasets - is correlated to dataset size -
            - - ZOOKEEPER-1466 - -QuorumCnxManager.shutdown missing synchronization -
            - - ZOOKEEPER-1471 - -Jute generates invalid C++ code -
            - - ZOOKEEPER-1480 - -ClientCnxn(1161) can't get the current zk server add, so that - Session 0x for server null, unexpected error -
            - - ZOOKEEPER-1483 - -Fix leader election recipe documentation -
            - - ZOOKEEPER-1489 - -Data loss after truncate on transaction log -
            - - ZOOKEEPER-1490 - -If the configured log directory does not exist zookeeper will not start. Better to create the directory and start -
            - - ZOOKEEPER-1493 - -C Client: zookeeper_process doesn't invoke completion callback if zookeeper_close has been called -
            - - ZOOKEEPER-1494 - -C client: socket leak after receive timeout in zookeeper_interest() -
            - - ZOOKEEPER-1496 - -Ephemeral node not getting cleared even after client has exited -
            - - ZOOKEEPER-1501 - -Nagios plugin always returns OK when it cannot connect to zookeeper -
            - - ZOOKEEPER-1514 - -FastLeaderElection - leader ignores the round information when joining a quorum -
            - - ZOOKEEPER-1521 - -LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits -
            - - ZOOKEEPER-1522 - -intermittent failures in Zab test due to NPE in recursiveDelete test function -
            - - ZOOKEEPER-1536 - -c client : memory leak in winport.c -
            - - ZOOKEEPER-1321 - -Add number of client connections metric in JMX and srvr -
            - - ZOOKEEPER-1377 - -add support for dumping a snapshot file content (similar to LogFormatter) -
            - - ZOOKEEPER-1389 - -it would be nice if start-foreground used exec $JAVA in order to get rid of the intermediate shell process -
            - - ZOOKEEPER-1390 - -some expensive debug code not protected by a check for debug -
            - - ZOOKEEPER-1433 - -improve ZxidRolloverTest (test seems flakey) -
            - - ZOOKEEPER-1454 - -Document how to run autoreconf if cppunit is installed in a non-standard directory -
            - - ZOOKEEPER-1481 - -allow the C cli to run exists with a watcher -
            - - ZOOKEEPER-1497 - -Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) -
            - - ZOOKEEPER-1503 - -remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest -
            - - ZOOKEEPER-1510 - -Should not log SASL errors for non-secure usage -
            - - ZOOKEEPER-1450 - -Backport ZOOKEEPER-1294 fix to 3.4 and 3.3 -
            -
            - - - -

            Changes Since ZooKeeper 3.4.2

            -
            - - - -Changes Since ZooKeeper 3.4.2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Release Notes - ZooKeeper - Version 3.4.5 + +

            Bug +

            +
              +
            • [ZOOKEEPER-1376] - zkServer.sh does not correctly check for $SERVER_JVMFLAGS +
            • +
            • [ZOOKEEPER-1550] - ZooKeeperSaslClient does not finish anonymous login on OpenJDK +
            • +
            • [ZOOKEEPER-1560] - Zookeeper client hangs on creation of large nodes +
            • +
            • [ZOOKEEPER-1686] - Publish ZK 3.4.5 test jar +
            • +
            + +

            Improvement +

            + - + Release Notes - ZooKeeper - Version 3.4.4 + +

            Bug +

            +
              +
            • [ZOOKEEPER-1048] - addauth command does not work in cli_mt/cli_st +
            • +
            • [ZOOKEEPER-1163] - Memory leak in zk_hashtable.c:do_insert_watcher_object() +
            • +
            • [ZOOKEEPER-1210] - Can't build ZooKeeper RPM with RPM >= 4.6.0 (i.e. on RHEL 6 and Fedora >= 10) +
            • +
            • [ZOOKEEPER-1236] - Security uses proprietary Sun APIs +
            • +
            • [ZOOKEEPER-1277] - servers stop serving when lower 32bits of zxid roll over +
            • +
            • [ZOOKEEPER-1303] - Observer LearnerHandlers are not removed from Leader collection. +
            • +
            • [ZOOKEEPER-1307] - zkCli.sh is exiting when an Invalid ACL exception is thrown from setACL command through client +
            • +
            • [ZOOKEEPER-1318] - In Python binding, get_children (and get and exists, and probably others) with expired session doesn't raise exception properly +
            • +
            • [ZOOKEEPER-1339] - C clien doesn't build with --enable-debug +
            • +
            • [ZOOKEEPER-1344] - ZooKeeper client multi-update command is not considering the Chroot request +
            • +
            • [ZOOKEEPER-1354] - AuthTest.testBadAuthThenSendOtherCommands fails intermittently +
            • +
            • [ZOOKEEPER-1361] - Leader.lead iterates over 'learners' set without proper synchronisation +
            • +
            • [ZOOKEEPER-1380] - zkperl: _zk_release_watch doesn't remove items properly from the watch list +
            • +
            • [ZOOKEEPER-1384] - test-cppunit overrides LD_LIBRARY_PATH and fails if gcc is in non-standard location +
            • +
            • [ZOOKEEPER-1386] - avoid flaky URL redirection in "ant javadoc" : replace "http://java.sun.com/javase/6/docs/api/" with "http://download.oracle.com/javase/6/docs/api/" +
            • +
            • [ZOOKEEPER-1395] - node-watcher double-free redux +
            • +
            • [ZOOKEEPER-1403] - zkCli.sh script quoting issue +
            • +
            • [ZOOKEEPER-1406] - dpkg init scripts don't restart - missing check_priv_sep_dir +
            • +
            • [ZOOKEEPER-1412] - java client watches inconsistently triggered on reconnect +
            • +
            • [ZOOKEEPER-1419] - Leader election never settles for a 5-node cluster +
            • +
            • [ZOOKEEPER-1427] - Writing to local files is done non-atomically +
            • +
            • [ZOOKEEPER-1431] - zkpython: async calls leak memory +
            • +
            • [ZOOKEEPER-1437] - Client uses session before SASL authentication complete +
            • +
            • [ZOOKEEPER-1463] - external inline function is not compatible with C99 +
            • +
            • [ZOOKEEPER-1465] - Cluster availability following new leader election takes a long time with large datasets - is correlated to dataset size +
            • +
            • [ZOOKEEPER-1466] - QuorumCnxManager.shutdown missing synchronization +
            • +
            • [ZOOKEEPER-1471] - Jute generates invalid C++ code +
            • +
            • [ZOOKEEPER-1483] - Fix leader election recipe documentation +
            • +
            • [ZOOKEEPER-1489] - Data loss after truncate on transaction log +
            • +
            • [ZOOKEEPER-1490] - If the configured log directory does not exist zookeeper will not start. Better to create the directory and start +
            • +
            • [ZOOKEEPER-1493] - C Client: zookeeper_process doesn't invoke completion callback if zookeeper_close has been called +
            • +
            • [ZOOKEEPER-1494] - C client: socket leak after receive timeout in zookeeper_interest() +
            • +
            • [ZOOKEEPER-1496] - Ephemeral node not getting cleared even after client has exited +
            • +
            • [ZOOKEEPER-1501] - Nagios plugin always returns OK when it cannot connect to zookeeper +
            • +
            • [ZOOKEEPER-1514] - FastLeaderElection - leader ignores the round information when joining a quorum +
            • +
            • [ZOOKEEPER-1521] - LearnerHandler initLimit/syncLimit problems specifying follower socket timeout limits +
            • +
            • [ZOOKEEPER-1522] - intermittent failures in Zab test due to NPE in recursiveDelete test function +
            • +
            • [ZOOKEEPER-1536] - c client : memory leak in winport.c +
            • +
            • [ZOOKEEPER-1686] - Publish ZK 3.4.5 test jar +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-1321] - Add number of client connections metric in JMX and srvr +
            • +
            • [ZOOKEEPER-1377] - add support for dumping a snapshot file content (similar to LogFormatter) +
            • +
            • [ZOOKEEPER-1389] - it would be nice if start-foreground used exec $JAVA in order to get rid of the intermediate shell process +
            • +
            • [ZOOKEEPER-1390] - some expensive debug code not protected by a check for debug +
            • +
            • [ZOOKEEPER-1433] - improve ZxidRolloverTest (test seems flakey) +
            • +
            • [ZOOKEEPER-1454] - Document how to run autoreconf if cppunit is installed in a non-standard directory +
            • +
            • [ZOOKEEPER-1481] - allow the C cli to run exists with a watcher +
            • +
            • [ZOOKEEPER-1497] - Allow server-side SASL login with JAAS configuration to be programmatically set (rather than only by reading JAAS configuration file) +
            • +
            • [ZOOKEEPER-1503] - remove redundant JAAS configuration code in SaslAuthTest and SaslAuthFailTest +
            • +
            • [ZOOKEEPER-1510] - Should not log SASL errors for non-secure usage +
            • +
            • [ZOOKEEPER-1565] - Allow ClientTest.java build with IBM Java +
            • +
            • [ZOOKEEPER-1570] - Allow QuorumBase.java build with IBM Java +
            • +
            • [ZOOKEEPER-1571] - Allow QuorumUtil.java build with IBM Java +
            • +
            + +

            Task +

            + - - - - + Release Notes - ZooKeeper - Version 3.4.3 + +

            Bug +

            +
              +
            • [ZOOKEEPER-973] - bind() could fail on Leader because it does not setReuseAddress on its ServerSocket +
            • +
            • [ZOOKEEPER-1089] - zkServer.sh status does not work due to invalid option of nc +
            • +
            • [ZOOKEEPER-1327] - there are still remnants of hadoop urls +
            • +
            • [ZOOKEEPER-1336] - javadoc for multi is confusing, references functionality that doesn't seem to exist +
            • +
            • [ZOOKEEPER-1338] - class cast exceptions may be thrown by multi ErrorResult class (invalid equals) +
            • +
            • [ZOOKEEPER-1340] - multi problem - typical user operations are generating ERROR level messages in the server +
            • +
            • [ZOOKEEPER-1343] - getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch +
            • +
            • [ZOOKEEPER-1348] - Zookeeper 3.4.2 C client incorrectly reports string version of 3.4.1 +
            • +
            • [ZOOKEEPER-1351] - invalid test verification in MultiTransactionTest +
            • +
            • [ZOOKEEPER-1352] - server.InvalidSnapshotTest is using connection timeouts that are too short +
            • +
            • [ZOOKEEPER-1353] - C client test suite fails consistently +
            • +
            • [ZOOKEEPER-1367] - Data inconsistencies and unexpired ephemeral nodes after cluster restart +
            • +
            • [ZOOKEEPER-1370] - Add logging changes in Release Notes needed for clients because of ZOOKEEPER-850. +
            • +
            • [ZOOKEEPER-1373] - Hardcoded SASL login context name clashes with Hadoop security configuration override +
            • +
            • [ZOOKEEPER-1374] - C client multi-threaded test suite fails to compile on ARM architectures. +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-1322] - Cleanup/fix logging in Quorum code. +
            • +
            • [ZOOKEEPER-1345] - Add a .gitignore file with general exclusions and Eclipse project files excluded +
            • +
            + +

            Test +

            +
              +
            • [ZOOKEEPER-1337] - multi's "Transaction" class is missing tests. +
            • +
            - + Release Notes - ZooKeeper - Version 3.4.2 + +

            Bug +

            + - - - - + Release Notes - ZooKeeper - Version 3.4.1 - +

            Bug +

            + - - - - - + Release Notes - ZooKeeper - Version 3.4.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.2
            IssueNotes
            - - ZOOKEEPER-1089 - -zkServer.sh status does not work due to invalid option of nc. -
            - - ZOOKEEPER-1345 - -Add a .gitignore file with general exclusions and Eclipse project files excluded. -
            - - ZOOKEEPER-1343 - -getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch. -
            - - ZOOKEEPER-850 - - replaces log4j with slf4j code (also in contrib for bookkeeper, zooinspector, - rest,loggraph), added slf4j dependencies into several ivy.xml files. - You must add slf4j-api-1.6.1.jar and slf4j-log4j12-1.6.1.jar (bridge from sl4j - to log4j) to the classpath, if not using the standard scripts. - log4j remains as the final logger yet, there is still work to do: - remove programmatic access to the log4j api from certain classes - (which add appenders or configure log4j at runtime), or move them to contrib -
            - - ZOOKEEPER-1358 - -In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) - doesn't sleep by checking that the latency of this call is less than 10sec -
            - - ZOOKEEPER-1351 - -Invalid test verification in MultiTransactionTest. -
            - - ZOOKEEPER-973 - -bind() could fail on Leader because it does not setReuseAddress on its ServerSocket. -
            - - ZOOKEEPER-1367 - - Data inconsistencies and unexpired ephemeral nodes after cluster restart. -
            - - ZOOKEEPER-1353 - - C client test suite fails consistently. -
            - - ZOOKEEPER-1373 - - Hardcoded SASL login context name clashes with Hadoop security - configuration override. -
            - - ZOOKEEPER-1352 - - server.InvalidSnapshotTest is using connection timeouts that - are too short. -
            - - ZOOKEEPER-1336 - - javadoc for multi is confusing, references functionality that doesn't - seem to exis -
            - - ZOOKEEPER-1340 - - multi problem - typical user operations are - generating ERROR level messages in the server. -
            - - ZOOKEEPER-1374 - - C client multi-threaded test suite fails to compile on ARM architectures. -
            - - ZOOKEEPER-1337 - - multi's "Transaction" class is missing tests. -
            - - ZOOKEEPER-1338 - - class cast exceptions may be thrown by multi ErrorResult class (invalid equals) -
            - - ZOOKEEPER-1322 - -Cleanup/fix logging in Quorum code. -
            - - ZOOKEEPER-1327 - -There are still remnants of hadoop urls. -
            -
            - - - -

            Changes Since ZooKeeper 3.4.1

            -
            - - - -Changes Since ZooKeeper 3.4.1 - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.1
            IssueNotes
            - - ZOOKEEPER-1333 - -NPE in FileTxnSnapLog when restarting a cluster. -
            - - ZOOKEEPER-1323 - -c client doesn't compile on freebsd -
            -
            - - - -

            Changes Since ZooKeeper 3.4.0

            -
            - - - -Changes Since ZooKeeper 3.4.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.4.0
            IssueNotes
            - - ZOOKEEPER-1311 - -ZooKeeper test jar is broken -
            - - ZOOKEEPER-1305 - -zookeeper.c:prepend_string func can dereference null ptr -
            - - ZOOKEEPER-1316 - -zookeeper_init leaks memory if chroot is just '/' -
            - - ZOOKEEPER-1315 - - zookeeper_init always reports sessionPasswd=hidden -
            - - ZOOKEEPER-1317 - - Possible segfault in zookeeper_init. -
            - - ZOOKEEPER-1319 - - Missing data after restarting+expanding a cluster. -
            - - ZOOKEEPER-1269 - - Multi deserialization issues. -
            -
            - - - -

            Changes Since ZooKeeper 3.3.0

            -
            - - - -Changes Since ZooKeeper 3.3.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Changes Since ZooKeeper 3.3.0
            IssueNotes
            - Sub-Tasks - - -
            - - ZOOKEEPER-1239 - - add logging/stats to identify fsync stalls. -
            - - ZOOKEEPER-1208 - -Ephemeral node not removed after the client session is long gone. -
            - - ZOOKEEPER-784 - - server-side functionality for read-only mode. This is not thoroughly tested. - Avoid using it in production. This is also at risk of being removed from - the feature set later. -
            - - ZOOKEEPER-798 - - Fixup loggraph for FLE changes -
            - - ZOOKEEPER-839 - - deleteRecursive does not belong to the other methods -
            - - ZOOKEEPER-908 - - Remove code duplication and inconsistent naming in ClientCnxn.Packet creation -
            - - ZOOKEEPER-909 - - Extract NIO specific code from ClientCnxn -
            - - ZOOKEEPER-966 - - Client side for multi -
            - - ZOOKEEPER-967 - - Server side decoding and function dispatch -
            - - ZOOKEEPER-968 - - Database multi-update -
            - - ZOOKEEPER-1042 - - Generate zookeeper test jar for maven installation -
            - - ZOOKEEPER-1081 - - modify leader/follower code to correctly deal with new leader -
            - - ZOOKEEPER-1082 - - modify leader election to correctly take into account current epoch -
            - - ZOOKEEPER-1150 - - fix for this patch to compile on windows... -
            - - ZOOKEEPER-1160 - - test timeouts are too small -
            - - ZOOKEEPER-1201 - - Clean SaslServerCallbackHandler.java -
            - Bug Fixes - - -
            - - ZOOKEEPER-1268 - - problems with read only mode, intermittent test failures and ERRORs in the log. -
            - - ZOOKEEPER-1271 - - testEarlyLeaderAbandonment failing on solaris - clients not retrying connection. -
            - - ZOOKEEPER-1192 - -Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished. -
            - - ZOOKEEPER-1246 - - Dead code in PrepRequestProcessor catch Exception block. -
            - - ZOOKEEPER-1264 - - FollowerResyncConcurrencyTest failing intermittently. -
            - - ZOOKEEPER-1270 - - testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. -
            - - ZOOKEEPER-1291 - - AcceptedEpoch not updated at leader before it proposes the epoch to followers. -
            - - ZOOKEEPER-1282 - -Learner.java not following Zab 1.0 protocol - setCurrentEpoch should be done upon receipt of NEWLEADER - (before acking it) and not upon receipt of UPTODATE. -
            - - ZOOKEEPER-335 - - zookeeper servers should commit the new leader txn to their logs. -
            - - ZOOKEEPER-418 - - Need nifty zookeeper browser -
            - - ZOOKEEPER-603 - - zkpython should do a better job of freeing memory under error conditions -
            - - ZOOKEEPER-662 - - Too many CLOSE_WAIT socket state on a server -
            - - ZOOKEEPER-690 - - AsyncTestHammer test fails on hudson. -
            - - ZOOKEEPER-719 - - Add throttling to BookKeeper client -
            - - ZOOKEEPER-720 - - Use zookeeper-{version}-sources.jar instead of zookeeper-{version}-src.jar to publish sources in the Maven repository -
            - - ZOOKEEPER-722 - - zkServer.sh uses sh's builtin echo on BSD, behaves incorrectly. -
            - - ZOOKEEPER-731 - - Zookeeper#delete , #create - async versions miss a verb in the javadoc -
            - - ZOOKEEPER-734 - - QuorumPeerTestBase.java and ZooKeeperServerMainTest.java do not handle windows path correctly -
            - - ZOOKEEPER-735 - - cppunit test testipv6 assumes that the machine is ipv6 enabled. -
            - - ZOOKEEPER-737 - - some 4 letter words may fail with netcat (nc) -
            - - ZOOKEEPER-738 - - zookeeper.jute.h fails to compile with -pedantic -
            - - ZOOKEEPER-741 - - root level create on REST proxy fails -
            - - ZOOKEEPER-742 - - Deallocatng None on writes -
            - - ZOOKEEPER-746 - - learner outputs session id to log in dec (should be hex) -
            - - ZOOKEEPER-749 - - OSGi metadata not included in binary only jar -
            - - ZOOKEEPER-750 - - move maven artifacts into "dist-maven" subdir of the release (package target) -
            - - ZOOKEEPER-758 - - zkpython segfaults on invalid acl with missing key -
            - - ZOOKEEPER-763 - - Deadlock on close w/ zkpython / c client -
            - - ZOOKEEPER-764 - - Observer elected leader due to inconsistent voting view -
            - - ZOOKEEPER-766 - - forrest recipes docs don't mention the lock/queue recipe implementations available in the release -
            - - ZOOKEEPER-769 - - Leader can treat observers as quorum members -
            - - ZOOKEEPER-772 - - zkpython segfaults when watcher from async get children is invoked. -
            - - ZOOKEEPER-774 - - Recipes tests are slightly outdated: they do not compile against JUnit 4.8 -
            - - ZOOKEEPER-777 - - setting acl on a non existant node should return no node error -
            - - ZOOKEEPER-782 - - Incorrect C API documentation for Watches -
            - - ZOOKEEPER-783 - - committedLog in ZKDatabase is not properly synchronized -
            - - ZOOKEEPER-787 - - groupId in deployed pom is wrong -
            - - ZOOKEEPER-790 - - Last processed zxid set prematurely while establishing leadership -
            - - ZOOKEEPER-792 - - zkpython memory leak -
            - - ZOOKEEPER-794 - - Callbacks are not invoked when the client is closed -
            - - ZOOKEEPER-795 - - eventThread isn't shutdown after a connection "session expired" event coming -
            - - ZOOKEEPER-796 - - zkServer.sh should support an external PIDFILE variable -
            - - ZOOKEEPER-800 - - zoo_add_auth returns ZOK if zookeeper handle is in ZOO_CLOSED_STATE -
            - - ZOOKEEPER-804 - - c unit tests failing due to "assertion cptr failed" -
            - - ZOOKEEPER-813 - - maven install is broken due to incorrect organisation -
            - - ZOOKEEPER-814 - - monitoring scripts are missing apache license headers -
            - - ZOOKEEPER-820 - - update c unit tests to ensure "zombie" java server processes don't cause failure -
            - - ZOOKEEPER-822 - - Leader election taking a long time to complete -
            - - ZOOKEEPER-831 - - BookKeeper: Throttling improved for reads -
            - - ZOOKEEPER-844 - - handle auth failure in java client -
            - - ZOOKEEPER-846 - - zookeeper client doesn't shut down cleanly on the close call -
            - - ZOOKEEPER-854 - - BookKeeper does not compile due to changes in the ZooKeeper code -
            - - ZOOKEEPER-855 - - clientPortBindAddress should be clientPortAddress -
            - - ZOOKEEPER-861 - - Missing the test SSL certificate used for running junit tests. -
            - - ZOOKEEPER-867 - - ClientTest is failing on hudson - fd cleanup -
            - - ZOOKEEPER-870 - - Zookeeper trunk build broken. -
            - - ZOOKEEPER-874 - - FileTxnSnapLog.restore does not call listener -
            - - ZOOKEEPER-880 - - QuorumCnxManager$SendWorker grows without bounds -
            - - ZOOKEEPER-881 - - ZooKeeperServer.loadData loads database twice -
            - - ZOOKEEPER-882 - - Startup loads last transaction from snapshot -
            - - ZOOKEEPER-884 - - Remove LedgerSequence references from BookKeeper documentation and comments in tests -
            - - ZOOKEEPER-888 - - c-client / zkpython: Double free corruption on node watcher -
            - - ZOOKEEPER-893 - - ZooKeeper high cpu usage when invalid requests -
            - - ZOOKEEPER-897 - - C Client seg faults during close -
            - - ZOOKEEPER-898 - - C Client might not cleanup correctly during close -
            - - ZOOKEEPER-902 - - Fix findbug issue in trunk "Malicious code vulnerability" -
            - - ZOOKEEPER-904 - - super digest is not actually acting as a full superuser -
            - - ZOOKEEPER-913 - - Version parser fails to parse "3.3.2-dev" from build.xml. -
            - - ZOOKEEPER-917 - - Leader election selected incorrect leader -
            - - ZOOKEEPER-919 - - Ephemeral nodes remains in one of ensemble after deliberate SIGKILL -
            - - ZOOKEEPER-921 - - zkPython incorrectly checks for existence of required ACL elements -
            - - ZOOKEEPER-937 - - test -e not available on solaris /bin/sh -
            - - ZOOKEEPER-957 - - zkCleanup.sh doesn't do anything -
            - - ZOOKEEPER-958 - - Flag to turn off autoconsume in hedwig c++ client -
            - - ZOOKEEPER-961 - - Watch recovery after disconnection when connection string contains a prefix -
            - - ZOOKEEPER-962 - - leader/follower coherence issue when follower is receiving a DIFF -
            - - ZOOKEEPER-963 - - Make Forrest work with JDK6 -
            - - ZOOKEEPER-965 - - Need a multi-update command to allow multiple znodes to be updated safely -
            - - ZOOKEEPER-975 - - new peer goes in LEADING state even if ensemble is online -
            - - ZOOKEEPER-976 - - ZooKeeper startup script doesn't use JAVA_HOME -
            - - ZOOKEEPER-981 - - Hang in zookeeper_close() in the multi-threaded C client -
            - - ZOOKEEPER-983 - - running zkServer.sh start remotely using ssh hangs -
            - - ZOOKEEPER-985 - - Test BookieRecoveryTest fails on trunk. -
            - - ZOOKEEPER-1006 - - QuorumPeer "Address already in use" -- regression in 3.3.3 -
            - - ZOOKEEPER-1007 - - iarchive leak in C client -
            - - ZOOKEEPER-1013 - - zkServer.sh usage message should mention all startup options -
            - - ZOOKEEPER-1027 - - chroot not transparent in zoo_create() -
            - - ZOOKEEPER-1028 - - In python bindings, zookeeper.set2() should return a stat dict but instead returns None -
            - - ZOOKEEPER-1033 - - c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src -
            - - ZOOKEEPER-1034 - - perl bindings should automatically find the zookeeper c-client headers -
            - - ZOOKEEPER-1046 - - Creating a new sequential node results in a ZNODEEXISTS error -
            - - ZOOKEEPER-1049 - - Session expire/close flooding renders heartbeats to delay significantly -
            - - ZOOKEEPER-1051 - - SIGPIPE in Zookeeper 0.3.* when send'ing after cluster disconnection -
            - - ZOOKEEPER-1052 - - Findbugs warning in QuorumPeer.ResponderThread.run() -
            - - ZOOKEEPER-1055 - - check for duplicate ACLs in addACL() and create() -
            - - ZOOKEEPER-1058 - - fix typo in opToString for getData -
            - - ZOOKEEPER-1059 - - stat command isses on non-existing node causes NPE -
            - - ZOOKEEPER-1060 - - QuorumPeer takes a long time to shutdown -
            - - ZOOKEEPER-1061 - - Zookeeper stop fails if start called twice -
            - - ZOOKEEPER-1063 - - Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes -
            - - ZOOKEEPER-1068 - - Documentation and default config suggest incorrect location for Zookeeper state -
            - - ZOOKEEPER-1069 - - Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log -
            - - ZOOKEEPER-1073 - - address a documentation issue in ZOOKEEPER-1030 -
            - - ZOOKEEPER-1074 - - zkServer.sh is missing nohup/sleep, which are necessary for remote invocation -
            - - ZOOKEEPER-1076 - - some quorum tests are unnecessarily extending QuorumBase -
            - - ZOOKEEPER-1083 - - Javadoc for WatchedEvent not being generated -
            - - ZOOKEEPER-1086 - - zookeeper test jar has non mavenised dependency. -
            - - ZOOKEEPER-1087 - - ForceSync VM arguement not working when set to "no" -
            - - ZOOKEEPER-1088 - - delQuota does not remove the quota node and subesquent setquota calls for that path will fail -
            - - ZOOKEEPER-1090 - - Race condition while taking snapshot can lead to not restoring data tree correctly -
            - - ZOOKEEPER-1091 - - when the chrootPath of ClientCnxn is not null and the Watches of zooKeeper is not null and the method primeConnection(SelectionKey k) of ClientCnxn Occurred again for some reason ,then the wrong watcher clientPath is sended to server -
            - - ZOOKEEPER-1097 - - Quota is not correctly rehydrated on snapshot reload -
            - - ZOOKEEPER-1101 - - Upload zookeeper-test maven artifacts to maven repository. -
            - - ZOOKEEPER-1108 - - Various bugs in zoo_add_auth in C -
            - - ZOOKEEPER-1109 - - Zookeeper service is down when SyncRequestProcessor meets any exception. -
            - - ZOOKEEPER-1111 - - JMXEnv uses System.err instead of logging -
            - - ZOOKEEPER-1119 - - zkServer stop command incorrectly reading comment lines in zoo.cfg -
            - - ZOOKEEPER-1124 - - Multiop submitted to non-leader always fails due to timeout -
            - - ZOOKEEPER-1136 - - NEW_LEADER should be queued not sent to match the Zab 1.0 protocol on the twiki -
            - - ZOOKEEPER-1138 - - release audit failing for a number of new files -
            - - ZOOKEEPER-1139 - - jenkins is reporting two warnings, fix these -
            - - ZOOKEEPER-1140 - - server shutdown is not stopping threads -
            - - ZOOKEEPER-1141 - - zkpython fails tests under python 2.4 -
            - - ZOOKEEPER-1142 - - incorrect stat output -
            - - ZOOKEEPER-1144 - - ZooKeeperServer not starting on leader due to a race condition -
            - - ZOOKEEPER-1145 - - ObserverTest.testObserver fails at particular point after several runs of ant junt.run -Dtestcase -
            - - ZOOKEEPER-1146 - - significant regression in client (c/python) performance -
            - - ZOOKEEPER-1152 - - Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer -
            - - ZOOKEEPER-1154 - - Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election -
            - - ZOOKEEPER-1156 - - Log truncation truncating log too much - can cause data loss -
            - - ZOOKEEPER-1165 - - better eclipse support in tests -
            - - ZOOKEEPER-1168 - - ZooKeeper fails to run with IKVM -
            - - ZOOKEEPER-1171 - - fix build for java 7 -
            - - ZOOKEEPER-1174 - - FD leak when network unreachable -
            - - ZOOKEEPER-1181 - - Fix problems with Kerberos TGT renewal -
            - - ZOOKEEPER-1185 - - Send AuthFailed event to client if SASL authentication fails -
            - - ZOOKEEPER-1189 - - For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. -
            - - ZOOKEEPER-1190 - - ant package is not including many of the bin scripts in the package (zkServer.sh for example) -
            - - ZOOKEEPER-1195 - - SASL authorizedID being incorrectly set: should use getHostName() rather than getServiceName() -
            - - ZOOKEEPER-1203 - - Zookeeper systest is missing Junit Classes -
            - - ZOOKEEPER-1206 - - Sequential node creation does not use always use digits in node name given certain Locales. -
            - - ZOOKEEPER-1212 - - zkServer.sh stop action is not conformat with LSB para 20.2 Init Script Actions -
            - - ZOOKEEPER-1237 - - ERRORs being logged when queued responses are sent after socket has closed. -
            - Improvements - - -
            - - ZOOKEEPER-494 - - zookeeper should install include headers in /usr/local/include/zookeeper -
            - - ZOOKEEPER-500 - - Async methods shouldnt throw exceptions -
            - - ZOOKEEPER-631 - - zkpython's C code could do with a style clean-up -
            - - ZOOKEEPER-636 - - configure.ac has instructions which override the contents of CFLAGS and CXXFLAGS. -
            - - ZOOKEEPER-724 - - Improve junit test integration - log harness information -
            - - ZOOKEEPER-733 - - use netty to handle client connections -
            - - ZOOKEEPER-765 - - Add python example script -
            - - ZOOKEEPER-773 - - Log visualisation -
            - - ZOOKEEPER-788 - - Add server id to message logs -
            - - ZOOKEEPER-789 - - Improve FLE log messages -
            - - ZOOKEEPER-797 - - c client source with AI_ADDRCONFIG cannot be compiled with early glibc -
            - - ZOOKEEPER-809 - - Improved REST Interface -
            - - ZOOKEEPER-821 - - Add ZooKeeper version information to zkpython -
            - - ZOOKEEPER-853 - - Make zookeeper.is_unrecoverable return True or False and not an integer -
            - - ZOOKEEPER-862 - - Hedwig created ledgers with hardcoded Bookkeeper ensemble and quorum size. Make these a server config parameter instead. -
            - - ZOOKEEPER-864 - - Hedwig C++ client improvements -
            - - ZOOKEEPER-891 - - Allow non-numeric version strings -
            - - ZOOKEEPER-905 - - enhance zkServer.sh for easier zookeeper automation-izing -
            - - ZOOKEEPER-926 - - Fork Hadoop common's test-patch.sh and modify for Zookeeper -
            - - ZOOKEEPER-977 - - passing null for path_buffer in zoo_create -
            - - ZOOKEEPER-980 - - allow configuration parameters for log4j.properties -
            - - ZOOKEEPER-993 - - Code improvements -
            - - ZOOKEEPER-997 - - ZkClient ignores command if there are any space in front of it -
            - - ZOOKEEPER-1018 - - The connection permutation in get_addrs uses a weak and inefficient shuffle -
            - - ZOOKEEPER-1025 - - zkCli is overly sensitive to to spaces. -
            - - ZOOKEEPER-1030 - - Increase default for maxClientCnxns -
            - - ZOOKEEPER-1094 - - Small improvements to LeaderElection and Vote classes -
            - - ZOOKEEPER-1095 - - Simple leader election recipe -
            - - ZOOKEEPER-1103 - - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. -
            - - ZOOKEEPER-1104 - - CLONE - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. -
            - - ZOOKEEPER-1143 - - quorum send & recv workers are missing thread names -
            - - ZOOKEEPER-1153 - - Deprecate AuthFLE and LE -
            - - ZOOKEEPER-1166 - - Please add a few svn:ignore properties -
            - - ZOOKEEPER-1169 - - Fix compiler (eclipse) warnings in (generated) jute code -
            - - ZOOKEEPER-1243 - - New 4lw for short simple monitoring ldck -
            - Features - - -
            - - ZOOKEEPER-464 - - Need procedure to garbage collect ledgers -
            - - ZOOKEEPER-465 - - Ledger size in bytes -
            - - ZOOKEEPER-546 - - add "diskless" ensemble support -
            - - ZOOKEEPER-712 - - Bookie recovery -
            - - ZOOKEEPER-729 - - Recursively delete a znode - zkCli.sh rmr /node -
            - - ZOOKEEPER-744 - - Add monitoring four-letter word -
            - - ZOOKEEPER-747 - - Add C# generation to Jute -
            - - ZOOKEEPER-775 - - A large scale pub/sub system -
            - - ZOOKEEPER-799 - - Add tools and recipes for monitoring as a contrib -
            - - ZOOKEEPER-808 - - Web-based Administrative Interface -
            - - ZOOKEEPER-859 - - Native Windows version of C client -
            - - ZOOKEEPER-938 - - Support Kerberos authentication of clients. -
            - - ZOOKEEPER-992 - - MT Native Version of Windows C Client -
            - - ZOOKEEPER-999 - - Create an package integration project -
            - - ZOOKEEPER-1012 - - support distinct JVMFLAGS for zookeeper server in zkServer.sh and zookeeper client in zkCli.sh -
            - - ZOOKEEPER-1020 - - Implement function in C client to determine which host you're currently connected to. -
            - - ZOOKEEPER-1107 - - automating log and snapshot cleaning -
            - Tasks - - -
            - - ZOOKEEPER-754 - - numerous misspellings "succesfully" -
            - - ZOOKEEPER-1149 - - users cannot migrate from 3.4->3.3->3.4 server code against a single datadir -
            - Tests - - -
            - - ZOOKEEPER-239 - - ZooKeeper System Tests -
            -
            +

            Sub-task +

            +
              +
            • [ZOOKEEPER-784] - server-side functionality for read-only mode +
            • +
            • [ZOOKEEPER-798] - Fixup loggraph for FLE changes +
            • +
            • [ZOOKEEPER-839] - deleteRecursive does not belong to the other methods +
            • +
            • [ZOOKEEPER-908] - Remove code duplication and inconsistent naming in ClientCnxn.Packet creation +
            • +
            • [ZOOKEEPER-909] - Extract NIO specific code from ClientCnxn +
            • +
            • [ZOOKEEPER-966] - Client side for multi +
            • +
            • [ZOOKEEPER-967] - Server side decoding and function dispatch +
            • +
            • [ZOOKEEPER-968] - Database multi-update +
            • +
            • [ZOOKEEPER-1042] - Generate zookeeper test jar for maven installation +
            • +
            • [ZOOKEEPER-1081] - modify leader/follower code to correctly deal with new leader +
            • +
            • [ZOOKEEPER-1082] - modify leader election to correctly take into account current epoch +
            • +
            • [ZOOKEEPER-1150] - fix for this patch to compile on windows... +
            • +
            • [ZOOKEEPER-1160] - test timeouts are too small +
            • +
            • [ZOOKEEPER-1201] - Clean SaslServerCallbackHandler.java +
            • +
            • [ZOOKEEPER-1246] - Dead code in PrepRequestProcessor catch Exception block +
            • +
            • [ZOOKEEPER-1282] - Learner.java not following Zab 1.0 protocol - setCurrentEpoch should be done upon receipt of NEWLEADER (before acking it) and not upon receipt of UPTODATE +
            • +
            • [ZOOKEEPER-1291] - AcceptedEpoch not updated at leader before it proposes the epoch to followers +
            • +
            + +

            Bug +

            +
              +
            • [ZOOKEEPER-335] - zookeeper servers should commit the new leader txn to their logs. +
            • +
            • [ZOOKEEPER-418] - Need nifty zookeeper browser +
            • +
            • [ZOOKEEPER-603] - zkpython should do a better job of freeing memory under error conditions +
            • +
            • [ZOOKEEPER-662] - Too many CLOSE_WAIT socket state on a server +
            • +
            • [ZOOKEEPER-690] - AsyncTestHammer test fails on hudson. +
            • +
            • [ZOOKEEPER-719] - Add throttling to BookKeeper client +
            • +
            • [ZOOKEEPER-720] - Use zookeeper-{version}-sources.jar instead of zookeeper-{version}-src.jar to publish sources in the Maven repository +
            • +
            • [ZOOKEEPER-722] - zkServer.sh uses sh's builtin echo on BSD, behaves incorrectly. +
            • +
            • [ZOOKEEPER-731] - Zookeeper#delete , #create - async versions miss a verb in the javadoc +
            • +
            • [ZOOKEEPER-734] - QuorumPeerTestBase.java and ZooKeeperServerMainTest.java do not handle windows path correctly +
            • +
            • [ZOOKEEPER-735] - cppunit test testipv6 assumes that the machine is ipv6 enabled. +
            • +
            • [ZOOKEEPER-737] - some 4 letter words may fail with netcat (nc) +
            • +
            • [ZOOKEEPER-738] - zookeeper.jute.h fails to compile with -pedantic +
            • +
            • [ZOOKEEPER-741] - root level create on REST proxy fails +
            • +
            • [ZOOKEEPER-742] - Deallocatng None on writes +
            • +
            • [ZOOKEEPER-746] - learner outputs session id to log in dec (should be hex) +
            • +
            • [ZOOKEEPER-749] - OSGi metadata not included in binary only jar +
            • +
            • [ZOOKEEPER-750] - move maven artifacts into "dist-maven" subdir of the release (package target) +
            • +
            • [ZOOKEEPER-758] - zkpython segfaults on invalid acl with missing key +
            • +
            • [ZOOKEEPER-763] - Deadlock on close w/ zkpython / c client +
            • +
            • [ZOOKEEPER-764] - Observer elected leader due to inconsistent voting view +
            • +
            • [ZOOKEEPER-766] - forrest recipes docs don't mention the lock/queue recipe implementations available in the release +
            • +
            • [ZOOKEEPER-769] - Leader can treat observers as quorum members +
            • +
            • [ZOOKEEPER-772] - zkpython segfaults when watcher from async get children is invoked. +
            • +
            • [ZOOKEEPER-774] - Recipes tests are slightly outdated: they do not compile against JUnit 4.8 +
            • +
            • [ZOOKEEPER-782] - Incorrect C API documentation for Watches +
            • +
            • [ZOOKEEPER-783] - committedLog in ZKDatabase is not properly synchronized +
            • +
            • [ZOOKEEPER-785] - Zookeeper 3.3.1 shouldn't infinite loop if someone creates a server.0 line +
            • +
            • [ZOOKEEPER-787] - groupId in deployed pom is wrong +
            • +
            • [ZOOKEEPER-790] - Last processed zxid set prematurely while establishing leadership +
            • +
            • [ZOOKEEPER-792] - zkpython memory leak +
            • +
            • [ZOOKEEPER-794] - Callbacks are not invoked when the client is closed +
            • +
            • [ZOOKEEPER-795] - eventThread isn't shutdown after a connection "session expired" event coming +
            • +
            • [ZOOKEEPER-796] - zkServer.sh should support an external PIDFILE variable +
            • +
            • [ZOOKEEPER-800] - zoo_add_auth returns ZOK if zookeeper handle is in ZOO_CLOSED_STATE +
            • +
            • [ZOOKEEPER-804] - c unit tests failing due to "assertion cptr failed" +
            • +
            • [ZOOKEEPER-813] - maven install is broken due to incorrect organisation +
            • +
            • [ZOOKEEPER-814] - monitoring scripts are missing apache license headers +
            • +
            • [ZOOKEEPER-820] - update c unit tests to ensure "zombie" java server processes don't cause failure +
            • +
            • [ZOOKEEPER-822] - Leader election taking a long time to complete +
            • +
            • [ZOOKEEPER-831] - BookKeeper: Throttling improved for reads +
            • +
            • [ZOOKEEPER-844] - handle auth failure in java client +
            • +
            • [ZOOKEEPER-846] - zookeeper client doesn't shut down cleanly on the close call +
            • +
            • [ZOOKEEPER-854] - BookKeeper does not compile due to changes in the ZooKeeper code +
            • +
            • [ZOOKEEPER-855] - clientPortBindAddress should be clientPortAddress +
            • +
            • [ZOOKEEPER-861] - Missing the test SSL certificate used for running junit tests. +
            • +
            • [ZOOKEEPER-867] - ClientTest is failing on hudson - fd cleanup +
            • +
            • [ZOOKEEPER-870] - Zookeeper trunk build broken. +
            • +
            • [ZOOKEEPER-874] - FileTxnSnapLog.restore does not call listener +
            • +
            • [ZOOKEEPER-880] - QuorumCnxManager$SendWorker grows without bounds +
            • +
            • [ZOOKEEPER-881] - ZooKeeperServer.loadData loads database twice +
            • +
            • [ZOOKEEPER-882] - Startup loads last transaction from snapshot +
            • +
            • [ZOOKEEPER-884] - Remove LedgerSequence references from BookKeeper documentation and comments in tests +
            • +
            • [ZOOKEEPER-888] - c-client / zkpython: Double free corruption on node watcher +
            • +
            • [ZOOKEEPER-893] - ZooKeeper high cpu usage when invalid requests +
            • +
            • [ZOOKEEPER-897] - C Client seg faults during close +
            • +
            • [ZOOKEEPER-898] - C Client might not cleanup correctly during close +
            • +
            • [ZOOKEEPER-902] - Fix findbug issue in trunk "Malicious code vulnerability" +
            • +
            • [ZOOKEEPER-904] - super digest is not actually acting as a full superuser +
            • +
            • [ZOOKEEPER-907] - Spurious "KeeperErrorCode = Session moved" messages +
            • +
            • [ZOOKEEPER-913] - Version parser fails to parse "3.3.2-dev" from build.xml. +
            • +
            • [ZOOKEEPER-919] - Ephemeral nodes remains in one of ensemble after deliberate SIGKILL +
            • +
            • [ZOOKEEPER-921] - zkPython incorrectly checks for existence of required ACL elements +
            • +
            • [ZOOKEEPER-937] - test -e not available on solaris /bin/sh +
            • +
            • [ZOOKEEPER-957] - zkCleanup.sh doesn't do anything +
            • +
            • [ZOOKEEPER-958] - Flag to turn off autoconsume in hedwig c++ client +
            • +
            • [ZOOKEEPER-961] - Watch recovery after disconnection when connection string contains a prefix +
            • +
            • [ZOOKEEPER-962] - leader/follower coherence issue when follower is receiving a DIFF +
            • +
            • [ZOOKEEPER-963] - Make Forrest work with JDK6 +
            • +
            • [ZOOKEEPER-975] - new peer goes in LEADING state even if ensemble is online +
            • +
            • [ZOOKEEPER-976] - ZooKeeper startup script doesn't use JAVA_HOME +
            • +
            • [ZOOKEEPER-981] - Hang in zookeeper_close() in the multi-threaded C client +
            • +
            • [ZOOKEEPER-983] - running zkServer.sh start remotely using ssh hangs +
            • +
            • [ZOOKEEPER-985] - Test BookieRecoveryTest fails on trunk. +
            • +
            • [ZOOKEEPER-994] - "eclipse" target in the build script doesnot include libraray required for test classes in the classpath +
            • +
            • [ZOOKEEPER-1006] - QuorumPeer "Address already in use" -- regression in 3.3.3 +
            • +
            • [ZOOKEEPER-1007] - iarchive leak in C client +
            • +
            • [ZOOKEEPER-1013] - zkServer.sh usage message should mention all startup options +
            • +
            • [ZOOKEEPER-1027] - chroot not transparent in zoo_create() +
            • +
            • [ZOOKEEPER-1028] - In python bindings, zookeeper.set2() should return a stat dict but instead returns None +
            • +
            • [ZOOKEEPER-1033] - c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src +
            • +
            • [ZOOKEEPER-1034] - perl bindings should automatically find the zookeeper c-client headers +
            • +
            • [ZOOKEEPER-1046] - Creating a new sequential node results in a ZNODEEXISTS error +
            • +
            • [ZOOKEEPER-1049] - Session expire/close flooding renders heartbeats to delay significantly +
            • +
            • [ZOOKEEPER-1051] - SIGPIPE in Zookeeper 0.3.* when send'ing after cluster disconnection +
            • +
            • [ZOOKEEPER-1052] - Findbugs warning in QuorumPeer.ResponderThread.run() +
            • +
            • [ZOOKEEPER-1055] - check for duplicate ACLs in addACL() and create() +
            • +
            • [ZOOKEEPER-1058] - fix typo in opToString for getData +
            • +
            • [ZOOKEEPER-1059] - stat command isses on non-existing node causes NPE +
            • +
            • [ZOOKEEPER-1060] - QuorumPeer takes a long time to shutdown +
            • +
            • [ZOOKEEPER-1061] - Zookeeper stop fails if start called twice +
            • +
            • [ZOOKEEPER-1063] - Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes +
            • +
            • [ZOOKEEPER-1068] - Documentation and default config suggest incorrect location for Zookeeper state +
            • +
            • [ZOOKEEPER-1069] - Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log +
            • +
            • [ZOOKEEPER-1073] - address a documentation issue in ZOOKEEPER-1030 +
            • +
            • [ZOOKEEPER-1074] - zkServer.sh is missing nohup/sleep, which are necessary for remote invocation +
            • +
            • [ZOOKEEPER-1076] - some quorum tests are unnecessarily extending QuorumBase +
            • +
            • [ZOOKEEPER-1083] - Javadoc for WatchedEvent not being generated +
            • +
            • [ZOOKEEPER-1086] - zookeeper test jar has non mavenised dependency. +
            • +
            • [ZOOKEEPER-1087] - ForceSync VM arguement not working when set to "no" +
            • +
            • [ZOOKEEPER-1090] - Race condition while taking snapshot can lead to not restoring data tree correctly +
            • +
            • [ZOOKEEPER-1091] - when the chrootPath of ClientCnxn is not null and the Watches of zooKeeper is not null and the method primeConnection(SelectionKey k) of ClientCnxn Occurred again for some reason ,then the wrong watcher clientPath is sended to server +
            • +
            • [ZOOKEEPER-1097] - Quota is not correctly rehydrated on snapshot reload +
            • +
            • [ZOOKEEPER-1101] - Upload zookeeper-test maven artifacts to maven repository. +
            • +
            • [ZOOKEEPER-1108] - Various bugs in zoo_add_auth in C +
            • +
            • [ZOOKEEPER-1109] - Zookeeper service is down when SyncRequestProcessor meets any exception. +
            • +
            • [ZOOKEEPER-1111] - JMXEnv uses System.err instead of logging +
            • +
            • [ZOOKEEPER-1117] - zookeeper 3.3.3 fails to build with gcc >= 4.6.1 on Debian/Ubuntu +
            • +
            • [ZOOKEEPER-1119] - zkServer stop command incorrectly reading comment lines in zoo.cfg +
            • +
            • [ZOOKEEPER-1124] - Multiop submitted to non-leader always fails due to timeout +
            • +
            • [ZOOKEEPER-1134] - ClientCnxnSocket string comparison using == rather than equals +
            • +
            • [ZOOKEEPER-1136] - NEW_LEADER should be queued not sent to match the Zab 1.0 protocol on the twiki +
            • +
            • [ZOOKEEPER-1138] - release audit failing for a number of new files +
            • +
            • [ZOOKEEPER-1139] - jenkins is reporting two warnings, fix these +
            • +
            • [ZOOKEEPER-1140] - server shutdown is not stopping threads +
            • +
            • [ZOOKEEPER-1141] - zkpython fails tests under python 2.4 +
            • +
            • [ZOOKEEPER-1142] - incorrect stat output +
            • +
            • [ZOOKEEPER-1144] - ZooKeeperServer not starting on leader due to a race condition +
            • +
            • [ZOOKEEPER-1145] - ObserverTest.testObserver fails at particular point after several runs of ant junt.run -Dtestcase=ObserverTest +
            • +
            • [ZOOKEEPER-1146] - significant regression in client (c/python) performance +
            • +
            • [ZOOKEEPER-1152] - Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer +
            • +
            • [ZOOKEEPER-1154] - Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election +
            • +
            • [ZOOKEEPER-1156] - Log truncation truncating log too much - can cause data loss +
            • +
            • [ZOOKEEPER-1165] - better eclipse support in tests +
            • +
            • [ZOOKEEPER-1168] - ZooKeeper fails to run with IKVM +
            • +
            • [ZOOKEEPER-1171] - fix build for java 7 +
            • +
            • [ZOOKEEPER-1174] - FD leak when network unreachable +
            • +
            • [ZOOKEEPER-1181] - Fix problems with Kerberos TGT renewal +
            • +
            • [ZOOKEEPER-1185] - Send AuthFailed event to client if SASL authentication fails +
            • +
            • [ZOOKEEPER-1189] - For an invalid snapshot file(less than 10bytes size) RandomAccessFile stream is leaking. +
            • +
            • [ZOOKEEPER-1190] - ant package is not including many of the bin scripts in the package (zkServer.sh for example) +
            • +
            • [ZOOKEEPER-1192] - Leader.waitForEpochAck() checks waitingForNewEpoch instead of checking electionFinished +
            • +
            • [ZOOKEEPER-1194] - Two possible race conditions during leader establishment +
            • +
            • [ZOOKEEPER-1195] - SASL authorizedID being incorrectly set: should use getHostName() rather than getServiceName() +
            • +
            • [ZOOKEEPER-1203] - Zookeeper systest is missing Junit Classes +
            • +
            • [ZOOKEEPER-1206] - Sequential node creation does not use always use digits in node name given certain Locales. +
            • +
            • [ZOOKEEPER-1208] - Ephemeral node not removed after the client session is long gone +
            • +
            • [ZOOKEEPER-1212] - zkServer.sh stop action is not conformat with LSB para 20.2 Init Script Actions +
            • +
            • [ZOOKEEPER-1264] - FollowerResyncConcurrencyTest failing intermittently +
            • +
            • [ZOOKEEPER-1268] - problems with read only mode, intermittent test failures and ERRORs in the log +
            • +
            • [ZOOKEEPER-1270] - testEarlyLeaderAbandonment failing intermittently, quorum formed, no serving. +
            • +
            • [ZOOKEEPER-1271] - testEarlyLeaderAbandonment failing on solaris - clients not retrying connection +
            • +
            • [ZOOKEEPER-1299] - Add winconfig.h file to ignore in release audit. +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-494] - zookeeper should install include headers in /usr/local/include/zookeeper +
            • +
            • [ZOOKEEPER-500] - Async methods shouldnt throw exceptions +
            • +
            • [ZOOKEEPER-631] - zkpython's C code could do with a style clean-up +
            • +
            • [ZOOKEEPER-636] - configure.ac has instructions which override the contents of CFLAGS and CXXFLAGS. +
            • +
            • [ZOOKEEPER-724] - Improve junit test integration - log harness information +
            • +
            • [ZOOKEEPER-733] - use netty to handle client connections +
            • +
            • [ZOOKEEPER-765] - Add python example script +
            • +
            • [ZOOKEEPER-773] - Log visualisation +
            • +
            • [ZOOKEEPER-788] - Add server id to message logs +
            • +
            • [ZOOKEEPER-789] - Improve FLE log messages +
            • +
            • [ZOOKEEPER-797] - c client source with AI_ADDRCONFIG cannot be compiled with early glibc +
            • +
            • [ZOOKEEPER-809] - Improved REST Interface +
            • +
            • [ZOOKEEPER-821] - Add ZooKeeper version information to zkpython +
            • +
            • [ZOOKEEPER-850] - Switch from log4j to slf4j +
            • +
            • [ZOOKEEPER-853] - Make zookeeper.is_unrecoverable return True or False and not an integer +
            • +
            • [ZOOKEEPER-862] - Hedwig created ledgers with hardcoded Bookkeeper ensemble and quorum size. Make these a server config parameter instead. +
            • +
            • [ZOOKEEPER-864] - Hedwig C++ client improvements +
            • +
            • [ZOOKEEPER-891] - Allow non-numeric version strings +
            • +
            • [ZOOKEEPER-905] - enhance zkServer.sh for easier zookeeper automation-izing +
            • +
            • [ZOOKEEPER-926] - Fork Hadoop common's test-patch.sh and modify for Zookeeper +
            • +
            • [ZOOKEEPER-977] - passing null for path_buffer in zoo_create +
            • +
            • [ZOOKEEPER-980] - allow configuration parameters for log4j.properties +
            • +
            • [ZOOKEEPER-993] - Code improvements +
            • +
            • [ZOOKEEPER-997] - ZkClient ignores command if there are any space in front of it +
            • +
            • [ZOOKEEPER-1018] - The connection permutation in get_addrs uses a weak and inefficient shuffle +
            • +
            • [ZOOKEEPER-1025] - zkCli is overly sensitive to to spaces. +
            • +
            • [ZOOKEEPER-1030] - Increase default for maxClientCnxns +
            • +
            • [ZOOKEEPER-1094] - Small improvements to LeaderElection and Vote classes +
            • +
            • [ZOOKEEPER-1095] - Simple leader election recipe +
            • +
            • [ZOOKEEPER-1103] - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. +
            • +
            • [ZOOKEEPER-1104] - CLONE - In QuorumTest, use the same "for ( .. try { break } catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. +
            • +
            • [ZOOKEEPER-1143] - quorum send & recv workers are missing thread names +
            • +
            • [ZOOKEEPER-1153] - Deprecate AuthFLE and LE +
            • +
            • [ZOOKEEPER-1166] - Please add a few svn:ignore properties +
            • +
            • [ZOOKEEPER-1169] - Fix compiler (eclipse) warnings in (generated) jute code +
            • +
            • [ZOOKEEPER-1239] - add logging/stats to identify fsync stalls +
            • +
            + +

            New Feature +

            + + +

            Task +

            +
              +
            • [ZOOKEEPER-754] - numerous misspellings "succesfully" +
            • +
            • [ZOOKEEPER-1149] - users cannot migrate from 3.4->3.3->3.4 server code against a single datadir +
            • +
            + +

            Test +

            +

            diff --git a/src/NOTICE.txt b/src/NOTICE.txt index d0446a6f7f2..7e4c7de34ac 100644 --- a/src/NOTICE.txt +++ b/src/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2015 The Apache Software Foundation +Copyright 2009-2016 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/src/c/configure.ac b/src/c/configure.ac index 83c665297eb..be00af90626 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.8,[user@zookeeper.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.9,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index 06c377e9317..ce42b26abe0 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -117,7 +117,7 @@ #define PACKAGE_NAME "zookeeper C client" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "zookeeper C client 3.4.8 win32" +#define PACKAGE_STRING "zookeeper C client 3.4.9 win32" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "c-client-src" @@ -126,7 +126,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.4.8" +#define PACKAGE_VERSION "3.4.9" /* poll() second argument type */ #define POLL_NFDS_TYPE @@ -138,7 +138,7 @@ #define TIME_WITH_SYS_TIME /* Version number of package */ -#define VERSION "3.4.8" +#define VERSION "3.4.9" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 92eee134eef..57790c49aee 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 8 +#define ZOO_PATCH_VERSION 9 #ifdef __cplusplus } From 962b6dba1e58d17b4d39cfcefcdfa48e64152d5b Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 22 Aug 2016 19:18:41 +0000 Subject: [PATCH 352/444] Preparing for release 3.4.9 RC1 git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1757250 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8552ba74a0c..40e0428de04 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -Release 3.4.9 - 2016-08-18 +Release 3.4.9 - 2016-08-23 Backward compatible changes: From 3aa6ec137e59ed21896b1649c2de136497291a8d Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Sat, 3 Sep 2016 04:22:24 +0000 Subject: [PATCH 353/444] Update version in build.xml git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1759054 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 8562000f7e7..ee6834c3d9e 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 552d84cf71902357a76cc65a4ca402a3474eed49 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Tue, 6 Sep 2016 17:23:37 +0000 Subject: [PATCH 354/444] ZOOKEEPER-2502: Flaky Test: org.apache.zookeeper.server.quorum.CnxManagerTest.testCnxFromFutureVersion (Michael Han via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1759466 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 11 +++++++++++ .../zookeeper/server/quorum/CnxManagerTest.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 40e0428de04..0873bd39936 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,14 @@ +Release 3.4.10 - Unreleased + +Backward compatible changes: + +BUGFIXES: + + ZOOKEEPER-2502: Flaky Test: + org.apache.zookeeper.server.quorum.CnxManagerTest.testCnxFromFutureVersion + (Michael Han via phunt) + + Release 3.4.9 - 2016-08-23 Backward compatible changes: diff --git a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java index 831d3ed202d..8db7fa858b5 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java @@ -288,7 +288,7 @@ String getMsgString(Message m){ */ @Test public void testCnxFromFutureVersion() throws Exception { - QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2); + QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 20); TestCnxManager cnxManager = new TestCnxManager(peer); QuorumCnxManager.Listener listener = cnxManager.listener; if(listener != null){ From 72b37623e3f9ce88cbfc872e20f2a5a721619c22 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 8 Sep 2016 02:58:04 +0000 Subject: [PATCH 355/444] ZOOKEEPER-2558: Potential memory leak in recordio.c (Michael Han via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1759737 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 3 +++ src/c/src/recordio.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0873bd39936..f060daf0fdf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,9 @@ BUGFIXES: org.apache.zookeeper.server.quorum.CnxManagerTest.testCnxFromFutureVersion (Michael Han via phunt) + ZOOKEEPER-2558: Potential memory leak in recordio.c + (Michael Han via phunt) + Release 3.4.9 - 2016-08-23 diff --git a/src/c/src/recordio.c b/src/c/src/recordio.c index 41797fbc978..b17cb79a365 100644 --- a/src/c/src/recordio.c +++ b/src/c/src/recordio.c @@ -296,9 +296,11 @@ static struct oarchive oa_default = { STRUCT_INITIALIZER (start_record , oa_star struct iarchive *create_buffer_iarchive(char *buffer, int len) { - struct iarchive *ia = malloc(sizeof(*ia)); - struct buff_struct *buff = malloc(sizeof(struct buff_struct)); + struct iarchive *ia; + struct buff_struct *buff; + ia = malloc(sizeof(*ia)); if (!ia) return 0; + buff = malloc(sizeof(struct buff_struct)); if (!buff) { free(ia); return 0; @@ -313,9 +315,11 @@ struct iarchive *create_buffer_iarchive(char *buffer, int len) struct oarchive *create_buffer_oarchive() { - struct oarchive *oa = malloc(sizeof(*oa)); - struct buff_struct *buff = malloc(sizeof(struct buff_struct)); + struct oarchive *oa; + struct buff_struct *buff; + oa = malloc(sizeof(*oa)); if (!oa) return 0; + buff = malloc(sizeof(struct buff_struct)); if (!buff) { free(oa); return 0; From b7638e885b7f20be6861425e77081a0d8ee84811 Mon Sep 17 00:00:00 2001 From: "Patrick D. Hunt" Date: Thu, 8 Sep 2016 14:34:13 +0000 Subject: [PATCH 356/444] ZOOKEEPER-2507: C unit test improvement: line break between 'ZooKeeper server started' and 'Running' (Michael Han via phunt) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1759841 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 5 +++++ src/c/tests/TestDriver.cc | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f060daf0fdf..4ff628cf8f5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,11 @@ BUGFIXES: ZOOKEEPER-2558: Potential memory leak in recordio.c (Michael Han via phunt) +IMPROVEMENTS: + + ZOOKEEPER-2507: C unit test improvement: line break between + 'ZooKeeper server started' and 'Running' (Michael Han via phunt) + Release 3.4.9 - 2016-08-23 diff --git a/src/c/tests/TestDriver.cc b/src/c/tests/TestDriver.cc index 3f32ba483c4..d60db69dc99 100644 --- a/src/c/tests/TestDriver.cc +++ b/src/c/tests/TestDriver.cc @@ -144,7 +144,7 @@ int main( int argc, char* argv[] ) { runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() ); try { - CPPUNIT_NS::stdCOut() << "Running " << endl; + CPPUNIT_NS::stdCOut() << endl << "Running " << endl; zoo_set_debug_level(ZOO_LOG_LEVEL_INFO); //zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); From 5859e043cbc7bad30c7e2e06bc33d11e92037814 Mon Sep 17 00:00:00 2001 From: Chris Nauroth Date: Thu, 8 Sep 2016 21:38:23 +0000 Subject: [PATCH 357/444] ZOOKEEPER-2557: Update gitignore to account for other file extensions. (Edward Ribeiro via cnauroth) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/branches/branch-3.4@1759914 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ CHANGES.txt | 2 ++ 2 files changed, 62 insertions(+) diff --git a/.gitignore b/.gitignore index 2428fad1f20..0cbf40d712d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,69 @@ +# Java +hs_err_pid* + +# Git +*.orij +*.rej + +# SVN +.svn +.revision + +# Eclipse +.metadata .classpath .eclipse/ +.idea/ .project .revision/ .settings/ +.externalToolBuilders/ +local.properties +.recommenders +*.launch build/ +target/ +out/ + +# Intellij +*.ipr +*.iws +*.iml + +# NetBeans +*~.nib +nbbuild/ +dist/ +nbdist/ +.ndb-gradle/ +.cproject +.buildpath + +# Other +*~ +logs/ +*.out +*.log +*.bak +*.tmp +tmp/** +tmp/**/* +.DS_Store +.gradle +*.patch +*.swp +generated + +# C +tags +.cproject +.project +obj +src/c/core.* +src/c/TEST-*.txt +src/c/*.la +src/c/*.lo +src/c/*.o src/c/generated/ src/java/generated/ src/java/lib/ant-eclipse-* diff --git a/CHANGES.txt b/CHANGES.txt index 4ff628cf8f5..65de3fbc686 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,8 @@ IMPROVEMENTS: ZOOKEEPER-2507: C unit test improvement: line break between 'ZooKeeper server started' and 'Running' (Michael Han via phunt) + ZOOKEEPER-2557: Update gitignore to account for other file extensions. + (Edward Ribeiro via cnauroth) Release 3.4.9 - 2016-08-23 From 17b93773cdd9546e6a72604a0722eed05dbb9bbe Mon Sep 17 00:00:00 2001 From: Patrick Hunt Date: Sun, 18 Sep 2016 14:26:47 -0700 Subject: [PATCH 358/444] ZOOKEEPER-2579: ZooKeeper server should verify that dataDir and snapDir are writeable before starting (Abraham Fine via phunt) Change-Id: I2ee91f3cdce655a92c2c6c468a79b3c6059436c3 --- CHANGES.txt | 3 + .../server/persistence/FileTxnSnapLog.java | 8 ++ .../server/ZooKeeperServerMainTest.java | 100 ++++++++++++++++-- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 65de3fbc686..a4f8053fb14 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,9 @@ BUGFIXES: ZOOKEEPER-2558: Potential memory leak in recordio.c (Michael Han via phunt) + ZOOKEEPER-2579: ZooKeeper server should verify that dataDir and + snapDir are writeable before starting (Abraham Fine via phunt) + IMPROVEMENTS: ZOOKEEPER-2507: C unit test improvement: line break between diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index 6f0df515f53..7841efad581 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -86,12 +86,20 @@ public FileTxnSnapLog(File dataDir, File snapDir) throws IOException { + this.dataDir); } } + if (!this.dataDir.canWrite()) { + throw new IOException("Cannot write to data directory " + this.dataDir); + } + if (!this.snapDir.exists()) { if (!this.snapDir.mkdirs()) { throw new IOException("Unable to create snap directory " + this.snapDir); } } + if (!this.snapDir.canWrite()) { + throw new IOException("Cannot write to snap directory " + this.snapDir); + } + txnLog = new FileTxnLog(this.dataDir); snapLog = new FileSnap(this.snapDir); } diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java index fcb7889f8bf..aa94fb6802b 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java @@ -62,35 +62,45 @@ public static class MainThread extends Thread { final File confFile; final TestZKSMain main; final File tmpDir; + final File dataDir; + final File logDir; public MainThread(int clientPort, boolean preCreateDirs) throws IOException { + this(clientPort, preCreateDirs, ClientBase.createTmpDir()); + } + + public MainThread(int clientPort, boolean preCreateDirs, File tmpDir) throws IOException { super("Standalone server with clientPort:" + clientPort); - tmpDir = ClientBase.createTmpDir(); - confFile = new File(tmpDir, "zoo.cfg"); + this.tmpDir = tmpDir; + confFile = new File(this.tmpDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=2000\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); - File dataDir = new File(tmpDir, "data"); - String dir = dataDir.toString(); - String dirLog = dataDir.toString() + "_txnlog"; + dataDir = new File(this.tmpDir, "data"); + logDir = new File(dataDir.toString() + "_txnlog"); if (preCreateDirs) { if (!dataDir.mkdir()) { throw new IOException("unable to mkdir " + dataDir); } - dirLog = dataDir.toString(); + if (!logDir.mkdir()) { + throw new IOException("unable to mkdir " + logDir); + } } - + + String dataDirPath = dataDir.toString(); + String logDirPath = logDir.toString(); + // Convert windows path to UNIX to avoid problems with "\" String osname = java.lang.System.getProperty("os.name"); if (osname.toLowerCase().contains("windows")) { - dir = dir.replace('\\', '/'); - dirLog = dirLog.replace('\\', '/'); + dataDirPath = dataDirPath.replace('\\', '/'); + logDirPath = logDirPath.replace('\\', '/'); } - fwriter.write("dataDir=" + dir + "\n"); - fwriter.write("dataLogDir=" + dirLog + "\n"); + fwriter.write("dataDir=" + dataDirPath + "\n"); + fwriter.write("dataLogDir=" + logDirPath + "\n"); fwriter.write("clientPort=" + clientPort + "\n"); fwriter.flush(); fwriter.close(); @@ -197,6 +207,74 @@ public void commit() throws IOException { main.deleteDirs(); } + @Test(timeout = 30000) + public void testReadOnlySnapshotDir() throws Exception { + ClientBase.setupTestEnv(); + final int CLIENT_PORT = PortAssignment.unique(); + + // Start up the ZK server to automatically create the necessary directories + // and capture the directory where data is stored + MainThread main = new MainThread(CLIENT_PORT, true); + File tmpDir = main.tmpDir; + main.start(); + Assert.assertTrue("waiting for server being up", ClientBase + .waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT / 2)); + main.shutdown(); + + // Make the snapshot directory read only + File snapDir = new File(main.dataDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); + snapDir.setWritable(false); + + // Restart ZK and observe a failure + main = new MainThread(CLIENT_PORT, false, tmpDir); + main.start(); + + Assert.assertFalse("waiting for server being up", ClientBase + .waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT / 2)); + + main.shutdown(); + + snapDir.setWritable(true); + + main.deleteDirs(); + } + + @Test(timeout = 30000) + public void testReadOnlyTxnLogDir() throws Exception { + ClientBase.setupTestEnv(); + final int CLIENT_PORT = PortAssignment.unique(); + + // Start up the ZK server to automatically create the necessary directories + // and capture the directory where data is stored + MainThread main = new MainThread(CLIENT_PORT, true); + File tmpDir = main.tmpDir; + main.start(); + Assert.assertTrue("waiting for server being up", ClientBase + .waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT / 2)); + main.shutdown(); + + // Make the transaction log directory read only + File logDir = new File(main.logDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); + logDir.setWritable(false); + + // Restart ZK and observe a failure + main = new MainThread(CLIENT_PORT, false, tmpDir); + main.start(); + + Assert.assertFalse("waiting for server being up", ClientBase + .waitForServerUp("127.0.0.1:" + CLIENT_PORT, + CONNECTION_TIMEOUT / 2)); + + main.shutdown(); + + logDir.setWritable(true); + + main.deleteDirs(); + } + /** * Verify the ability to start a standalone server instance. */ From 8bb13ba2f8fde163eb0b988ff308a7b347bda97b Mon Sep 17 00:00:00 2001 From: Patrick Hunt Date: Wed, 5 Oct 2016 09:32:01 -0700 Subject: [PATCH 359/444] ZOOKEEPER-2594: Use TLS for downloading artifacts during build (Olaf Flebbe via phunt) Change-Id: If93e088ed7ae673f4fe433e145bb0f65ea34c47a --- CHANGES.txt | 3 +++ build.xml | 4 ++-- ivysettings.xml | 9 ++------- src/contrib/build-contrib.xml | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a4f8053fb14..f0e77990eb5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,6 +22,9 @@ IMPROVEMENTS: ZOOKEEPER-2557: Update gitignore to account for other file extensions. (Edward Ribeiro via cnauroth) + ZOOKEEPER-2594: Use TLS for downloading artifacts during build + (Olaf Flebbe via phunt) + Release 3.4.9 - 2016-08-23 Backward compatible changes: diff --git a/build.xml b/build.xml index ee6834c3d9e..505597a5b40 100644 --- a/build.xml +++ b/build.xml @@ -107,7 +107,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + value="https://repo1.maven.org/maven2/org/apache/ivy/ivy" /> @@ -115,7 +115,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + + value="https://repo1.maven.org/maven2/" override="false"/> - + value="https://repository.jboss.org/nexus/content/groups/public/" override="false"/> @@ -33,13 +31,10 @@ pattern="${maven2.pattern.ext}" m2compatible="true"/> - - diff --git a/src/contrib/build-contrib.xml b/src/contrib/build-contrib.xml index fab8a5f25bf..276516e2a4b 100644 --- a/src/contrib/build-contrib.xml +++ b/src/contrib/build-contrib.xml @@ -43,7 +43,7 @@ + value="https://repo1.maven.org/maven2/org/apache/ivy/ivy" /> From a601b032915e85a5f693e7ef247700fefb950a12 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Sun, 16 Oct 2016 18:29:41 +0530 Subject: [PATCH 360/444] ZOOKEEPER-2467: NullPointerException when redo Command is passed negative value (Rakesh Kumar Singh via rakeshr) --- CHANGES.txt | 3 +++ .../org/apache/zookeeper/ZooKeeperMain.java | 2 +- .../org/apache/zookeeper/ZooKeeperTest.java | 23 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f0e77990eb5..f66378dac63 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,9 @@ BUGFIXES: ZOOKEEPER-2579: ZooKeeper server should verify that dataDir and snapDir are writeable before starting (Abraham Fine via phunt) + ZOOKEEPER-2467: NullPointerException when redo Command is passed negative value + (Rakesh Kumar Singh via rakeshr) + IMPROVEMENTS: ZOOKEEPER-2507: C unit test improvement: line break between diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index baf1c494016..26721f54fb0 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -649,7 +649,7 @@ protected boolean processZKCmd(MyCommandOptions co) System.exit(0); } else if (cmd.equals("redo") && args.length >= 2) { Integer i = Integer.decode(args[1]); - if (commandCount <= i){ // don't allow redoing this redo + if (commandCount <= i || i < 0){ // don't allow redoing this redo System.out.println("Command index out of range"); return false; } diff --git a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java index d75a817e6be..2cdc4cb6eac 100644 --- a/src/java/test/org/apache/zookeeper/ZooKeeperTest.java +++ b/src/java/test/org/apache/zookeeper/ZooKeeperTest.java @@ -255,4 +255,27 @@ public void testParseWithMultipleQuotes() throws Exception { Assert.assertEquals("empty string is not taken as third argument", zkMain.cl.getCmdArgument(2), ""); Assert.assertEquals("empty string is not taken as fourth argument", zkMain.cl.getCmdArgument(3), ""); } + + // ZOOKEEPER-2467 : Testing negative number for redo command + @Test + public void testRedoWithNegativeCmdNumber() throws Exception { + final ZooKeeper zk = createClient(); + ZooKeeperMain zkMain = new ZooKeeperMain(zk); + String cmd1 = "redo -1"; + + // setup redirect out/err streams to get System.in/err, use this + // judiciously! + final PrintStream systemOut = System.out; // get current out + final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outContent)); + try { + zkMain.executeLine(cmd1); + Assert.assertEquals("Command index out of range", outContent + .toString().trim()); + } finally { + // revert redirect of out/err streams - important step! + System.setOut(systemOut); + } + } + } From 967c3a71bd8eaf1ac29b2702173115976874bd8e Mon Sep 17 00:00:00 2001 From: fpj Date: Mon, 17 Oct 2016 09:43:25 +0100 Subject: [PATCH 361/444] ZOOKEEPER-2606: SaslServerCallbackHandler#handleAuthorizeCallback() should log the exception (Ted Yu via fpj) --- CHANGES.txt | 3 +++ .../zookeeper/server/auth/SaslServerCallbackHandler.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f66378dac63..2b69758caaf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -28,6 +28,9 @@ IMPROVEMENTS: ZOOKEEPER-2594: Use TLS for downloading artifacts during build (Olaf Flebbe via phunt) + ZOOKEEPER-2606: SaslServerCallbackHandler#handleAuthorizeCallback() should + log the exception (Ted Yu via fpj) + Release 3.4.9 - 2016-08-23 Backward compatible changes: diff --git a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java index 2fbd6eda4e7..7fdffde19b8 100644 --- a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java +++ b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java @@ -134,7 +134,7 @@ private void handleAuthorizeCallback(AuthorizeCallback ac) { LOG.info("Setting authorizedID: " + userNameBuilder); ac.setAuthorizedID(userNameBuilder.toString()); } catch (IOException e) { - LOG.error("Failed to set name based on Kerberos authentication rules."); + LOG.error("Failed to set name based on Kerberos authentication rules.", e); } } From 8a06bd1ccef382461c7b0a63f2012f4aeac90753 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 5 Dec 2016 16:15:37 -0800 Subject: [PATCH 362/444] ZOOKEEPER-1045: Support Quorum Peer mutual authentication via SASL (rakeshr via phunt) Change-Id: I7ae6bd863d46621bba5b9abc908e1497111e0336 --- CHANGES.txt | 6 + build.xml | 7 +- ivy.xml | 28 + src/java/main/org/apache/zookeeper/Login.java | 7 +- .../zookeeper/SaslClientCallbackHandler.java | 104 ++ .../zookeeper/client/ZooKeeperSaslClient.java | 161 +-- .../zookeeper/server/ZooKeeperSaslServer.java | 114 +-- .../auth/SaslServerCallbackHandler.java | 10 +- .../server/quorum/FastLeaderElection.java | 2 + .../zookeeper/server/quorum/Follower.java | 5 +- .../zookeeper/server/quorum/Leader.java | 12 +- .../zookeeper/server/quorum/Learner.java | 24 +- .../server/quorum/LearnerHandler.java | 27 +- .../zookeeper/server/quorum/Observer.java | 8 +- .../server/quorum/QuorumCnxManager.java | 347 +++++-- .../zookeeper/server/quorum/QuorumPeer.java | 181 +++- .../server/quorum/QuorumPeerConfig.java | 48 +- .../server/quorum/QuorumPeerMain.java | 39 +- .../quorum/auth/NullQuorumAuthLearner.java | 33 + .../quorum/auth/NullQuorumAuthServer.java | 34 + .../server/quorum/auth/QuorumAuth.java | 96 ++ .../server/quorum/auth/QuorumAuthLearner.java | 40 + .../server/quorum/auth/QuorumAuthServer.java | 41 + .../quorum/auth/SaslQuorumAuthLearner.java | 230 +++++ .../quorum/auth/SaslQuorumAuthServer.java | 180 ++++ .../auth/SaslQuorumServerCallbackHandler.java | 148 +++ .../apache/zookeeper/util/SecurityUtils.java | 298 ++++++ src/java/test/data/kerberos/minikdc-krb5.conf | 30 + src/java/test/data/kerberos/minikdc.ldiff | 52 + .../server/quorum/CnxManagerTest.java | 15 +- .../quorum/FLEBackwardElectionRoundTest.java | 4 +- .../server/quorum/FLECompatibilityTest.java | 4 +- .../server/quorum/FLEDontCareTest.java | 10 +- .../server/quorum/FLELostMessageTest.java | 2 +- .../zookeeper/server/quorum/LearnerTest.java | 4 +- .../server/quorum/QuorumCnxManagerTest.java | 925 ++++++++++++++++++ .../server/quorum/QuorumPeerTestBase.java | 65 +- .../zookeeper/server/quorum/Zab1_0Test.java | 44 +- .../quorum/auth/KerberosSecurityTestcase.java | 120 +++ .../server/quorum/auth/KerberosTestUtils.java | 76 ++ .../zookeeper/server/quorum/auth/MiniKdc.java | 418 ++++++++ .../server/quorum/auth/MiniKdcTest.java | 184 ++++ .../quorum/auth/QuorumAuthTestBase.java | 146 +++ .../quorum/auth/QuorumAuthUpgradeTest.java | 239 +++++ .../quorum/auth/QuorumDigestAuthTest.java | 221 +++++ .../quorum/auth/QuorumKerberosAuthTest.java | 110 +++ .../auth/QuorumKerberosHostBasedAuthTest.java | 184 ++++ .../zookeeper/test/FLEPredicateTest.java | 2 +- src/zookeeper.jute | 5 + 49 files changed, 4655 insertions(+), 435 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java create mode 100644 src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java create mode 100644 src/java/main/org/apache/zookeeper/util/SecurityUtils.java create mode 100644 src/java/test/data/kerberos/minikdc-krb5.conf create mode 100644 src/java/test/data/kerberos/minikdc.ldiff create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java diff --git a/CHANGES.txt b/CHANGES.txt index 2b69758caaf..eb9ef86af24 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -31,6 +31,12 @@ IMPROVEMENTS: ZOOKEEPER-2606: SaslServerCallbackHandler#handleAuthorizeCallback() should log the exception (Ted Yu via fpj) +NEW FEATURE: + + ZOOKEEPER-1045: Support Quorum Peer mutual authentication via SASL + (rakeshr via phunt) + + Release 3.4.9 - 2016-08-23 Backward compatible changes: diff --git a/build.xml b/build.xml index 505597a5b40..5c4fab25474 100644 --- a/build.xml +++ b/build.xml @@ -75,6 +75,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + @@ -1242,6 +1243,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + @@ -1258,7 +1260,10 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + + + + diff --git a/ivy.xml b/ivy.xml index 95b0e5a1062..437b86be04d 100644 --- a/ivy.xml +++ b/ivy.xml @@ -74,6 +74,34 @@ rev="2.4" conf="releaseaudit->default"/> + + + + + + + + + + + + + + + diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index aaa220cfd51..3e21aae7c8c 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -32,8 +32,9 @@ import javax.security.auth.login.LoginException; import javax.security.auth.callback.CallbackHandler; -import org.apache.log4j.Logger; import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.Subject; import java.util.Date; @@ -41,7 +42,7 @@ import java.util.Set; public class Login { - Logger LOG = Logger.getLogger(Login.class); + private static final Logger LOG = LoggerFactory.getLogger(Login.class); public CallbackHandler callbackHandler; // LoginThread will sleep until 80% of time from last refresh to @@ -291,7 +292,7 @@ private synchronized LoginContext login(final String loginContextName) throws Lo } LoginContext loginContext = new LoginContext(loginContextName,callbackHandler); loginContext.login(); - LOG.info("successfully logged in."); + LOG.info("{} successfully logged in.", loginContextName); return loginContext; } diff --git a/src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java b/src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java new file mode 100644 index 00000000000..d6f554955f0 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/SaslClientCallbackHandler.java @@ -0,0 +1,104 @@ +/** + * 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 javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is used by the SASL mechanisms to get further information to complete + * the authentication. For example, a SASL mechanism might use this callback + * handler to do verification operation. The CallbackHandler interface here + * refers to javax.security.auth.callback.CallbackHandler. It should not be + * confused with ZooKeeper packet callbacks like + * org.apache.zookeeper.server.auth.SaslServerCallbackHandler. + */ +public class SaslClientCallbackHandler implements CallbackHandler { + private String password = null; + private static final Logger LOG = LoggerFactory.getLogger(SaslClientCallbackHandler.class); + private final String entity; + public SaslClientCallbackHandler(String password, String client) { + this.password = password; + this.entity = client; + } + + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + NameCallback nc = (NameCallback) callback; + nc.setName(nc.getDefaultName()); + } + else { + if (callback instanceof PasswordCallback) { + PasswordCallback pc = (PasswordCallback)callback; + if (password != null) { + pc.setPassword(this.password.toCharArray()); + } else { + LOG.warn("Could not login: the {} is being asked for a password, but the ZooKeeper {}" + + " code does not currently support obtaining a password from the user." + + " Make sure that the {} is configured to use a ticket cache (using" + + " the JAAS configuration setting 'useTicketCache=true)' and restart the {}. If" + + " you still get this message after that, the TGT in the ticket cache has expired and must" + + " be manually refreshed. To do so, first determine if you are using a password or a" + + " keytab. If the former, run kinit in a Unix shell in the environment of the user who" + + " is running this Zookeeper {} using the command" + + " 'kinit ' (where is the name of the {}'s Kerberos principal)." + + " If the latter, do" + + " 'kinit -k -t ' (where is the name of the Kerberos principal, and" + + " is the location of the keytab file). After manually refreshing your cache," + + " restart this {}. If you continue to see this message after manually refreshing" + + " your cache, ensure that your KDC host's clock is in sync with this host's clock.", + new Object[]{entity, entity, entity, entity, entity, entity, entity}); + } + } + else { + if (callback instanceof RealmCallback) { + RealmCallback rc = (RealmCallback) callback; + rc.setText(rc.getDefaultText()); + } + else { + if (callback instanceof AuthorizeCallback) { + AuthorizeCallback ac = (AuthorizeCallback) callback; + String authid = ac.getAuthenticationID(); + String authzid = ac.getAuthorizationID(); + if (authid.equals(authzid)) { + ac.setAuthorized(true); + } else { + ac.setAuthorized(false); + } + if (ac.isAuthorized()) { + ac.setAuthorizedID(authzid); + } + } + else { + throw new UnsupportedCallbackException(callback, "Unrecognized SASL " + entity + "Callback"); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index 21ef0fa2725..f3c9d3c5453 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -19,22 +19,13 @@ package org.apache.zookeeper.client; import java.io.IOException; -import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; -import javax.security.sasl.AuthorizeCallback; -import javax.security.sasl.RealmCallback; -import javax.security.sasl.Sasl; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; @@ -42,17 +33,13 @@ import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.Environment; import org.apache.zookeeper.Login; +import org.apache.zookeeper.SaslClientCallbackHandler; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.SetSASLResponse; -import org.apache.zookeeper.server.auth.KerberosName; -import org.ietf.jgss.GSSContext; -import org.ietf.jgss.GSSCredential; -import org.ietf.jgss.GSSException; -import org.ietf.jgss.GSSManager; -import org.ietf.jgss.Oid; +import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -226,83 +213,14 @@ private SaslClient createSaslClient(final String servicePrincipal, } // note that the login object is static: it's shared amongst all zookeeper-related connections. // in order to ensure the login is initialized only once, it must be synchronized the code snippet. - login = new Login(loginContext, new ClientCallbackHandler(null)); + login = new Login(loginContext, new SaslClientCallbackHandler(null, "Client")); login.startThreadIfNeeded(); initializedLogin = true; } } } - Subject subject = login.getSubject(); - SaslClient saslClient; - // Use subject.getPrincipals().isEmpty() as an indication of which SASL mechanism to use: - // if empty, use DIGEST-MD5; otherwise, use GSSAPI. - if (subject.getPrincipals().isEmpty()) { - // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism instead. - LOG.info("Client will use DIGEST-MD5 as SASL mechanism."); - String[] mechs = {"DIGEST-MD5"}; - String username = (String)(subject.getPublicCredentials().toArray()[0]); - String password = (String)(subject.getPrivateCredentials().toArray()[0]); - // "zk-sasl-md5" is a hard-wired 'domain' parameter shared with zookeeper server code (see ServerCnxnFactory.java) - saslClient = Sasl.createSaslClient(mechs, username, "zookeeper", "zk-sasl-md5", null, new ClientCallbackHandler(password)); - return saslClient; - } - else { // GSSAPI. - boolean usingNativeJgss = - Boolean.getBoolean("sun.security.jgss.native"); - if (usingNativeJgss) { - // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html - // """ - // In addition, when performing operations as a particular - // Subject, e.g. Subject.doAs(...) or Subject.doAsPrivileged(...), - // the to-be-used GSSCredential should be added to Subject's - // private credential set. Otherwise, the GSS operations will - // fail since no credential is found. - // """ - try { - GSSManager manager = GSSManager.getInstance(); - Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); - GSSCredential cred = manager.createCredential(null, - GSSContext.DEFAULT_LIFETIME, - krb5Mechanism, - GSSCredential.INITIATE_ONLY); - subject.getPrivateCredentials().add(cred); - if (LOG.isDebugEnabled()) { - LOG.debug("Added private credential to subject: " + cred); - } - } catch (GSSException ex) { - LOG.warn("Cannot add private credential to subject; " + - "authentication at the server may fail", ex); - } - } - final Object[] principals = subject.getPrincipals().toArray(); - // determine client principal from subject. - final Principal clientPrincipal = (Principal)principals[0]; - final KerberosName clientKerberosName = new KerberosName(clientPrincipal.getName()); - // assume that server and client are in the same realm (by default; unless the system property - // "zookeeper.server.realm" is set). - String serverRealm = System.getProperty("zookeeper.server.realm",clientKerberosName.getRealm()); - KerberosName serviceKerberosName = new KerberosName(servicePrincipal+"@"+serverRealm); - final String serviceName = serviceKerberosName.getServiceName(); - final String serviceHostname = serviceKerberosName.getHostName(); - final String clientPrincipalName = clientKerberosName.toString(); - try { - saslClient = Subject.doAs(subject,new PrivilegedExceptionAction() { - public SaslClient run() throws SaslException { - LOG.info("Client will use GSSAPI as SASL mechanism."); - String[] mechs = {"GSSAPI"}; - LOG.debug("creating sasl client: client="+clientPrincipalName+";service="+serviceName+";serviceHostname="+serviceHostname); - SaslClient saslClient = Sasl.createSaslClient(mechs,clientPrincipalName,serviceName,serviceHostname,null,new ClientCallbackHandler(null)); - return saslClient; - } - }); - return saslClient; - } - catch (Exception e) { - LOG.error("Exception while trying to create SASL client", e); - e.printStackTrace(); - return null; - } - } + return SecurityUtils.createSaslClient(login.getSubject(), + servicePrincipal, "zookeeper", "zk-sasl-md5", LOG, "Client"); } catch (LoginException e) { // We throw LoginExceptions... throw e; @@ -471,75 +389,6 @@ public void initialize(ClientCnxn cnxn) throws SaslException { } } - // The CallbackHandler interface here refers to - // javax.security.auth.callback.CallbackHandler. - // It should not be confused with Zookeeper packet callbacks like - // org.apache.zookeeper.server.auth.SaslServerCallbackHandler. - public static class ClientCallbackHandler implements CallbackHandler { - private String password = null; - - public ClientCallbackHandler(String password) { - this.password = password; - } - - public void handle(Callback[] callbacks) throws - UnsupportedCallbackException { - for (Callback callback : callbacks) { - if (callback instanceof NameCallback) { - NameCallback nc = (NameCallback) callback; - nc.setName(nc.getDefaultName()); - } - else { - if (callback instanceof PasswordCallback) { - PasswordCallback pc = (PasswordCallback)callback; - if (password != null) { - pc.setPassword(this.password.toCharArray()); - } else { - LOG.warn("Could not login: the client is being asked for a password, but the Zookeeper" + - " client code does not currently support obtaining a password from the user." + - " Make sure that the client is configured to use a ticket cache (using" + - " the JAAS configuration setting 'useTicketCache=true)' and restart the client. If" + - " you still get this message after that, the TGT in the ticket cache has expired and must" + - " be manually refreshed. To do so, first determine if you are using a password or a" + - " keytab. If the former, run kinit in a Unix shell in the environment of the user who" + - " is running this Zookeeper client using the command" + - " 'kinit ' (where is the name of the client's Kerberos principal)." + - " If the latter, do" + - " 'kinit -k -t ' (where is the name of the Kerberos principal, and" + - " is the location of the keytab file). After manually refreshing your cache," + - " restart this client. If you continue to see this message after manually refreshing" + - " your cache, ensure that your KDC host's clock is in sync with this host's clock."); - } - } - else { - if (callback instanceof RealmCallback) { - RealmCallback rc = (RealmCallback) callback; - rc.setText(rc.getDefaultText()); - } - else { - if (callback instanceof AuthorizeCallback) { - AuthorizeCallback ac = (AuthorizeCallback) callback; - String authid = ac.getAuthenticationID(); - String authzid = ac.getAuthorizationID(); - if (authid.equals(authzid)) { - ac.setAuthorized(true); - } else { - ac.setAuthorized(false); - } - if (ac.isAuthorized()) { - ac.setAuthorizedID(authzid); - } - } - else { - throw new UnsupportedCallbackException(callback,"Unrecognized SASL ClientCallback"); - } - } - } - } - } - } - } - public boolean clientTunneledAuthenticationInProgress() { if (!isSASLConfigured) { return false; diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java index 71870ce1c18..dd6ee8ff971 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperSaslServer.java @@ -18,22 +18,12 @@ package org.apache.zookeeper.server; -import java.security.Principal; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; - import javax.security.auth.Subject; -import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.zookeeper.Login; -import org.ietf.jgss.GSSContext; -import org.ietf.jgss.GSSCredential; -import org.ietf.jgss.GSSException; -import org.ietf.jgss.GSSManager; -import org.ietf.jgss.GSSName; -import org.ietf.jgss.Oid; +import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,107 +41,9 @@ public class ZooKeeperSaslServer { private SaslServer createSaslServer(final Login login) { synchronized (login) { Subject subject = login.getSubject(); - if (subject != null) { - // server is using a JAAS-authenticated subject: determine service principal name and hostname from zk server's subject. - if (subject.getPrincipals().size() > 0) { - try { - final Object[] principals = subject.getPrincipals().toArray(); - final Principal servicePrincipal = (Principal)principals[0]; - - // e.g. servicePrincipalNameAndHostname := "zookeeper/myhost.foo.com@FOO.COM" - final String servicePrincipalNameAndHostname = servicePrincipal.getName(); - - int indexOf = servicePrincipalNameAndHostname.indexOf("/"); - - // e.g. servicePrincipalName := "zookeeper" - final String servicePrincipalName = servicePrincipalNameAndHostname.substring(0, indexOf); - - // e.g. serviceHostnameAndKerbDomain := "myhost.foo.com@FOO.COM" - final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname.substring(indexOf+1,servicePrincipalNameAndHostname.length()); - - indexOf = serviceHostnameAndKerbDomain.indexOf("@"); - // e.g. serviceHostname := "myhost.foo.com" - final String serviceHostname = serviceHostnameAndKerbDomain.substring(0,indexOf); - - final String mech = "GSSAPI"; // TODO: should depend on zoo.cfg specified mechs, but if subject is non-null, it can be assumed to be GSSAPI. - - LOG.debug("serviceHostname is '"+ serviceHostname + "'"); - LOG.debug("servicePrincipalName is '"+ servicePrincipalName + "'"); - LOG.debug("SASL mechanism(mech) is '"+ mech +"'"); - - boolean usingNativeJgss = - Boolean.getBoolean("sun.security.jgss.native"); - if (usingNativeJgss) { - // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html - // """ - // In addition, when performing operations as a particular - // Subject, e.g. Subject.doAs(...) or - // Subject.doAsPrivileged(...), the to-be-used - // GSSCredential should be added to Subject's - // private credential set. Otherwise, the GSS operations - // will fail since no credential is found. - // """ - try { - GSSManager manager = GSSManager.getInstance(); - Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); - GSSName gssName = manager.createName( - servicePrincipalName + "@" + serviceHostname, - GSSName.NT_HOSTBASED_SERVICE); - GSSCredential cred = manager.createCredential(gssName, - GSSContext.DEFAULT_LIFETIME, - krb5Mechanism, - GSSCredential.ACCEPT_ONLY); - subject.getPrivateCredentials().add(cred); - if (LOG.isDebugEnabled()) { - LOG.debug("Added private credential to subject: " + cred); - } - } catch (GSSException ex) { - LOG.warn("Cannot add private credential to subject; " + - "clients authentication may fail", ex); - } - } - try { - return Subject.doAs(subject,new PrivilegedExceptionAction() { - public SaslServer run() { - try { - SaslServer saslServer; - saslServer = Sasl.createSaslServer(mech, servicePrincipalName, serviceHostname, null, login.callbackHandler); - return saslServer; - } - catch (SaslException e) { - LOG.error("Zookeeper Server failed to create a SaslServer to interact with a client during session initiation: " + e); - e.printStackTrace(); - return null; - } - } - } - ); - } - catch (PrivilegedActionException e) { - // TODO: exit server at this point(?) - LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException exception while creating a SaslServer using a JAAS principal context:" + e); - e.printStackTrace(); - } - } - catch (IndexOutOfBoundsException e) { - LOG.error("server principal name/hostname determination error: ", e); - } - } - else { - // JAAS non-GSSAPI authentication: assuming and supporting only DIGEST-MD5 mechanism for now. - // TODO: use 'authMech=' value in zoo.cfg. - try { - SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5","zookeeper","zk-sasl-md5",null, login.callbackHandler); - return saslServer; - } - catch (SaslException e) { - LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e); - } - } - } + return SecurityUtils.createSaslServer(subject, "zookeeper", + "zk-sasl-md5", login.callbackHandler, LOG); } - LOG.error("failed to create saslServer object."); - return null; } public byte[] evaluateResponse(byte[] response) throws SaslException { diff --git a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java index 7fdffde19b8..9f53a4d1c17 100644 --- a/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java +++ b/src/java/main/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java @@ -46,13 +46,15 @@ public class SaslServerCallbackHandler implements CallbackHandler { private String userName; private final Map credentials = new HashMap(); - public SaslServerCallbackHandler(Configuration configuration) throws IOException { - String serverSection = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, - ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME); + public SaslServerCallbackHandler(Configuration configuration) + throws IOException { + String serverSection = System.getProperty( + ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, + ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME); AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry(serverSection); if (configurationEntries == null) { - String errorMessage = "Could not find a 'Server' entry in this configuration: Server cannot start."; + String errorMessage = "Could not find a '" + serverSection + "' entry in this configuration: Server cannot start."; LOG.error(errorMessage); throw new IOException(errorMessage); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index 78f3aa6687c..2a3d4fd4da1 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -997,6 +997,8 @@ else if(self.getVotingView().containsKey(n.sid)) { LOG.warn("Failed to unregister with JMX", e); } self.jmxLeaderElectionBean = null; + LOG.debug("Number of connection processing threads: {}", + manager.getConnectionThreadCount()); } } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java index 043a522e648..9aa0d0b5985 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java @@ -22,6 +22,7 @@ import java.net.InetSocketAddress; import org.apache.jute.Record; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.TxnHeader; @@ -64,9 +65,9 @@ void followLeader() throws InterruptedException { self.end_fle = 0; fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean); try { - InetSocketAddress addr = findLeader(); + QuorumServer leaderServer = findLeader(); try { - connectToLeader(addr); + connectToLeader(leaderServer.addr, leaderServer.hostname); long newEpochZxid = registerWithLeader(Leader.FOLLOWERINFO); //check to see if the leader zxid is lower than ours diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index c83d352260b..710745d4a4a 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -19,6 +19,7 @@ package org.apache.zookeeper.server.quorum; import java.io.ByteArrayOutputStream; +import java.io.BufferedInputStream; import java.io.IOException; import java.net.BindException; import java.net.ServerSocket; @@ -32,10 +33,12 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + +import javax.security.sasl.SaslException; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.server.FinalRequestProcessor; @@ -318,7 +321,10 @@ public void run() { // in LearnerHandler switch to the syncLimit s.setSoTimeout(self.tickTime * self.initLimit); s.setTcpNoDelay(nodelay); - LearnerHandler fh = new LearnerHandler(s, Leader.this); + + BufferedInputStream is = new BufferedInputStream( + s.getInputStream()); + LearnerHandler fh = new LearnerHandler(s, is, Leader.this); fh.start(); } catch (SocketException e) { if (stop) { @@ -332,6 +338,8 @@ public void run() { } else { throw e; } + } catch (SaslException e){ + LOG.error("Exception while connecting to quorum learner", e); } } } catch (Exception e) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index 647b8a22dab..749b2741728 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -39,8 +39,6 @@ import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ZooTrace; @@ -48,6 +46,8 @@ import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.TxnHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class is the superclass of two of the three main actors in a ZK @@ -191,8 +191,8 @@ void request(Request request) throws IOException { /** * Returns the address of the node we think is the leader. */ - protected InetSocketAddress findLeader() { - InetSocketAddress addr = null; + protected QuorumServer findLeader() { + QuorumServer leaderServer = null; // Find the leader by id Vote current = self.getCurrentVote(); for (QuorumServer s : self.getView().values()) { @@ -200,27 +200,28 @@ protected InetSocketAddress findLeader() { // Ensure we have the leader's correct IP address before // attempting to connect. s.recreateSocketAddresses(); - addr = s.addr; + leaderServer = s; break; } } - if (addr == null) { + if (leaderServer == null) { LOG.warn("Couldn't find the leader with id = " + current.getId()); } - return addr; + return leaderServer; } /** * Establish a connection with the Leader found by findLeader. Retries * 5 times before giving up. * @param addr - the address of the Leader to connect to. - * @throws IOException - if the socket connection fails on the 5th attempt + * @throws IOException

          • if the socket connection fails on the 5th attempt
          • + *
          • if there is an authentication failure while connecting to leader
          • * @throws ConnectException * @throws InterruptedException */ - protected void connectToLeader(InetSocketAddress addr) - throws IOException, ConnectException, InterruptedException { + protected void connectToLeader(InetSocketAddress addr, String hostname) + throws IOException, ConnectException, InterruptedException { sock = new Socket(); sock.setSoTimeout(self.tickTime * self.initLimit); for (int tries = 0; tries < 5; tries++) { @@ -241,6 +242,9 @@ protected void connectToLeader(InetSocketAddress addr) } Thread.sleep(1000); } + + self.authLearner.authenticate(sock, hostname); + leaderIs = BinaryInputArchive.getArchive(new BufferedInputStream( sock.getInputStream())); bufferedOutput = new BufferedOutputStream(sock.getOutputStream()); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 8a748c78f49..51ed7e7035c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -32,6 +32,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import javax.security.sasl.SaslException; + import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; @@ -153,15 +155,30 @@ public synchronized boolean check(long time) { private BinaryOutputArchive oa; + private final BufferedInputStream bufferedInput; private BufferedOutputStream bufferedOutput; - LearnerHandler(Socket sock, Leader leader) throws IOException { + LearnerHandler(Socket sock, BufferedInputStream bufferedInput, + Leader leader) throws IOException { super("LearnerHandler-" + sock.getRemoteSocketAddress()); this.sock = sock; this.leader = leader; - leader.addLearnerHandler(this); + this.bufferedInput = bufferedInput; + try { + leader.self.authServer.authenticate(sock, + new DataInputStream(bufferedInput)); + } catch (IOException e) { + LOG.error("Server failed to authenticate quorum learner, addr: {}, closing connection", + sock.getRemoteSocketAddress(), e); + try { + sock.close(); + } catch (IOException ie) { + LOG.error("Exception while closing socket", ie); + } + throw new SaslException("Authentication failure: " + e.getMessage()); + } } - + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -296,11 +313,11 @@ static public String packetToString(QuorumPacket p) { @Override public void run() { try { + leader.addLearnerHandler(this); tickOfNextAckDeadline = leader.self.tick + leader.self.initLimit + leader.self.syncLimit; - ia = BinaryInputArchive.getArchive(new BufferedInputStream(sock - .getInputStream())); + ia = BinaryInputArchive.getArchive(bufferedInput); bufferedOutput = new BufferedOutputStream(sock.getOutputStream()); oa = BinaryOutputArchive.getArchive(bufferedOutput); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java index e53f6f27f3f..53f516fb2dc 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java @@ -19,11 +19,11 @@ package org.apache.zookeeper.server.quorum; import java.io.IOException; -import java.net.InetSocketAddress; import org.apache.jute.Record; import org.apache.zookeeper.server.ObserverBean; import org.apache.zookeeper.server.Request; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.TxnHeader; @@ -61,10 +61,10 @@ void observeLeader() throws InterruptedException { zk.registerJMX(new ObserverBean(this, zk), self.jmxLocalPeerBean); try { - InetSocketAddress addr = findLeader(); - LOG.info("Observing " + addr); + QuorumServer leaderServer = findLeader(); + LOG.info("Observing " + leaderServer.addr); try { - connectToLeader(addr); + connectToLeader(leaderServer.addr, leaderServer.hostname); long newLeaderZxid = registerWithLeader(Leader.OBSERVERINFO); syncWithLeader(newLeaderZxid); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 20e5f165bd6..74d1c1e621c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -18,6 +18,7 @@ package org.apache.zookeeper.server.quorum; +import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; @@ -28,20 +29,28 @@ import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.channels.UnresolvedAddressException; +import java.util.Collections; import java.util.Enumeration; +import java.util.HashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicInteger; -import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.zookeeper.server.ZooKeeperThread; +import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.server.ZooKeeperThread; - /** * This class implements a connection manager for leader election using TCP. It * maintains one connection for every pair of servers. The tricky part is to @@ -89,7 +98,7 @@ public class QuorumCnxManager { * Negative counter for observer server ids. */ - private long observerCounter = -1; + private AtomicLong observerCounter = new AtomicLong(-1); /* * Connection time out value in milliseconds @@ -100,7 +109,20 @@ public class QuorumCnxManager { /* * Local IP address */ - final QuorumPeer self; + final long mySid; + final int socketTimeout; + final Map view; + final boolean listenOnAllIPs; + private ThreadPoolExecutor connectionExecutor; + private final Set inprogressConnections = Collections + .synchronizedSet(new HashSet()); + private QuorumAuthServer authServer; + private QuorumAuthLearner authLearner; + private boolean quorumSaslAuthEnabled; + /* + * Counter to count connection processing threads. + */ + private AtomicInteger connectionThreadCnt = new AtomicInteger(0); /* * Mapping from Peer to Thread number @@ -145,7 +167,14 @@ static public class Message { long sid; } - public QuorumCnxManager(QuorumPeer self) { + public QuorumCnxManager(final long mySid, + Map view, + QuorumAuthServer authServer, + QuorumAuthLearner authLearner, + int socketTimeout, + boolean listenOnAllIPs, + int quorumCnxnThreadsSize, + boolean quorumSaslAuthEnabled) { this.recvQueue = new ArrayBlockingQueue(RECV_CAPACITY); this.queueSendMap = new ConcurrentHashMap>(); this.senderWorkerMap = new ConcurrentHashMap(); @@ -155,13 +184,53 @@ public QuorumCnxManager(QuorumPeer self) { if(cnxToValue != null){ this.cnxTO = new Integer(cnxToValue); } - - this.self = self; + + this.mySid = mySid; + this.socketTimeout = socketTimeout; + this.view = view; + this.listenOnAllIPs = listenOnAllIPs; + + initializeAuth(mySid, authServer, authLearner, quorumCnxnThreadsSize, + quorumSaslAuthEnabled); // Starts listener thread that waits for connection requests listener = new Listener(); } + private void initializeAuth(final long mySid, + final QuorumAuthServer authServer, + final QuorumAuthLearner authLearner, + final int quorumCnxnThreadsSize, + final boolean quorumSaslAuthEnabled) { + this.authServer = authServer; + this.authLearner = authLearner; + this.quorumSaslAuthEnabled = quorumSaslAuthEnabled; + if (!this.quorumSaslAuthEnabled) { + LOG.debug("Not initializing connection executor as quorum sasl auth is disabled"); + return; + } + + // init connection executors + final AtomicInteger threadIndex = new AtomicInteger(1); + SecurityManager s = System.getSecurityManager(); + final ThreadGroup group = (s != null) ? s.getThreadGroup() + : Thread.currentThread().getThreadGroup(); + ThreadFactory daemonThFactory = new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(group, r, "QuorumConnectionThread-" + + "[myid=" + mySid + "]-" + + threadIndex.getAndIncrement()); + return t; + } + }; + this.connectionExecutor = new ThreadPoolExecutor(3, + quorumCnxnThreadsSize, 60, TimeUnit.SECONDS, + new SynchronousQueue(), daemonThFactory); + this.connectionExecutor.allowCoreThreadTimeOut(true); + } + /** * Invokes initiateConnection for testing purposes * @@ -173,7 +242,8 @@ public void testInitiateConnection(long sid) throws Exception { } Socket sock = new Socket(); setSockOpts(sock); - sock.connect(self.getVotingView().get(sid).electionAddr, cnxTO); + sock.connect(QuorumPeer.viewToVotingView(view).get(sid).electionAddr, + cnxTO); initiateConnection(sock, sid); } @@ -181,28 +251,96 @@ public void testInitiateConnection(long sid) throws Exception { * If this server has initiated the connection, then it gives up on the * connection if it loses challenge. Otherwise, it keeps the connection. */ - public boolean initiateConnection(Socket sock, Long sid) { + public void initiateConnection(final Socket sock, final Long sid) { + try { + startConnection(sock, sid); + } catch (IOException e) { + LOG.error("Exception while connecting, id: {}, addr: {}, closing learner connection", + new Object[] { sid, sock.getRemoteSocketAddress() }, e); + closeSocket(sock); + return; + } + } + + /** + * Server will initiate the connection request to its peer server + * asynchronously via separate connection thread. + */ + public void initiateConnectionAsync(final Socket sock, final Long sid) { + if(!inprogressConnections.add(sid)){ + // simply return as there is a connection request to + // server 'sid' already in progress. + LOG.debug("Connection request to server id: {} is already in progress, so skipping this request", + sid); + closeSocket(sock); + return; + } + try { + connectionExecutor.execute( + new QuorumConnectionReqThread(sock, sid)); + connectionThreadCnt.incrementAndGet(); + } catch (Throwable e) { + // Imp: Safer side catching all type of exceptions and remove 'sid' + // from inprogress connections. This is to avoid blocking further + // connection requests from this 'sid' in case of errors. + inprogressConnections.remove(sid); + LOG.error("Exception while submitting quorum connection request", e); + closeSocket(sock); + } + } + + /** + * Thread to send connection request to peer server. + */ + private class QuorumConnectionReqThread extends ZooKeeperThread { + final Socket sock; + final Long sid; + QuorumConnectionReqThread(final Socket sock, final Long sid) { + super("QuorumConnectionReqThread-" + sid); + this.sock = sock; + this.sid = sid; + } + + @Override + public void run() { + try{ + initiateConnection(sock, sid); + } finally { + inprogressConnections.remove(sid); + } + } + } + + private boolean startConnection(Socket sock, Long sid) + throws IOException { DataOutputStream dout = null; + DataInputStream din = null; try { // Sending id and challenge dout = new DataOutputStream(sock.getOutputStream()); - dout.writeLong(self.getId()); + dout.writeLong(this.mySid); dout.flush(); + + din = new DataInputStream( + new BufferedInputStream(sock.getInputStream())); } catch (IOException e) { LOG.warn("Ignoring exception reading or writing challenge: ", e); closeSocket(sock); return false; } - + + // authenticate learner + authLearner.authenticate(sock, view.get(sid).hostname); + // If lost the challenge, then drop the new connection - if (sid > self.getId()) { + if (sid > this.mySid) { LOG.info("Have smaller server identifier, so dropping the " + - "connection: (" + sid + ", " + self.getId() + ")"); + "connection: (" + sid + ", " + this.mySid + ")"); closeSocket(sock); // Otherwise proceed with the connection } else { SendWorker sw = new SendWorker(sock, sid); - RecvWorker rw = new RecvWorker(sock, sid, sw); + RecvWorker rw = new RecvWorker(sock, din, sid, sw); sw.setRecv(rw); SendWorker vsw = senderWorkerMap.get(sid); @@ -225,8 +363,6 @@ public boolean initiateConnection(Socket sock, Long sid) { return false; } - - /** * If this server receives a connection request, then it gives up on the new * connection if it wins. Notice that it checks whether it has a connection @@ -234,12 +370,57 @@ public boolean initiateConnection(Socket sock, Long sid) { * possible long value to lose the challenge. * */ - public void receiveConnection(Socket sock) { + public void receiveConnection(final Socket sock) { + DataInputStream din = null; + try { + din = new DataInputStream( + new BufferedInputStream(sock.getInputStream())); + + handleConnection(sock, din); + } catch (IOException e) { + LOG.error("Exception handling connection, addr: {}, closing server connection", + sock.getRemoteSocketAddress()); + closeSocket(sock); + } + } + + /** + * Server receives a connection request and handles it asynchronously via + * separate thread. + */ + public void receiveConnectionAsync(final Socket sock) { + try { + connectionExecutor.execute( + new QuorumConnectionReceiverThread(sock)); + connectionThreadCnt.incrementAndGet(); + } catch (Throwable e) { + LOG.error("Exception handling connection, addr: {}, closing server connection", + sock.getRemoteSocketAddress()); + closeSocket(sock); + } + } + + /** + * Thread to receive connection request from peer server. + */ + private class QuorumConnectionReceiverThread extends ZooKeeperThread { + private final Socket sock; + QuorumConnectionReceiverThread(final Socket sock) { + super("QuorumConnectionReceiverThread-" + sock.getRemoteSocketAddress()); + this.sock = sock; + } + + @Override + public void run() { + receiveConnection(sock); + } + } + + private void handleConnection(Socket sock, DataInputStream din) + throws IOException { Long sid = null; - try { // Read server id - DataInputStream din = new DataInputStream(sock.getInputStream()); sid = din.readLong(); if (sid < 0) { // this is not a server id but a protocol version (see ZOOKEEPER-1633) sid = din.readLong(); @@ -265,8 +446,7 @@ public void receiveConnection(Socket sock) { * Choose identifier at random. We need a value to identify * the connection. */ - - sid = observerCounter--; + sid = observerCounter.getAndDecrement(); LOG.info("Setting arbitrary identifier to observer: " + sid); } } catch (IOException e) { @@ -274,9 +454,12 @@ public void receiveConnection(Socket sock) { LOG.warn("Exception reading or writing challenge: " + e.toString()); return; } - + + // do authenticating learner + authServer.authenticate(sock, din); + //If wins the challenge, then close the new connection. - if (sid < self.getId()) { + if (sid < this.mySid) { /* * This replica might still believe that the connection to sid is * up, so we have to shut down the workers before trying to open a @@ -297,7 +480,7 @@ public void receiveConnection(Socket sock) { // Otherwise start worker threads to receive data. } else { SendWorker sw = new SendWorker(sock, sid); - RecvWorker rw = new RecvWorker(sock, sid, sw); + RecvWorker rw = new RecvWorker(sock, din, sid, sw); sw.setRecv(rw); SendWorker vsw = senderWorkerMap.get(sid); @@ -327,7 +510,7 @@ public void toSend(Long sid, ByteBuffer b) { /* * If sending message to myself, then simply enqueue it (loopback). */ - if (self.getId() == sid) { + if (this.mySid == sid) { b.position(0); addToRecvQueue(new Message(b.duplicate(), sid)); /* @@ -361,28 +544,32 @@ public void toSend(Long sid, ByteBuffer b) { * * @param sid server id */ - - synchronized void connectOne(long sid){ - if (senderWorkerMap.get(sid) == null){ + synchronized public void connectOne(long sid){ + if (!connectedToPeer(sid)){ InetSocketAddress electionAddr; - if (self.quorumPeers.containsKey(sid)) { - electionAddr = self.quorumPeers.get(sid).electionAddr; + if (view.containsKey(sid)) { + electionAddr = view.get(sid).electionAddr; } else { LOG.warn("Invalid server id: " + sid); return; } try { - if (LOG.isDebugEnabled()) { - LOG.debug("Opening channel to server " + sid); - } + LOG.debug("Opening channel to server " + sid); Socket sock = new Socket(); setSockOpts(sock); - sock.connect(self.getView().get(sid).electionAddr, cnxTO); - if (LOG.isDebugEnabled()) { - LOG.debug("Connected to server " + sid); + sock.connect(view.get(sid).electionAddr, cnxTO); + LOG.debug("Connected to server " + sid); + + // Sends connection request asynchronously if the quorum + // sasl authentication is enabled. This is required because + // sasl server authentication process may take few seconds to + // finish, this may delay next peer connection requests. + if (quorumSaslAuthEnabled) { + initiateConnectionAsync(sock, sid); + } else { + initiateConnection(sock, sid); } - initiateConnection(sock, sid); } catch (UnresolvedAddressException e) { // Sun doesn't include the address that causes this // exception to be thrown, also UAE cannot be wrapped cleanly @@ -392,8 +579,8 @@ synchronized void connectOne(long sid){ + " at election address " + electionAddr, e); // Resolve hostname for this server in case the // underlying ip address has changed. - if (self.getView().containsKey(sid)) { - self.getView().get(sid).recreateSocketAddresses(); + if (view.containsKey(sid)) { + view.get(sid).recreateSocketAddresses(); } throw e; } catch (IOException e) { @@ -403,8 +590,8 @@ synchronized void connectOne(long sid){ // We can't really tell if the server is actually down or it failed // to connect to the server because the underlying IP address // changed. Resolve the hostname again just in case. - if (self.getView().containsKey(sid)) { - self.getView().get(sid).recreateSocketAddresses(); + if (view.containsKey(sid)) { + view.get(sid).recreateSocketAddresses(); } } } else { @@ -451,6 +638,13 @@ public void halt() { listener.halt(); softHalt(); + + // clear data structures used for auth + if (connectionExecutor != null) { + connectionExecutor.shutdown(); + } + inprogressConnections.clear(); + resetConnectionThreadCount(); } /** @@ -471,7 +665,7 @@ public void softHalt() { */ private void setSockOpts(Socket sock) throws SocketException { sock.setTcpNoDelay(true); - sock.setSoTimeout(self.tickTime * self.syncLimit); + sock.setSoTimeout(socketTimeout); } /** @@ -494,11 +688,19 @@ private void closeSocket(Socket sock) { public long getThreadCount() { return threadCnt.get(); } + + /** + * Return number of connection processing threads. + */ + public long getConnectionThreadCount() { + return connectionThreadCnt.get(); + } + /** - * Return reference to QuorumPeer + * Reset the value of connection processing threads count to zero. */ - public QuorumPeer getQuorumPeer() { - return self; + private void resetConnectionThreadCount() { + connectionThreadCnt.set(0); } /** @@ -525,22 +727,35 @@ public void run() { try { ss = new ServerSocket(); ss.setReuseAddress(true); - if (self.getQuorumListenOnAllIPs()) { - int port = self.quorumPeers.get(self.getId()).electionAddr.getPort(); + if (listenOnAllIPs) { + int port = view.get(QuorumCnxManager.this.mySid) + .electionAddr.getPort(); addr = new InetSocketAddress(port); } else { - addr = self.quorumPeers.get(self.getId()).electionAddr; + addr = view.get(QuorumCnxManager.this.mySid) + .electionAddr; } LOG.info("My election bind port: " + addr.toString()); - setName(self.quorumPeers.get(self.getId()).electionAddr - .toString()); + setName(view.get(QuorumCnxManager.this.mySid) + .electionAddr.toString()); ss.bind(addr); while (!shutdown) { Socket client = ss.accept(); setSockOpts(client); LOG.info("Received connection request " + client.getRemoteSocketAddress()); - receiveConnection(client); + + // Receive and handle the connection request + // asynchronously if the quorum sasl authentication is + // enabled. This is required because sasl server + // authentication process may take few seconds to finish, + // this may delay next peer connection requests. + if (quorumSaslAuthEnabled) { + receiveConnectionAsync(client); + } else { + receiveConnection(client); + } + numRetries = 0; } } catch (IOException e) { @@ -562,7 +777,7 @@ public void run() { LOG.error("As I'm leaving the listener thread, " + "I won't be able to participate in leader " + "election any longer: " - + self.quorumPeers.get(self.getId()).electionAddr); + + view.get(QuorumCnxManager.this.mySid).electionAddr); } } @@ -573,7 +788,8 @@ void halt(){ try{ LOG.debug("Trying to close listener: " + ss); if(ss != null) { - LOG.debug("Closing listener: " + self.getId()); + LOG.debug("Closing listener: " + + QuorumCnxManager.this.mySid); ss.close(); } } catch (IOException e){ @@ -729,8 +945,9 @@ public void run() { } } } catch (Exception e) { - LOG.warn("Exception when using channel: for id " + sid + " my id = " + - self.getId() + " error = " + e); + LOG.warn("Exception when using channel: for id " + sid + + " my id = " + QuorumCnxManager.this.mySid + + " error = " + e); } this.finish(); LOG.warn("Send worker leaving thread"); @@ -745,16 +962,16 @@ class RecvWorker extends ZooKeeperThread { Long sid; Socket sock; volatile boolean running = true; - DataInputStream din; + final DataInputStream din; final SendWorker sw; - RecvWorker(Socket sock, Long sid, SendWorker sw) { + RecvWorker(Socket sock, DataInputStream din, Long sid, SendWorker sw) { super("RecvWorker:" + sid); this.sid = sid; this.sock = sock; this.sw = sw; + this.din = din; try { - din = new DataInputStream(sock.getInputStream()); // OK to wait until socket disconnects while reading. sock.setSoTimeout(0); } catch (IOException e) { @@ -807,8 +1024,8 @@ public void run() { addToRecvQueue(new Message(message.duplicate(), sid)); } } catch (Exception e) { - LOG.warn("Connection broken for id " + sid + ", my id = " + - self.getId() + ", error = " , e); + LOG.warn("Connection broken for id " + sid + ", my id = " + + QuorumCnxManager.this.mySid + ", error = " , e); } finally { LOG.warn("Interrupting SendWorker"); sw.finish(); @@ -930,4 +1147,8 @@ public Message pollRecvQueue(long timeout, TimeUnit unit) throws InterruptedException { return recvQueue.poll(timeout, unit); } + + public boolean connectedToPeer(long peerSid) { + return senderWorkerMap.get(peerSid) != null; + } } 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 2f0f21bcb18..2dbedcf802e 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -34,8 +34,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; + +import javax.security.sasl.SaslException; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.apache.zookeeper.jmx.MBeanRegistry; @@ -45,6 +49,13 @@ import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.quorum.auth.QuorumAuth; +import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; +import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthServer; +import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthServer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ZxidUtils; @@ -85,6 +96,8 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider LocalPeerBean jmxLocalPeerBean; LeaderElectionBean jmxLeaderElectionBean; QuorumCnxManager qcm; + QuorumAuthServer authServer; + QuorumAuthLearner authLearner; /* ZKDatabase is a top level member of quorumpeer * which will be used in all the zookeeperservers @@ -102,7 +115,8 @@ private QuorumServer(long id, InetSocketAddress addr, this.electionAddr = electionAddr; } - private QuorumServer(long id, InetSocketAddress addr) { + // VisibleForTesting + public QuorumServer(long id, InetSocketAddress addr) { this.id = id; this.addr = addr; this.electionAddr = null; @@ -337,6 +351,50 @@ synchronized void setBCVote(Vote v) { */ protected boolean quorumListenOnAllIPs = false; + /** + * Enable/Disables quorum authentication using sasl. Defaulting to false. + */ + protected boolean quorumSaslEnableAuth; + + /** + * If this is false, quorum peer server will accept another quorum peer client + * connection even if the authentication did not succeed. This can be used while + * upgrading ZooKeeper server. Defaulting to false (required). + */ + protected boolean quorumServerSaslAuthRequired; + + /** + * If this is false, quorum peer learner will talk to quorum peer server + * without authentication. This can be used while upgrading ZooKeeper + * server. Defaulting to false (required). + */ + protected boolean quorumLearnerSaslAuthRequired; + + /** + * Kerberos quorum service principal. Defaulting to 'zkquorum/localhost'. + */ + protected String quorumServicePrincipal; + + /** + * Quorum learner login context name in jaas-conf file to read the kerberos + * security details. Defaulting to 'QuorumLearner'. + */ + protected String quorumLearnerLoginContext; + + /** + * Quorum server login context name in jaas-conf file to read the kerberos + * security details. Defaulting to 'QuorumServer'. + */ + protected String quorumServerLoginContext; + + // TODO: need to tune the default value of thread size + private static final int QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE = 20; + /** + * The maximum number of threads to allow in the connectionExecutors thread + * pool which will be used to initiate quorum server connections. + */ + protected int quorumCnxnThreadsSize = QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE; + /** * @deprecated As of release 3.4.0, this class has been deprecated, since * it is used with one of the udp-based versions of leader election, which @@ -449,10 +507,15 @@ public InetSocketAddress getQuorumAddress(){ private FileTxnSnapLog logFactory = null; private final QuorumStats quorumStats; - - public QuorumPeer() { + + public static QuorumPeer testingQuorumPeer() throws SaslException { + return new QuorumPeer(); + } + + private QuorumPeer() throws SaslException { super("QuorumPeer"); quorumStats = new QuorumStats(this); + initialize(); } @@ -490,7 +553,24 @@ public QuorumPeer(Map quorumPeers, File dataDir, this.quorumConfig = new QuorumMaj(countParticipants(quorumPeers)); else this.quorumConfig = quorumConfig; } - + + public void initialize() throws SaslException { + // init quorum auth server & learner + if (isQuorumSaslAuthEnabled()) { + Set authzHosts = new HashSet(); + for (QuorumServer qs : getView().values()) { + authzHosts.add(qs.hostname); + } + authServer = new SaslQuorumAuthServer(isQuorumServerSaslAuthRequired(), + quorumServerLoginContext, authzHosts); + authLearner = new SaslQuorumAuthLearner(isQuorumLearnerSaslAuthRequired(), + quorumServicePrincipal, quorumLearnerLoginContext); + } else { + authServer = new NullQuorumAuthServer(); + authLearner = new NullQuorumAuthLearner(); + } + } + QuorumStats quorumStats() { return quorumStats; } @@ -686,7 +766,7 @@ protected Election createElectionAlgorithm(int electionAlgorithm){ le = new AuthFastLeaderElection(this, true); break; case 3: - qcm = new QuorumCnxManager(this); + qcm = createCnxnManager(); QuorumCnxManager.Listener listener = qcm.listener; if(listener != null){ listener.start(); @@ -903,33 +983,37 @@ public void shutdown() { zkDb.close(); } catch (IOException ie) { LOG.warn("Error closing logs ", ie); - } + } } /** * A 'view' is a node's current opinion of the membership of the entire - * ensemble. + * ensemble. */ public Map getView() { return Collections.unmodifiableMap(this.quorumPeers); } - + /** * Observers are not contained in this view, only nodes with - * PeerType=PARTICIPANT. + * PeerType=PARTICIPANT. */ public Map getVotingView() { - Map ret = + return QuorumPeer.viewToVotingView(getView()); + } + + static Map viewToVotingView( + Map view) { + Map ret = new HashMap(); - Map view = getView(); - for (QuorumServer server : view.values()) { + for (QuorumServer server : view.values()) { if (server.type == LearnerType.PARTICIPANT) { ret.put(server.id, server); } - } + } return ret; } - + /** * Returns only observers, no followers. */ @@ -1306,4 +1390,73 @@ protected void updateElectionVote(long newEpoch) { } } + void setQuorumServerSaslRequired(boolean serverSaslRequired) { + quorumServerSaslAuthRequired = serverSaslRequired; + LOG.info("{} set to {}", QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, + serverSaslRequired); + } + + void setQuorumLearnerSaslRequired(boolean learnerSaslRequired) { + quorumLearnerSaslAuthRequired = learnerSaslRequired; + LOG.info("{} set to {}", QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, + learnerSaslRequired); + } + + void setQuorumSaslEnabled(boolean enableAuth) { + quorumSaslEnableAuth = enableAuth; + if (!quorumSaslEnableAuth) { + LOG.info("QuorumPeer communication is not secured!"); + } else { + LOG.info("{} set to {}", + QuorumAuth.QUORUM_SASL_AUTH_ENABLED, enableAuth); + } + } + + void setQuorumServicePrincipal(String servicePrincipal) { + quorumServicePrincipal = servicePrincipal; + LOG.info("{} set to {}",QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, + quorumServicePrincipal); + } + + void setQuorumLearnerLoginContext(String learnerContext) { + quorumLearnerLoginContext = learnerContext; + LOG.info("{} set to {}", QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, + quorumLearnerLoginContext); + } + + void setQuorumServerLoginContext(String serverContext) { + quorumServerLoginContext = serverContext; + LOG.info("{} set to {}", QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT, + quorumServerLoginContext); + } + + void setQuorumCnxnThreadsSize(int qCnxnThreadsSize) { + if (qCnxnThreadsSize > QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE) { + quorumCnxnThreadsSize = qCnxnThreadsSize; + } + LOG.info("quorum.cnxn.threads.size set to {}", quorumCnxnThreadsSize); + } + + boolean isQuorumSaslAuthEnabled() { + return quorumSaslEnableAuth; + } + + private boolean isQuorumServerSaslAuthRequired() { + return quorumServerSaslAuthRequired; + } + + private boolean isQuorumLearnerSaslAuthRequired() { + return quorumLearnerSaslAuthRequired; + } + + public QuorumCnxManager createCnxnManager() { + return new QuorumCnxManager(this.getId(), + this.getView(), + this.authServer, + this.authLearner, + this.tickTime * this.syncLimit, + this.getQuorumListenOnAllIPs(), + this.quorumCnxnThreadsSize, + this.isQuorumSaslAuthEnabled()); + } } 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 0924ef6ae15..621f830d81d 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java @@ -38,6 +38,7 @@ import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.auth.QuorumAuth; import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; @@ -75,7 +76,16 @@ public class QuorumPeerConfig { protected boolean syncEnabled = true; protected LearnerType peerType = LearnerType.PARTICIPANT; - + + /** Configurations for the quorumpeer-to-quorumpeer sasl authentication */ + protected boolean quorumServerRequireSasl = false; + protected boolean quorumLearnerRequireSasl = false; + protected boolean quorumEnableSasl = false; + protected String quorumServicePrincipal = QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE; + protected String quorumLearnerLoginContext = QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT_DFAULT_VALUE; + protected String quorumServerLoginContext = QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT_DFAULT_VALUE; + protected int quorumCnxnThreadsSize; + /** * Minimum snapshot retain count. * @see org.apache.zookeeper.server.PurgeTxnLog#purge(File, File, int) @@ -246,11 +256,45 @@ public void parseProperties(Properties zkProp) int dot = key.indexOf('.'); long sid = Long.parseLong(key.substring(dot + 1)); serverWeight.put(sid, Long.parseLong(value)); + } else if (key.equals(QuorumAuth.QUORUM_SASL_AUTH_ENABLED)) { + quorumEnableSasl = Boolean.parseBoolean(value); + } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED)) { + quorumServerRequireSasl = Boolean.parseBoolean(value); + } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED)) { + quorumLearnerRequireSasl = Boolean.parseBoolean(value); + } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT)) { + quorumLearnerLoginContext = value; + } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT)) { + quorumServerLoginContext = value; + } else if (key.equals(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL)) { + quorumServicePrincipal = value; + } else if (key.equals("quorum.cnxn.threads.size")) { + quorumCnxnThreadsSize = Integer.parseInt(value); } else { System.setProperty("zookeeper." + key, value); } } - + if (!quorumEnableSasl && quorumServerRequireSasl) { + throw new IllegalArgumentException( + QuorumAuth.QUORUM_SASL_AUTH_ENABLED + + " is disabled, so cannot enable " + + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED); + } + if (!quorumEnableSasl && quorumLearnerRequireSasl) { + throw new IllegalArgumentException( + QuorumAuth.QUORUM_SASL_AUTH_ENABLED + + " is disabled, so cannot enable " + + QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED); + } + // If quorumpeer learner is not auth enabled then self won't be able to + // join quorum. So this condition is ensuring that the quorumpeer learner + // is also auth enabled while enabling quorum server require sasl. + if (!quorumLearnerRequireSasl && quorumServerRequireSasl) { + throw new IllegalArgumentException( + QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED + + " is disabled, so cannot enable " + + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED); + } // Reset to MIN_SNAP_RETAIN_COUNT if invalid (less than 3) // PurgeTxnLog.purge(File, File, int) will not allow to purge less // than 3. 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 e9c80070b3d..4ea7e54caed 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -130,26 +130,37 @@ public void runFromConfig(QuorumPeerConfig config) throws IOException { cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns()); - quorumPeer = new QuorumPeer(); + quorumPeer = new QuorumPeer(config.getServers(), + new File(config.getDataDir()), + new File(config.getDataLogDir()), + config.getElectionAlg(), + config.getServerId(), + config.getTickTime(), + config.getInitLimit(), + config.getSyncLimit(), + config.getQuorumListenOnAllIPs(), + cnxnFactory, + config.getQuorumVerifier()); quorumPeer.setClientPortAddress(config.getClientPortAddress()); - quorumPeer.setTxnFactory(new FileTxnSnapLog( - new File(config.getDataLogDir()), - new File(config.getDataDir()))); - quorumPeer.setQuorumPeers(config.getServers()); - quorumPeer.setElectionType(config.getElectionAlg()); - quorumPeer.setMyid(config.getServerId()); - quorumPeer.setTickTime(config.getTickTime()); quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout()); quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout()); - quorumPeer.setInitLimit(config.getInitLimit()); - quorumPeer.setSyncLimit(config.getSyncLimit()); - quorumPeer.setQuorumVerifier(config.getQuorumVerifier()); - quorumPeer.setCnxnFactory(cnxnFactory); quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory())); quorumPeer.setLearnerType(config.getPeerType()); quorumPeer.setSyncEnabled(config.getSyncEnabled()); - quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs()); - + + // sets quorum sasl authentication configurations + quorumPeer.setQuorumSaslEnabled(config.quorumEnableSasl); + if(quorumPeer.isQuorumSaslAuthEnabled()){ + quorumPeer.setQuorumServerSaslRequired(config.quorumServerRequireSasl); + quorumPeer.setQuorumLearnerSaslRequired(config.quorumLearnerRequireSasl); + quorumPeer.setQuorumServicePrincipal(config.quorumServicePrincipal); + quorumPeer.setQuorumServerLoginContext(config.quorumServerLoginContext); + quorumPeer.setQuorumLearnerLoginContext(config.quorumLearnerLoginContext); + } + + quorumPeer.setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize); + quorumPeer.initialize(); + quorumPeer.start(); quorumPeer.join(); } catch (InterruptedException e) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java new file mode 100644 index 00000000000..0af891c55e1 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java @@ -0,0 +1,33 @@ +/** + * 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.server.quorum.auth; + +import java.net.Socket; + +/** + * This class represents no authentication learner, it just return + * without performing any authentication. + */ +public class NullQuorumAuthLearner implements QuorumAuthLearner { + + @Override + public void authenticate(Socket sock, String hostname) { + return; // simply return don't require auth + } +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java new file mode 100644 index 00000000000..b26a54a81ea --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java @@ -0,0 +1,34 @@ +/** + * 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.server.quorum.auth; + +import java.io.DataInputStream; +import java.net.Socket; + +/** + * This class represents no authentication server, it just return + * without performing any authentication. + */ +public class NullQuorumAuthServer implements QuorumAuthServer { + + @Override + public void authenticate(final Socket sock, final DataInputStream din) { + return; // simply return don't require auth + } +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java new file mode 100644 index 00000000000..8bfa394decc --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java @@ -0,0 +1,96 @@ +/** + * 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.server.quorum.auth; + +import java.io.DataInputStream; +import java.io.IOException; +import org.apache.jute.BinaryInputArchive; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.zookeeper.server.quorum.QuorumAuthPacket; + +public class QuorumAuth { + private static final Logger LOG = LoggerFactory.getLogger(QuorumAuth.class); + + public static final String QUORUM_SASL_AUTH_ENABLED = "quorum.auth.enableSasl"; + public static final String QUORUM_SERVER_SASL_AUTH_REQUIRED = "quorum.auth.serverRequireSasl"; + public static final String QUORUM_LEARNER_SASL_AUTH_REQUIRED = "quorum.auth.learnerRequireSasl"; + + public static final String QUORUM_KERBEROS_SERVICE_PRINCIPAL = "quorum.auth.kerberos.servicePrincipal"; + public static final String QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE = "zkquorum/localhost"; + + public static final String QUORUM_LEARNER_SASL_LOGIN_CONTEXT = "quorum.auth.learner.saslLoginContext"; + public static final String QUORUM_LEARNER_SASL_LOGIN_CONTEXT_DFAULT_VALUE = "QuorumLearner"; + + public static final String QUORUM_SERVER_SASL_LOGIN_CONTEXT = "quorum.auth.server.saslLoginContext"; + public static final String QUORUM_SERVER_SASL_LOGIN_CONTEXT_DFAULT_VALUE = "QuorumServer"; + + static final String QUORUM_SERVER_PROTOCOL_NAME = "zookeeper-quorum"; + static final String QUORUM_SERVER_SASL_DIGEST = "zk-quorum-sasl-md5"; + static final String QUORUM_AUTH_MESSAGE_TAG = "qpconnect"; + + // this is negative, so that if a learner that does auth, connects to a + // server, it'll think the received packet is an authentication packet + public static final long QUORUM_AUTH_MAGIC_NUMBER = -0xa0dbcafecafe1234L; + + public enum Status { + IN_PROGRESS(0), SUCCESS(1), ERROR(-1); + private int status; + + Status(int status) { + this.status = status; + } + + static Status getStatus(int status) { + switch (status) { + case 0: + return IN_PROGRESS; + case 1: + return SUCCESS; + case -1: + return ERROR; + default: + LOG.error("Unknown status:{}!", status); + assert false : "Unknown status!"; + return ERROR; + } + } + + int status() { + return status; + } + } + + public static QuorumAuthPacket createPacket(Status status, byte[] response) { + return new QuorumAuthPacket(QUORUM_AUTH_MAGIC_NUMBER, + status.status(), response); + } + + public static boolean nextPacketIsAuth(DataInputStream din) + throws IOException { + din.mark(32); + BinaryInputArchive bia = new BinaryInputArchive(din); + boolean firstIsAuth = (bia.readLong("NO_TAG") + == QuorumAuth.QUORUM_AUTH_MAGIC_NUMBER); + din.reset(); + return firstIsAuth; + } +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java new file mode 100644 index 00000000000..af712574802 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java @@ -0,0 +1,40 @@ +/** + * 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.server.quorum.auth; + +import java.io.IOException; +import java.net.Socket; + +/** + * Interface for quorum learner authentication mechanisms. + */ +public interface QuorumAuthLearner { + + /** + * Performs an authentication step for the given socket connection. + * + * @param sock + * socket connection to other quorum peer server + * @param hostname + * host name of other quorum peer server + * @throws IOException + * if there is an authentication failure + */ + public void authenticate(Socket sock, String hostname) throws IOException; +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java new file mode 100644 index 00000000000..e9de8f00a5a --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java @@ -0,0 +1,41 @@ +/** + * 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.server.quorum.auth; + +import java.io.DataInputStream; +import java.io.IOException; +import java.net.Socket; + +/** + * Interface for quorum server authentication mechanisms. + */ +public interface QuorumAuthServer { + + /** + * Performs an authentication step for the given socket connection. + * + * @param sock + * socket connection to other quorum peer + * @param din + * stream used to read auth data send by the quorum learner + * @throws IOException if the server fails to authenticate connecting quorum learner + */ + public void authenticate(Socket sock, DataInputStream din) + throws IOException; +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java new file mode 100644 index 00000000000..fffb55ac496 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java @@ -0,0 +1,230 @@ +/** + * 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.server.quorum.auth; + +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import javax.security.auth.Subject; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginException; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.Login; +import org.apache.zookeeper.SaslClientCallbackHandler; +import org.apache.zookeeper.server.quorum.QuorumAuthPacket; +import org.apache.zookeeper.util.SecurityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SaslQuorumAuthLearner implements QuorumAuthLearner { + private static final Logger LOG = LoggerFactory + .getLogger(SaslQuorumAuthLearner.class); + + private final Login learnerLogin; + private final boolean quorumRequireSasl; + private final String quorumServicePrincipal; + + public SaslQuorumAuthLearner(boolean quorumRequireSasl, + String quorumServicePrincipal, String loginContext) + throws SaslException { + this.quorumRequireSasl = quorumRequireSasl; + this.quorumServicePrincipal = quorumServicePrincipal; + try { + AppConfigurationEntry entries[] = Configuration + .getConfiguration() + .getAppConfigurationEntry(loginContext); + if (entries == null || entries.length == 0) { + throw new LoginException("SASL-authentication failed because" + + " the specified JAAS configuration " + + "section '" + loginContext + + "' could not be found."); + } + this.learnerLogin = new Login(loginContext, + new SaslClientCallbackHandler(null, "QuorumLearner")); + this.learnerLogin.startThreadIfNeeded(); + } catch (LoginException e) { + throw new SaslException("Failed to initialize authentication mechanism using SASL", e); + } + } + + @Override + public void authenticate(Socket sock, String hostName) throws IOException { + if (!quorumRequireSasl) { // let it through, we don't require auth + LOG.info("Skipping SASL authentication as {}={}", + QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, + quorumRequireSasl); + return; + } + SaslClient sc = null; + String principalConfig = SecurityUtils + .getServerPrincipal(quorumServicePrincipal, hostName); + try { + DataOutputStream dout = new DataOutputStream( + sock.getOutputStream()); + DataInputStream din = new DataInputStream(sock.getInputStream()); + byte[] responseToken = new byte[0]; + sc = SecurityUtils.createSaslClient(learnerLogin.getSubject(), + principalConfig, + QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME, + QuorumAuth.QUORUM_SERVER_SASL_DIGEST, LOG, "QuorumLearner"); + + if (sc.hasInitialResponse()) { + responseToken = createSaslToken(new byte[0], sc, learnerLogin); + } + send(dout, responseToken); + QuorumAuthPacket authPacket = receive(din); + QuorumAuth.Status qpStatus = QuorumAuth.Status + .getStatus(authPacket.getStatus()); + while (!sc.isComplete()) { + switch (qpStatus) { + case SUCCESS: + responseToken = createSaslToken(authPacket.getToken(), sc, + learnerLogin); + // we're done; don't expect to send another BIND + if (responseToken != null) { + throw new SaslException("Protocol error: attempting to send response after completion"); + } + break; + case IN_PROGRESS: + responseToken = createSaslToken(authPacket.getToken(), sc, + learnerLogin); + send(dout, responseToken); + authPacket = receive(din); + qpStatus = QuorumAuth.Status + .getStatus(authPacket.getStatus()); + break; + case ERROR: + throw new SaslException( + "Authentication failed against server addr: " + + sock.getRemoteSocketAddress()); + default: + LOG.warn("Unknown status:{}!", qpStatus); + throw new SaslException( + "Authentication failed against server addr: " + + sock.getRemoteSocketAddress()); + } + } + + // Validate status code at the end of authentication exchange. + checkAuthStatus(sock, qpStatus); + } finally { + if (sc != null) { + try { + sc.dispose(); + } catch (SaslException e) { + LOG.error("SaslClient dispose() failed", e); + } + } + } + return; + } + + private void checkAuthStatus(Socket sock, QuorumAuth.Status qpStatus) + throws SaslException { + if (qpStatus == QuorumAuth.Status.SUCCESS) { + LOG.info("Successfully completed the authentication using SASL. server addr: {}, status: {}", + sock.getRemoteSocketAddress(), qpStatus); + } else { + throw new SaslException("Authentication failed against server addr: " + + sock.getRemoteSocketAddress() + ", qpStatus: " + + qpStatus); + } + } + + private QuorumAuthPacket receive(DataInputStream din) throws IOException { + QuorumAuthPacket authPacket = new QuorumAuthPacket(); + BinaryInputArchive bia = BinaryInputArchive.getArchive(din); + authPacket.deserialize(bia, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); + return authPacket; + } + + private void send(DataOutputStream dout, byte[] response) + throws IOException { + QuorumAuthPacket authPacket; + BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); + BinaryOutputArchive boa = BinaryOutputArchive + .getArchive(bufferedOutput); + if (response != null && response.length < 0) { + throw new IOException("Response length < 0"); + } else if (response == null) { + authPacket = QuorumAuth.createPacket( + QuorumAuth.Status.IN_PROGRESS, response); + } else { + authPacket = QuorumAuth.createPacket( + QuorumAuth.Status.IN_PROGRESS, response); + } + + boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); + bufferedOutput.flush(); + } + + // TODO: need to consolidate the #createSaslToken() implementation between ZooKeeperSaslClient#createSaslToken(). + private byte[] createSaslToken(final byte[] saslToken, + final SaslClient saslClient, final Login login) + throws SaslException { + if (saslToken == null) { + throw new SaslException( + "Error in authenticating with a Zookeeper Quorum member: the quorum member's saslToken is null."); + } + if (login.getSubject() != null) { + synchronized (login) { + try { + final byte[] retval = Subject.doAs(login.getSubject(), + new PrivilegedExceptionAction() { + public byte[] run() throws SaslException { + LOG.debug("saslClient.evaluateChallenge(len=" + + saslToken.length + ")"); + return saslClient.evaluateChallenge(saslToken); + } + }); + return retval; + } catch (PrivilegedActionException e) { + String error = "An error: (" + e + + ") occurred when evaluating Zookeeper Quorum Member's " + + " received SASL token."; + // Try to provide hints to use about what went wrong so they + // can fix their configuration. + // TODO: introspect about e: look for GSS information. + final String UNKNOWN_SERVER_ERROR_TEXT = "(Mechanism level: Server not found in Kerberos database (7) - UNKNOWN_SERVER)"; + if (e.toString().indexOf(UNKNOWN_SERVER_ERROR_TEXT) > -1) { + error += " This may be caused by Java's being unable to resolve the Zookeeper Quorum Member's" + + " hostname correctly. You may want to try to adding" + + " '-Dsun.net.spi.nameservice.provider.1=dns,sun' to your server's JVMFLAGS environment."; + } + LOG.error(error); + throw new SaslException(error); + } + } + } else { + throw new SaslException( + "Cannot make SASL token without subject defined. " + + "For diagnosis, please look for WARNs and ERRORs in your log related to the Login class."); + } + } +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java new file mode 100644 index 00000000000..8430da20ded --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java @@ -0,0 +1,180 @@ +/** + * 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.server.quorum.auth; + +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.util.Set; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.Login; +import org.apache.zookeeper.server.quorum.QuorumAuthPacket; +import org.apache.zookeeper.util.SecurityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SaslQuorumAuthServer implements QuorumAuthServer { + + private static final Logger LOG = LoggerFactory + .getLogger(SaslQuorumAuthServer.class); + + private final static int MAX_RETRIES = 5; + private final Login serverLogin; + private final boolean quorumRequireSasl; + + public SaslQuorumAuthServer(boolean quorumRequireSasl, String loginContext, Set authzHosts) + throws SaslException { + this.quorumRequireSasl = quorumRequireSasl; + try { + AppConfigurationEntry entries[] = Configuration.getConfiguration() + .getAppConfigurationEntry(loginContext); + if (entries == null || entries.length == 0) { + throw new LoginException("SASL-authentication failed" + + " because the specified JAAS configuration " + + "section '" + loginContext + "' could not be found."); + } + SaslQuorumServerCallbackHandler saslServerCallbackHandler = new SaslQuorumServerCallbackHandler( + Configuration.getConfiguration(), loginContext, authzHosts); + serverLogin = new Login(loginContext, saslServerCallbackHandler); + serverLogin.startThreadIfNeeded(); + } catch (Throwable e) { + throw new SaslException( + "Failed to initialize authentication mechanism using SASL", + e); + } + } + + @Override + public void authenticate(Socket sock, DataInputStream din) + throws SaslException { + DataOutputStream dout = null; + SaslServer ss = null; + try { + if (!QuorumAuth.nextPacketIsAuth(din)) { + if (quorumRequireSasl) { + throw new SaslException("Learner not trying to authenticate" + + " and authentication is required"); + } else { + // let it through, we don't require auth + return; + } + } + + byte[] token = receive(din); + int tries = 0; + dout = new DataOutputStream(sock.getOutputStream()); + byte[] challenge = null; + ss = SecurityUtils.createSaslServer(serverLogin.getSubject(), + QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME, + QuorumAuth.QUORUM_SERVER_SASL_DIGEST, serverLogin.callbackHandler, + LOG); + while (!ss.isComplete()) { + challenge = ss.evaluateResponse(token); + if (!ss.isComplete()) { + // limited number of retries. + if (++tries > MAX_RETRIES) { + send(dout, challenge, QuorumAuth.Status.ERROR); + LOG.warn("Failed to authenticate using SASL, server addr: {}, retries={} exceeded.", + sock.getRemoteSocketAddress(), tries); + break; + } + send(dout, challenge, QuorumAuth.Status.IN_PROGRESS); + token = receive(din); + } + } + // Authentication exchange has completed + if (ss.isComplete()) { + send(dout, challenge, QuorumAuth.Status.SUCCESS); + LOG.info("Successfully completed the authentication using SASL. learner addr: {}", + sock.getRemoteSocketAddress()); + } + } catch (Exception e) { + try { + if (dout != null) { + // send error message to the learner + send(dout, new byte[0], QuorumAuth.Status.ERROR); + } + } catch (IOException ioe) { + LOG.warn("Exception while sending failed status", ioe); + } + // If sasl is not required, when a server initializes a + // connection it will try to log in, but it will also + // accept connections that do not start with a sasl + // handshake. + if (quorumRequireSasl) { + LOG.error("Failed to authenticate using SASL", e); + throw new SaslException( + "Failed to authenticate using SASL: " + e.getMessage()); + } else { + LOG.warn("Failed to authenticate using SASL", e); + LOG.warn("Maintaining learner connection despite SASL authentication failure." + + " server addr: {}, {}: {}", + new Object[] { sock.getRemoteSocketAddress(), + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, + quorumRequireSasl }); + return; // let it through, we don't require auth + } + } finally { + if (ss != null) { + try { + ss.dispose(); + } catch (SaslException e) { + LOG.error("SaslServer dispose() failed", e); + } + } + } + return; + } + + private byte[] receive(DataInputStream din) throws IOException { + QuorumAuthPacket authPacket = new QuorumAuthPacket(); + BinaryInputArchive bia = BinaryInputArchive.getArchive(din); + authPacket.deserialize(bia, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); + return authPacket.getToken(); + } + + private void send(DataOutputStream dout, byte[] challenge, + QuorumAuth.Status s) throws IOException { + BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); + BinaryOutputArchive boa = BinaryOutputArchive + .getArchive(bufferedOutput); + QuorumAuthPacket authPacket; + if (challenge != null && challenge.length < 0) { + throw new IOException("Response length < 0"); + } else if (challenge == null && s != QuorumAuth.Status.SUCCESS) { + authPacket = QuorumAuth.createPacket( + QuorumAuth.Status.IN_PROGRESS, challenge); + } else { + authPacket = QuorumAuth.createPacket(s, challenge); + } + + boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); + bufferedOutput.flush(); + } +} diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java new file mode 100644 index 00000000000..3e71bb1774b --- /dev/null +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java @@ -0,0 +1,148 @@ +/** + * 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.server.quorum.auth; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is used by the SASL mechanisms to get further information to complete + * the authentication. For example, a SASL mechanism might use this callback + * handler to do verification operation. This is used by the QuorumServer to + * perform the mutual quorum peer authentication. + */ +public class SaslQuorumServerCallbackHandler implements CallbackHandler { + private static final String USER_PREFIX = "user_"; + private static final Logger LOG = LoggerFactory.getLogger(SaslQuorumServerCallbackHandler.class); + + private String userName; + private final Map credentials = new HashMap(); + private final Set authzHosts; + + public SaslQuorumServerCallbackHandler(Configuration configuration, + String serverSection, Set authzHosts) throws IOException { + AppConfigurationEntry configurationEntries[] = configuration.getAppConfigurationEntry(serverSection); + + if (configurationEntries == null) { + String errorMessage = "Could not find a '" + serverSection + "' entry in this configuration: Server cannot start."; + LOG.error(errorMessage); + throw new IOException(errorMessage); + } + credentials.clear(); + for(AppConfigurationEntry entry: configurationEntries) { + Map options = entry.getOptions(); + // Populate DIGEST-MD5 user -> password map with JAAS configuration entries from the "QuorumServer" section. + // Usernames are distinguished from other options by prefixing the username with a "user_" prefix. + for(Map.Entry pair : options.entrySet()) { + String key = pair.getKey(); + if (key.startsWith(USER_PREFIX)) { + String userName = key.substring(USER_PREFIX.length()); + credentials.put(userName,(String)pair.getValue()); + } + } + } + + // authorized host lists + this.authzHosts = authzHosts; + } + + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + handleNameCallback((NameCallback) callback); + } else if (callback instanceof PasswordCallback) { + handlePasswordCallback((PasswordCallback) callback); + } else if (callback instanceof RealmCallback) { + handleRealmCallback((RealmCallback) callback); + } else if (callback instanceof AuthorizeCallback) { + handleAuthorizeCallback((AuthorizeCallback) callback); + } + } + } + + private void handleNameCallback(NameCallback nc) { + // check to see if this user is in the user password database. + if (credentials.get(nc.getDefaultName()) == null) { + LOG.warn("User '{}' not found in list of DIGEST-MD5 authenticateable users.", + nc.getDefaultName()); + return; + } + nc.setName(nc.getDefaultName()); + userName = nc.getDefaultName(); + } + + private void handlePasswordCallback(PasswordCallback pc) { + if (credentials.containsKey(userName) ) { + pc.setPassword(credentials.get(userName).toCharArray()); + } else { + LOG.warn("No password found for user: {}", userName); + } + } + + private void handleRealmCallback(RealmCallback rc) { + LOG.debug("QuorumLearner supplied realm: {}", rc.getDefaultText()); + rc.setText(rc.getDefaultText()); + } + + private void handleAuthorizeCallback(AuthorizeCallback ac) { + String authenticationID = ac.getAuthenticationID(); + String authorizationID = ac.getAuthorizationID(); + + boolean authzFlag = false; + // 1. Matches authenticationID and authorizationID + authzFlag = authenticationID.equals(authorizationID); + + // 2. Verify whether the connecting host is present in authorized hosts. + // If not exists, then connecting peer is not authorized to join the + // ensemble and will reject it. + if (authzFlag) { + String[] components = authorizationID.split("[/@]"); + if (components.length == 3) { + authzFlag = authzHosts.contains(components[1]); + } + if (!authzFlag) { + LOG.error("SASL authorization completed, {} is not authorized to connect", + components[1]); + } + } + + // Sets authorization flag + ac.setAuthorized(authzFlag); + if (ac.isAuthorized()) { + ac.setAuthorizedID(authorizationID); + LOG.info("Successfully authenticated learner: authenticationID={}; authorizationID={}.", + authenticationID, authorizationID); + } + LOG.debug("SASL authorization completed, authorized flag set to {}", ac.isAuthorized()); + } +} diff --git a/src/java/main/org/apache/zookeeper/util/SecurityUtils.java b/src/java/main/org/apache/zookeeper/util/SecurityUtils.java new file mode 100644 index 00000000000..67484e4e03f --- /dev/null +++ b/src/java/main/org/apache/zookeeper/util/SecurityUtils.java @@ -0,0 +1,298 @@ +/** + * 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.util; + +import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.zookeeper.SaslClientCallbackHandler; +import org.apache.zookeeper.server.auth.KerberosName; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.slf4j.Logger; + +public final class SecurityUtils { + + public static final String QUORUM_HOSTNAME_PATTERN = "_HOST"; + + /** + * Create an instance of a SaslClient. It will return null if there is an exception. + * + * @param subject subject + * @param servicePrincipal principal + * @param protocol name of the protocol for which the authentication is being performed + * @param serverName name of the server to authenticate to + * @param LOG logger + * @param entity can be either zookeeper client or quorum learner + * + * @return saslclient object + * @throws SaslException + */ + public static SaslClient createSaslClient(final Subject subject, + final String servicePrincipal, final String protocol, + final String serverName, final Logger LOG, final String entity) throws SaslException { + SaslClient saslClient; + // Use subject.getPrincipals().isEmpty() as an indication of which SASL + // mechanism to use: if empty, use DIGEST-MD5; otherwise, use GSSAPI. + if (subject.getPrincipals().isEmpty()) { + // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism + // instead. + LOG.info("{} will use DIGEST-MD5 as SASL mechanism.", entity); + String[] mechs = { "DIGEST-MD5" }; + String username = (String) (subject.getPublicCredentials() + .toArray()[0]); + String password = (String) (subject.getPrivateCredentials() + .toArray()[0]); + // 'domain' parameter is hard-wired between the server and client + saslClient = Sasl.createSaslClient(mechs, username, protocol, + serverName, null, new SaslClientCallbackHandler(password, entity)); + return saslClient; + } else { // GSSAPI. + final Object[] principals = subject.getPrincipals().toArray(); + // determine client principal from subject. + final Principal clientPrincipal = (Principal) principals[0]; + boolean usingNativeJgss = Boolean + .getBoolean("sun.security.jgss.native"); + if (usingNativeJgss) { + // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + // """ + // In addition, when performing operations as a particular + // Subject, e.g. Subject.doAs(...) or + // Subject.doAsPrivileged(...), + // the to-be-used GSSCredential should be added to Subject's + // private credential set. Otherwise, the GSS operations will + // fail since no credential is found. + // """ + try { + GSSManager manager = GSSManager.getInstance(); + Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); + GSSCredential cred = manager.createCredential(null, + GSSContext.DEFAULT_LIFETIME, krb5Mechanism, + GSSCredential.INITIATE_ONLY); + subject.getPrivateCredentials().add(cred); + LOG.debug("Added private credential to {} principal name: '{}'", + entity, clientPrincipal); + } catch (GSSException ex) { + LOG.warn("Cannot add private credential to subject; " + + "authentication at the server may fail", ex); + } + } + final KerberosName clientKerberosName = new KerberosName( + clientPrincipal.getName()); + // assume that server and client are in the same realm (by default; + // unless the system property + // "zookeeper.server.realm" is set). + String serverRealm = System.getProperty("zookeeper.server.realm", + clientKerberosName.getRealm()); + KerberosName serviceKerberosName = new KerberosName( + servicePrincipal + "@" + serverRealm); + final String serviceName = serviceKerberosName.getServiceName(); + final String serviceHostname = serviceKerberosName.getHostName(); + final String clientPrincipalName = clientKerberosName.toString(); + try { + saslClient = Subject.doAs(subject, + new PrivilegedExceptionAction() { + public SaslClient run() throws SaslException { + LOG.info("{} will use GSSAPI as SASL mechanism.", entity); + String[] mechs = { "GSSAPI" }; + LOG.debug("creating sasl client: {}={};service={};serviceHostname={}", + new Object[] { entity, clientPrincipalName, serviceName, serviceHostname }); + SaslClient saslClient = Sasl.createSaslClient( + mechs, clientPrincipalName, serviceName, + serviceHostname, null, + new SaslClientCallbackHandler(null, entity)); + return saslClient; + } + }); + return saslClient; + } catch (Exception e) { + LOG.error("Exception while trying to create SASL client", e); + return null; + } + } + } + + /** + * Create an instance of a SaslServer. It will return null if there is an exception. + * + * @param subject subject + * @param protocol protocol + * @param serverName server name + * @param callbackHandler login callback handler + * @param LOG logger + * @return sasl server object + */ + public static SaslServer createSaslServer(final Subject subject, + final String protocol, final String serverName, + final CallbackHandler callbackHandler, final Logger LOG) { + if (subject != null) { + // server is using a JAAS-authenticated subject: determine service + // principal name and hostname from zk server's subject. + if (subject.getPrincipals().size() > 0) { + try { + final Object[] principals = subject.getPrincipals() + .toArray(); + final Principal servicePrincipal = (Principal) principals[0]; + + // e.g. servicePrincipalNameAndHostname := + // "zookeeper/myhost.foo.com@FOO.COM" + final String servicePrincipalNameAndHostname = servicePrincipal + .getName(); + + int indexOf = servicePrincipalNameAndHostname.indexOf("/"); + + // e.g. servicePrincipalName := "zookeeper" + final String servicePrincipalName = servicePrincipalNameAndHostname + .substring(0, indexOf); + + // e.g. serviceHostnameAndKerbDomain := + // "myhost.foo.com@FOO.COM" + final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname + .substring(indexOf + 1, + servicePrincipalNameAndHostname.length()); + + indexOf = serviceHostnameAndKerbDomain.indexOf("@"); + // e.g. serviceHostname := "myhost.foo.com" + final String serviceHostname = serviceHostnameAndKerbDomain + .substring(0, indexOf); + + // TODO: should depend on zoo.cfg specified mechs, but if + // subject is non-null, it can be assumed to be GSSAPI. + final String mech = "GSSAPI"; + + LOG.debug("serviceHostname is '" + serviceHostname + "'"); + LOG.debug("servicePrincipalName is '" + servicePrincipalName + + "'"); + LOG.debug("SASL mechanism(mech) is '" + mech + "'"); + + boolean usingNativeJgss = Boolean + .getBoolean("sun.security.jgss.native"); + if (usingNativeJgss) { + // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + // """ + // In addition, when performing operations as a + // particular + // Subject, e.g. Subject.doAs(...) or + // Subject.doAsPrivileged(...), the to-be-used + // GSSCredential should be added to Subject's + // private credential set. Otherwise, the GSS operations + // will fail since no credential is found. + // """ + try { + GSSManager manager = GSSManager.getInstance(); + Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); + GSSName gssName = manager.createName( + servicePrincipalName + "@" + + serviceHostname, + GSSName.NT_HOSTBASED_SERVICE); + GSSCredential cred = manager.createCredential( + gssName, GSSContext.DEFAULT_LIFETIME, + krb5Mechanism, GSSCredential.ACCEPT_ONLY); + subject.getPrivateCredentials().add(cred); + LOG.debug("Added private credential to service principal name: '{}'," + + " GSSCredential name: {}", servicePrincipalName, cred.getName()); + } catch (GSSException ex) { + LOG.warn("Cannot add private credential to subject; " + + "clients authentication may fail", ex); + } + } + try { + return Subject.doAs(subject, + new PrivilegedExceptionAction() { + public SaslServer run() { + try { + SaslServer saslServer; + saslServer = Sasl.createSaslServer( + mech, servicePrincipalName, + serviceHostname, null, + callbackHandler); + return saslServer; + } catch (SaslException e) { + LOG.error("Zookeeper Server failed to create a SaslServer to interact with a client during session initiation: ", e); + return null; + } + } + }); + } catch (PrivilegedActionException e) { + // TODO: exit server at this point(?) + LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException exception while creating a SaslServer using a JAAS principal context:", e); + } + } catch (IndexOutOfBoundsException e) { + LOG.error("server principal name/hostname determination error: ", e); + } + } else { + // JAAS non-GSSAPI authentication: assuming and supporting only + // DIGEST-MD5 mechanism for now. + // TODO: use 'authMech=' value in zoo.cfg. + try { + SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5", + protocol, serverName, null, callbackHandler); + return saslServer; + } catch (SaslException e) { + LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e); + } + } + } + return null; + } + + /** + * Convert Kerberos principal name pattern to valid Kerberos principal name. + * If the principal name contains hostname pattern "_HOST" then it replaces + * with the given hostname, which should be fully-qualified domain name. + * + * @param principalConfig + * the Kerberos principal name conf value to convert + * @param hostname + * the fully-qualified domain name used for substitution + * @return converted Kerberos principal name + */ + public static String getServerPrincipal(String principalConfig, + String hostname) { + String[] components = getComponents(principalConfig); + if (components == null || components.length != 2 + || !components[1].equals(QUORUM_HOSTNAME_PATTERN)) { + return principalConfig; + } else { + return replacePattern(components, hostname); + } + } + + private static String[] getComponents(String principalConfig) { + if (principalConfig == null) + return null; + return principalConfig.split("[/]"); + } + + private static String replacePattern(String[] components, String hostname) { + return components[0] + "/" + hostname.toLowerCase(); + } +} diff --git a/src/java/test/data/kerberos/minikdc-krb5.conf b/src/java/test/data/kerberos/minikdc-krb5.conf new file mode 100644 index 00000000000..43ec7c4c4e9 --- /dev/null +++ b/src/java/test/data/kerberos/minikdc-krb5.conf @@ -0,0 +1,30 @@ +# +# 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. +# +# This resource is originally from HDFS, see the similarly named files there +# in case of bug fixing, history, etc. +# Branch : trunk +# Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33 +# +[libdefaults] + default_realm = {0} + udp_preference_limit = 1 + +[realms] + {0} = '{' + kdc = {1}:{2} + '}' \ No newline at end of file diff --git a/src/java/test/data/kerberos/minikdc.ldiff b/src/java/test/data/kerberos/minikdc.ldiff new file mode 100644 index 00000000000..20c8d7759ae --- /dev/null +++ b/src/java/test/data/kerberos/minikdc.ldiff @@ -0,0 +1,52 @@ +# +# 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. +# +# This resource is originally from HDFS, see the similarly named files there +# in case of bug fixing, history, etc. +# Branch : trunk +# Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33 +# +dn: ou=users,dc=${0},dc=${1} +objectClass: organizationalUnit +objectClass: top +ou: users + +dn: uid=krbtgt,ou=users,dc=${0},dc=${1} +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: KDC Service +sn: Service +uid: krbtgt +userPassword: secret +krb5PrincipalName: krbtgt/${2}.${3}@${2}.${3} +krb5KeyVersionNumber: 0 + +dn: uid=ldap,ou=users,dc=${0},dc=${1} +objectClass: top +objectClass: person +objectClass: inetOrgPerson +objectClass: krb5principal +objectClass: krb5kdcentry +cn: LDAP +sn: Service +uid: ldap +userPassword: secret +krb5PrincipalName: ldap/${4}@${2}.${3} +krb5KeyVersionNumber: 0 \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java index 8db7fa858b5..a82a728e26e 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/CnxManagerTest.java @@ -87,7 +87,7 @@ class CnxManagerThread extends Thread { public void run(){ try { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[0], peerTmpdir[0], peerClientPort[0], 3, 0, 1000, 2, 2); - QuorumCnxManager cnxManager = new QuorumCnxManager(peer); + QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if(listener != null){ listener.start(); @@ -131,7 +131,7 @@ public void testCnxManager() throws Exception { thread.start(); QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2); - QuorumCnxManager cnxManager = new QuorumCnxManager(peer); + QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if(listener != null){ listener.start(); @@ -175,7 +175,7 @@ public void testCnxManagerTimeout() throws Exception { peerTmpdir[2] = ClientBase.createTmpDir(); QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2); - QuorumCnxManager cnxManager = new QuorumCnxManager(peer); + QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if(listener != null){ listener.start(); @@ -202,7 +202,7 @@ public void testCnxManagerTimeout() throws Exception { @Test public void testCnxManagerSpinLock() throws Exception { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2); - QuorumCnxManager cnxManager = new QuorumCnxManager(peer); + QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if(listener != null){ listener.start(); @@ -258,7 +258,10 @@ public void testCnxManagerSpinLock() throws Exception { class TestCnxManager extends QuorumCnxManager { TestCnxManager(QuorumPeer self) { - super(self); + super(self.getId(), self.getView(), self.authServer, + self.authLearner, self.tickTime * self.syncLimit, + self.getQuorumListenOnAllIPs(), + self.quorumCnxnThreadsSize, false); } boolean senderWorkerMapContains(Long l){ @@ -359,7 +362,7 @@ public void testCnxFromFutureVersion() throws Exception { @Test public void testSocketTimeout() throws Exception { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 2000, 2, 2); - QuorumCnxManager cnxManager = new QuorumCnxManager(peer); + QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if(listener != null){ listener.start(); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java index c1259d12895..c0ab3eaaf6d 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java @@ -113,7 +113,7 @@ public void testBackwardElectionRound() throws Exception { * Start mock server 1 */ QuorumPeer mockPeer = new QuorumPeer(peers, tmpdir[1], tmpdir[1], port[1], 3, 1, 1000, 2, 2); - cnxManagers[0] = new QuorumCnxManager(mockPeer); + cnxManagers[0] = mockPeer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManagers[0].listener; listener.start(); @@ -124,7 +124,7 @@ public void testBackwardElectionRound() throws Exception { * Start mock server 2 */ mockPeer = new QuorumPeer(peers, tmpdir[2], tmpdir[2], port[2], 3, 2, 1000, 2, 2); - cnxManagers[1] = new QuorumCnxManager(mockPeer); + cnxManagers[1] = mockPeer.createCnxnManager(); listener = cnxManagers[1].listener; listener.start(); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java index 72e4fc97db9..f1c04cad763 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLECompatibilityTest.java @@ -288,7 +288,7 @@ public void testBackwardCompatibility() QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); peer.setPeerState(ServerState.LOOKING); - QuorumCnxManager mng = new QuorumCnxManager(peer); + QuorumCnxManager mng = peer.createCnxnManager(); /* * Check that it generates an internal notification correctly @@ -325,7 +325,7 @@ public void testForwardCompatibility() QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); peer.setPeerState(ServerState.LOOKING); - QuorumCnxManager mng = new QuorumCnxManager(peer); + QuorumCnxManager mng = peer.createCnxnManager(); /* * Check that it generates an internal notification correctly diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java index a4c0cb0bd64..3d4a02c2bad 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java @@ -90,7 +90,7 @@ public void tearDown(){ @Test public void testDontCare() { - MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + MockFLE fle = new MockFLE(peer, peer.createCnxnManager()); HashMap votes = new HashMap(); votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 2, ServerState.FOLLOWING)); @@ -104,7 +104,7 @@ public void testDontCare() { @Test public void testDontCareVersion() { - MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + MockFLE fle = new MockFLE(peer, peer.createCnxnManager()); HashMap votes = new HashMap(); votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.FOLLOWING)); @@ -118,7 +118,7 @@ public void testDontCareVersion() { @Test public void testLookingNormal() { - MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + MockFLE fle = new MockFLE(peer, peer.createCnxnManager()); HashMap votes = new HashMap(); votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); @@ -132,7 +132,7 @@ public void testLookingNormal() { @Test public void testLookingDiffRounds() { - MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + MockFLE fle = new MockFLE(peer, peer.createCnxnManager()); HashMap votes = new HashMap(); votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.LOOKING)); @@ -188,7 +188,7 @@ FastLeaderElection.Notification genNotification(int version, @Test public void testOutofElection() { - MockFLE fle = new MockFLE(peer, new QuorumCnxManager(peer)); + MockFLE fle = new MockFLE(peer, peer.createCnxnManager()); HashMap outofelection = new HashMap(); /* diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java index 39a53cad397..190785c4217 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLELostMessageTest.java @@ -101,7 +101,7 @@ void mockServer() throws InterruptedException, IOException { * Create an instance of the connection manager */ QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2); - cnxManager = new QuorumCnxManager(peer); + cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; listener.start(); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java index 2ae57ce6a5b..fd08d21339d 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/LearnerTest.java @@ -69,8 +69,8 @@ public void startup() { } class SimpleLearner extends Learner { SimpleLearner(FileTxnSnapLog ftsl) throws IOException { - self = new QuorumPeer(); - zk = new SimpleLearnerZooKeeperServer(ftsl, self); + self = QuorumPeer.testingQuorumPeer(); + zk = new SimpleLearnerZooKeeperServer(ftsl, self); ((SimpleLearnerZooKeeperServer)zk).learner = this; } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java new file mode 100644 index 00000000000..6b6c0b4c331 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java @@ -0,0 +1,925 @@ +/** + * 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.server.quorum; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.security.sasl.SaslException; + +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.server.ServerCnxn; +import org.apache.zookeeper.server.ServerCnxnFactory; +import org.apache.zookeeper.server.ZKDatabase; +import org.apache.zookeeper.server.ZooKeeperServer; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthServer; +import org.apache.zookeeper.server.quorum.auth.QuorumAuth; +import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; +import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthLearner; +import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthServer; +import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; +import org.apache.zookeeper.test.ClientBase; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QuorumCnxManagerTest extends ZKTestCase { + private static final Logger LOG = LoggerFactory.getLogger(QuorumCnxManagerTest.class); + private int count; + private HashMap peers; + private int peerQuorumPort[]; + private int peerClientPort[]; + private ThreadPoolExecutor executor; + /** + * The maximum number of threads to allow in the connectionExecutors thread + * pool which will be used to initiate quorum server connections. Defaulting to 20. + * TODO: Need to tune this param. + */ + private final int quorumCnxnThreadsSize = 20; + private Set authzHosts; + + private static File saslConfigFile = null; + + @BeforeClass + public static void setupSasl() throws Exception { + String jaasEntries = new String("" + + "QuorumServer {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_test=\"mypassword\";\n" + + "};\n" + + "QuorumLearner {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"test\"\n" + + " password=\"mypassword\";\n" + + "};\n" + + "QuorumLearnerInvalid {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"test\"\n" + + " password=\"invalid\";\n" + + "};\n"); + + saslConfigFile = File.createTempFile("jaas.", ".conf"); + FileWriter fwriter = new FileWriter(saslConfigFile); + fwriter.write(jaasEntries); + fwriter.close(); + System.setProperty("java.security.auth.login.config", + saslConfigFile.getAbsolutePath()); + } + + @AfterClass + public static void cleanupSasl() throws Exception { + if (saslConfigFile != null) { + saslConfigFile.delete(); + } + } + + @Before + public void setUp() throws Exception { + this.count = 3; + this.peers = new HashMap(count); + peerQuorumPort = new int[count]; + peerClientPort = new int[count]; + authzHosts = new HashSet(); + for(int i = 0; i < count; i++) { + peerQuorumPort[i] = PortAssignment.unique(); + peerClientPort[i] = PortAssignment.unique(); + QuorumServer qs = new QuorumServer(i, "0.0.0.0", + peerQuorumPort[i], PortAssignment.unique(), null); + peers.put(Long.valueOf(i), qs); + authzHosts.add(qs.hostname); + } + executor = new ThreadPoolExecutor(3, 10, + 60, TimeUnit.SECONDS, new SynchronousQueue()); + } + + @After + public void tearDown() throws Exception { + if (executor != null) { + executor.shutdownNow(); + } + } + + @Test(timeout = 30000) + public void testNoAuthConnection() throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0); + QuorumCnxManager peer1 = createAndStartManager(1); + + peer0.connectOne(1); + assertEventuallyConnected(peer0, 1); + } + + @Test(timeout = 30000) + public void testAuthConnection() throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearner", true, true); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearner", true, true); + peer0.connectOne(1); + assertEventuallyConnected(peer0, 1); + } + + /** + * Peer0 has no auth configured, Peer1 has auth configured. + * Peer1 connects to peer0, because null auth server sees an auth packet and connection succeeds. + * Peer0 connects to peer1, but connection isn't initiated because + * peer0's sid is lower than peer1's + */ + @Test(timeout = 30000) + public void testClientAuthAgainstNoAuthServerWithLowerSid() + throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearner", false, false); + peer1.connectOne(0); + peer0.connectOne(1); + assertEventuallyConnected(peer0, 1); + } + + /** + * Peer0 has auth configured, Peer1 has no auth configured. + * Peer1 connects to peer0, but is disconnected, because peer1's sid is + * higher than peer0. + * Peer0 connects to peer1, but is disconnected, because peer1 cannot + * handle auth. + */ + @Test(timeout = 30000) + public void testClientAuthAgainstNoAuthServerWithHigherSid() + throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearner", false, false); + QuorumCnxManager peer1 = createAndStartManager(1); + peer0.connectOne(1); + peer1.connectOne(0); + assertEventuallyConnected(peer0, 1); + } + + /** + * No auth learner connects to a server that requires auth, when the server + * has a higher sid. + * The connection should fail in both directions. + */ + @Test(timeout = 30000) + public void testNoAuthLearnerConnectToAuthRequiredServerWithLowerSid() + throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearner", true, true); + QuorumCnxManager peer1 = createAndStartManager(1); + peer0.connectOne(1); + peer1.connectOne(0); + assertEventuallyNotConnected(peer0, 1); + } + + /** + * No auth learner connects to a server that requires auth, when the server + * has a higher sid. + * The connection should fail in both directions. + */ + @Test(timeout = 30000) + public void testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid() + throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearner", true, true); + peer0.connectOne(1); + peer1.connectOne(0); + assertEventuallyNotConnected(peer0, 1); + } + + /** + * An auth learner connects to a auth server, but the credentials are bad. + * The peer with the higher sid has the bad credentials. + * The connection will be denied. + */ + @Test(timeout = 30000) + public void testAuthLearnerBadCredToAuthRequiredServerWithLowerSid() + throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearner", true, true); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearnerInvalid", true, true); + peer0.connectOne(1); + peer1.connectOne(0); + + assertEventuallyNotConnected(peer0, 1); + } + + /** + * An auth learner connects to a auth server, but the credentials are bad. + * The peer with the lower sid has the bad credentials. + * The connection will work, because peer1 is connecting to peer0. + */ + @Test(timeout = 30000) + public void testAuthLearnerBadCredToAuthRequiredServerWithHigherSid() + throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearnerInvalid", true, true); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearner", true, true); + peer0.connectOne(1); + peer1.connectOne(0); + assertEventuallyConnected(peer0, 1); + assertEventuallyConnected(peer1, 0); + } + + /** + * An auth learner connects to a auth server, but the credentials are bad. + * The connection should fail in both directions. + */ + @Test(timeout = 30000) + public void testAuthLearnerBadCredToNoAuthServerWithHigherSid() throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearner", false, false); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearnerInvalid", true, true); + peer1.connectOne(0); + assertEventuallyNotConnected(peer1, 0); + } + + /** + * An auth learner connects to a auth server, but the credentials are bad. + * The peer with the lower sid has the bad credentials. + * The connection will work, because peer0 is connecting to peer1 and peer1 + * server doesn't require sasl + */ + @Test(timeout = 30000) + public void testAuthLearnerBadCredToNoAuthServerWithLowerSid() throws Exception { + QuorumCnxManager peer0 = createAndStartManager(0, "QuorumServer", + "QuorumLearnerInvalid", true, true); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", + "QuorumLearner", false, true); + peer0.connectOne(1); + assertEventuallyConnected(peer0, 1); + assertEventuallyConnected(peer1, 0); + } + + /** + * Test verifies that the LearnerHandler should authenticate the connecting + * quorumpeer. Here its simulating authentication failure and it should throw + * SaslException + */ + @Test(timeout = 30000) + public void testLearnerHandlerAuthFailed() throws Exception { + File testData = ClientBase.createTmpDir(); + Socket leaderSocket = getSocketPair(); + File tmpDir = File.createTempFile("test", ".dir", testData); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, true, false, true, + "QuorumLearner", "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + leader = createLeader(tmpDir, peer); + peer.leader = leader; + + // authentication failed as qpserver didn't get auth packet from qpclient. + try { + new LearnerHandler(leaderSocket, + new BufferedInputStream(leaderSocket.getInputStream()), leader); + Assert.fail("Must throw exception as there is an authentication failure"); + } catch (SaslException e){ + Assert.assertEquals("Mistakely added to learners", 0, + leader.getLearners().size()); + } + ClientBase.recursiveDelete(testData); + } + + /** + * Test verifies that the Leader should authenticate the connecting learner + * quorumpeer. After the successful authentication it should add this + * learner to the learnerHandler list. + */ + @Test(timeout = 30000) + public void testAuthLearnerConnectsToServerWithAuthRequired() + throws Exception { + File testDataLearner = ClientBase.createTmpDir(); + File tmpDir = File.createTempFile("test", ".dir", testDataLearner); + tmpDir.delete(); + FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir); + QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, true, true, + "QuorumLearner", "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer); + + File testDataLeader = ClientBase.createTmpDir(); + tmpDir = File.createTempFile("test", ".dir", testDataLeader); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, true, true, true, "QuorumLearner", + "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + CountDownLatch learnerLatch = new CountDownLatch(1); + leader = createSimpleLeader(tmpDir, peer, learnerLatch); + peer.leader = leader; + + startLearnerCnxAcceptorThread(leader); + LOG.info("Start establishing a connection with the Leader"); + String hostname = getLeaderHostname(peer); + sl.connectToLeader(peer.getQuorumAddress(), hostname); + // wait till leader socket soTimeout period + Assert.assertTrue("Leader should accept the auth learner connection", + learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000, + TimeUnit.MILLISECONDS)); + Assert.assertEquals("Failed to added the learner", 1, + leader.getLearners().size()); + ClientBase.recursiveDelete(testDataLearner); + ClientBase.recursiveDelete(testDataLeader); + } + + private String getLeaderHostname(QuorumPeer peer) { + String hostname = null; + for (QuorumServer p : peer.getView().values()) { + if (p.id == peer.getId()) { + hostname = p.hostname; + break; + } + } + Assert.assertNotNull("Didn't find leader", hostname); + return hostname; + } + + /** + * Test verifies that the Leader should authenticate the connecting learner + * quorumpeer. After the successful authentication it should add this + * learner to the learnerHandler list. + */ + @Test(timeout = 30000) + public void testAuthLearnerConnectsToServerWithAuthNotRequired() + throws Exception { + File testDataLearner = ClientBase.createTmpDir(); + File tmpDir = File.createTempFile("test", ".dir", testDataLearner); + tmpDir.delete(); + FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir); + QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, true, true, + "QuorumLearner", "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer); + + File testDataLeader = ClientBase.createTmpDir(); + tmpDir = File.createTempFile("test", ".dir", testDataLeader); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, true, true, false, "QuorumLearner", + "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + CountDownLatch learnerLatch = new CountDownLatch(1); + leader = createSimpleLeader(tmpDir, peer, learnerLatch); + peer.leader = leader; + + startLearnerCnxAcceptorThread(leader); + LOG.info("Start establishing a connection with the Leader"); + String hostname = getLeaderHostname(peer); + sl.connectToLeader(peer.getQuorumAddress(), hostname); + // wait till leader socket soTimeout period + Assert.assertTrue("Leader should accept the auth learner connection", + learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000, + TimeUnit.MILLISECONDS)); + Assert.assertEquals("Failed to added the learner", 1, + leader.getLearners().size()); + ClientBase.recursiveDelete(testDataLearner); + ClientBase.recursiveDelete(testDataLeader); + } + + private void startLearnerCnxAcceptorThread(Leader leader) + throws InterruptedException { + final CountDownLatch cnxAcceptorWatcher = new CountDownLatch(1); + leader.cnxAcceptor = leader.new LearnerCnxAcceptor(){ + @Override + public void run() { + cnxAcceptorWatcher.countDown(); + super.run(); + } + }; + leader.cnxAcceptor.start(); + // waiting to start the thread + Assert.assertTrue("Failed to start leader.cnxAcceptor thread!", + cnxAcceptorWatcher.await(15, TimeUnit.SECONDS)); + LOG.info("Started leader.cnxAcceptor:{} thread, state:{}", + leader.cnxAcceptor.getName(), leader.cnxAcceptor.getState()); + } + + /** + * Test verifies that the Auth enabled Learner is connecting to a Null Auth + * Leader server. Learner is failing to get an auth response from Null Auth + * Leader and fails the connection establishment. + */ + @Test(timeout = 30000) + public void testAuthLearnerConnectsToNullAuthServer() + throws Exception { + File testDataLearner = ClientBase.createTmpDir(); + File tmpDir = File.createTempFile("test", ".dir", testDataLearner); + tmpDir.delete(); + FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir); + QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, true, true, + "QuorumLearner", "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer); + + File testDataLeader = ClientBase.createTmpDir(); + tmpDir = File.createTempFile("test", ".dir", testDataLeader); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, false, false, false, + "QuorumLearner", "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + CountDownLatch learnerLatch = new CountDownLatch(1); + leader = createSimpleLeader(tmpDir, peer, learnerLatch); + peer.leader = leader; + + startLearnerCnxAcceptorThread(leader); + LOG.info("Start establishing a connection with the Leader"); + + try { + String hostname = getLeaderHostname(peer); + sl.connectToLeader(peer.getQuorumAddress(), hostname); + Assert.fail("Must throw exception as server doesn't supports authentication"); + } catch (IOException e) { + // expected + Assert.assertTrue("Leader should accept the auth learner connection", + learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 500, + TimeUnit.MILLISECONDS)); + } + + ClientBase.recursiveDelete(testDataLearner); + ClientBase.recursiveDelete(testDataLeader); + } + + /** + * Test verifies that the No Auth enabled Learner is connecting to a No Auth + * Leader server. Learner should be able to establish a connection with + * Leader as auth is not required. + */ + @Test(timeout = 30000) + public void testNoAuthLearnerConnectsToServerWithAuthNotRequired() + throws Exception { + File testDataLearner = ClientBase.createTmpDir(); + File tmpDir = File.createTempFile("test", ".dir", testDataLearner); + tmpDir.delete(); + FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir); + QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, false, false, + "QuorumLearner", "QuorumServer", ""); + SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer); + + File testDataLeader = ClientBase.createTmpDir(); + tmpDir = File.createTempFile("test", ".dir", testDataLeader); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, true, false, false, "QuorumLearner", + "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + CountDownLatch learnerLatch = new CountDownLatch(1); + leader = createSimpleLeader(tmpDir, peer, learnerLatch); + peer.leader = leader; + + startLearnerCnxAcceptorThread(leader); + LOG.info("Start establishing a connection with the Leader"); + String hostname = getLeaderHostname(peer); + sl.connectToLeader(peer.getQuorumAddress(), hostname); + + Assert.assertTrue("Leader should accept no auth learner connection", + learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000, + TimeUnit.MILLISECONDS)); + ClientBase.recursiveDelete(testDataLearner); + ClientBase.recursiveDelete(testDataLeader); + } + + /** + * Test verifies that the No Auth enabled Learner is connecting to a No Auth + * Leader server. Learner shouldn't be able to establish a connection with + * Leader as auth as auth is required. + */ + @Test(timeout = 30000) + public void testNoAuthLearnerConnectsToServerWithAuthRequired() + throws Exception { + File testDataLearner = ClientBase.createTmpDir(); + File tmpDir = File.createTempFile("test", ".dir", testDataLearner); + tmpDir.delete(); + FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir); + QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, false, false, + "QuorumLearner", "QuorumServer", ""); + SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer); + + File testDataLeader = ClientBase.createTmpDir(); + tmpDir = File.createTempFile("test", ".dir", testDataLeader); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, true, true, true, "QuorumLearner", + "QuorumServer", + QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE); + CountDownLatch learnerLatch = new CountDownLatch(1); + leader = createSimpleLeader(tmpDir, peer, learnerLatch); + peer.leader = leader; + + startLearnerCnxAcceptorThread(leader); + LOG.info("Start establishing a connection with the Leader"); + String hostname = getLeaderHostname(peer); + sl.connectToLeader(peer.getQuorumAddress(), hostname); + Assert.assertFalse("Leader shouldn't accept no auth learner connection", + learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000, + TimeUnit.MILLISECONDS)); + ClientBase.recursiveDelete(testDataLearner); + ClientBase.recursiveDelete(testDataLeader); + } + + /** + * Test verifies that the No Auth enabled Learner is connecting to a No Auth + * Leader server. Learner should be able to establish a connection with + * Leader as auth is not required. + */ + @Test(timeout = 30000) + public void testNoAuthLearnerConnectsToNullAuthServer() + throws Exception { + File testDataLearner = ClientBase.createTmpDir(); + File tmpDir = File.createTempFile("test", ".dir", testDataLearner); + tmpDir.delete(); + FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpDir, tmpDir); + QuorumPeer learnerPeer = createQuorumPeer(tmpDir, true, false, false, + "QuorumLearner", "QuorumServer", ""); + SimpleLearner sl = new SimpleLearner(ftsl, learnerPeer); + + File testDataLeader = ClientBase.createTmpDir(); + tmpDir = File.createTempFile("test", ".dir", testDataLeader); + tmpDir.delete(); + tmpDir.mkdir(); + Leader leader = null; + QuorumPeer peer = createQuorumPeer(tmpDir, false, false, false, "", "", + ""); + CountDownLatch learnerLatch = new CountDownLatch(1); + leader = createSimpleLeader(tmpDir, peer, learnerLatch); + peer.leader = leader; + + startLearnerCnxAcceptorThread(leader); + LOG.info("Start establishing a connection with the Leader"); + String hostname = getLeaderHostname(peer); + sl.connectToLeader(peer.getQuorumAddress(), hostname); + + Assert.assertTrue("Leader should accept no auth learner connection", + learnerLatch.await(leader.self.tickTime * leader.self.initLimit + 1000, + TimeUnit.MILLISECONDS)); + ClientBase.recursiveDelete(testDataLearner); + ClientBase.recursiveDelete(testDataLeader); + } + + /** + * SaslQuorumAuthServer throws exception on receiving an invalid quorum + * auth packet. + */ + @Test(timeout = 30000) + public void testSaslQuorumAuthServerWithInvalidQuorumAuthPacket() + throws Exception { + Socket socket = getSocketPair(); + DataOutputStream dout = new DataOutputStream(socket.getOutputStream()); + BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); + BinaryOutputArchive boa = BinaryOutputArchive + .getArchive(bufferedOutput); + QuorumAuthPacket authPacket = QuorumAuth + .createPacket(QuorumAuth.Status.IN_PROGRESS, null); + authPacket.setMagic(Long.MAX_VALUE); // invalid magic number + boa.writeRecord(authPacket, null); + bufferedOutput.flush(); + QuorumAuthServer authServer = new SaslQuorumAuthServer(true, + "QuorumServer", authzHosts); + BufferedInputStream is = new BufferedInputStream( + socket.getInputStream()); + try { + authServer.authenticate(socket, new DataInputStream(is)); + Assert.fail("Must throw exception as QuorumAuthPacket is invalid"); + } catch (SaslException e) { + // expected + } + } + + /** + * NullQuorumAuthServer should return true when no auth quorum packet + * received and timed out. + */ + @Test(timeout = 30000) + public void testNullQuorumAuthServerShouldReturnTrue() + throws Exception { + Socket socket = getSocketPair(); + QuorumAuthServer authServer = new NullQuorumAuthServer(); + BufferedInputStream is = new BufferedInputStream( + socket.getInputStream()); + // It will throw exception and fail the + // test if any unexpected error. Not adding any extra assertion. + authServer.authenticate(socket, new DataInputStream(is)); + } + + /** + * NullQuorumAuthServer should return true on receiving a valid quorum auth + * packet. + */ + @Test(timeout = 30000) + public void testNullQuorumAuthServerWithValidQuorumAuthPacket() + throws Exception { + Socket socket = getSocketPair(); + DataOutputStream dout = new DataOutputStream(socket.getOutputStream()); + BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); + BinaryOutputArchive boa = BinaryOutputArchive + .getArchive(bufferedOutput); + QuorumAuthPacket authPacket = QuorumAuth + .createPacket(QuorumAuth.Status.IN_PROGRESS, null); + boa.writeRecord(authPacket, null); + bufferedOutput.flush(); + QuorumAuthServer authServer = new NullQuorumAuthServer(); + BufferedInputStream is = new BufferedInputStream( + socket.getInputStream()); + // It will throw exception and fail the + // test if any unexpected error. Not adding any extra assertion. + authServer.authenticate(socket, new DataInputStream(is)); + } + + private QuorumCnxManager createAndStartManager(long sid) { + QuorumCnxManager peer = new QuorumCnxManager(sid, peers, + new NullQuorumAuthServer(), new NullQuorumAuthLearner(), 10000, + false, quorumCnxnThreadsSize, false); + executor.submit(peer.listener); + InetSocketAddress electionAddr = peer.view.get(sid).electionAddr; + waitForElectionAddrBinding(electionAddr, 15); + return peer; + } + + private QuorumCnxManager createAndStartManager(long sid, + String serverLoginContext, + String learnerLoginContext, + boolean serverRequireSasl, + boolean learnerRequireSasl) + throws Exception { + QuorumAuthLearner authClient = new SaslQuorumAuthLearner(learnerRequireSasl, + "NOT_USING_KRB_PRINCIPAL", learnerLoginContext); + QuorumAuthServer authServer = new SaslQuorumAuthServer(serverRequireSasl, + serverLoginContext, authzHosts); + QuorumCnxManager peer = new QuorumCnxManager(sid, peers, + authServer, authClient, 10000, false, quorumCnxnThreadsSize, true); + executor.submit(peer.listener); + InetSocketAddress electionAddr = peer.view.get(sid).electionAddr; + waitForElectionAddrBinding(electionAddr, 15); + return peer; + } + + private void waitForElectionAddrBinding(InetSocketAddress electionAddr, + int retries) { + boolean success = false; + while (retries > 0) { + Socket sock = new Socket(); + try { + sock.setTcpNoDelay(true); + sock.setSoTimeout(5000); + sock.connect(electionAddr, 5000); + success = true; + } catch (IOException e) { + LOG.error("IOException while checking election addr", e); + } finally { + cleanup(sock); + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + retries--; + } + Assert.assertTrue("Did not connect to election port", success); + } + + private void cleanup(Socket sock) { + try { + sock.close(); + } catch (IOException ie) { + LOG.error("Exception while closing socket", ie); + } + } + + private void assertEventuallyConnected(QuorumCnxManager peer, long sid) + throws Exception { + for (int i = 0; i < 20 && !peer.connectedToPeer(sid); i++) { + Thread.sleep(1000); + } + Assert.assertTrue("Not connected to peer", peer.connectedToPeer(sid)); + } + + private void assertEventuallyNotConnected(QuorumCnxManager peer, long sid) + throws Exception { + for (int i = 0; i < 3 && !peer.connectedToPeer(sid); i++) { + Thread.sleep(1000); + } + Assert.assertFalse("Connected to peer (shouldn't be)", + peer.connectedToPeer(sid)); + } + + private QuorumPeer createQuorumPeer(File tmpDir, + boolean isQuorumAuthEnabled, boolean isQuorumLearnerAuthRequired, + boolean isQuorumServerAuthRequired, String quorumLearnerLoginContext, + String quorumServerLoginContext, String quorumServicePrincipal) + throws IOException, FileNotFoundException { + QuorumPeer peer = QuorumPeer.testingQuorumPeer(); + peer.syncLimit = 2; + peer.initLimit = 2; + peer.tickTime = 2000; + peer.quorumPeers = new HashMap(); + peer.quorumPeers.put(0L, + new QuorumServer(0, "0.0.0.0", PortAssignment.unique(), null, null)); + peer.quorumPeers.put(1L, + new QuorumServer(1, "0.0.0.0", PortAssignment.unique(), null, null)); + peer.setQuorumVerifier(new QuorumMaj(3)); + peer.setCnxnFactory(new NullServerCnxnFactory()); + // auth + if (isQuorumAuthEnabled) { + peer.authServer = new SaslQuorumAuthServer( + isQuorumServerAuthRequired, quorumServerLoginContext, authzHosts); + peer.authLearner = new SaslQuorumAuthLearner( + isQuorumLearnerAuthRequired, quorumServicePrincipal, + quorumLearnerLoginContext); + } + File version2 = new File(tmpDir, "version-2"); + version2.mkdir(); + FileOutputStream fos; + fos = new FileOutputStream(new File(version2, "currentEpoch")); + fos.write("0\n".getBytes()); + fos.close(); + fos = new FileOutputStream(new File(version2, "acceptedEpoch")); + fos.write("0\n".getBytes()); + fos.close(); + return peer; + } + + private static final class NullServerCnxnFactory extends ServerCnxnFactory { + public void startup(ZooKeeperServer zkServer) + throws IOException, InterruptedException { + } + + public void start() { + } + + public void shutdown() { + } + + public void setMaxClientCnxnsPerHost(int max) { + } + + public void join() throws InterruptedException { + } + + public int getMaxClientCnxnsPerHost() { + return 0; + } + + public int getLocalPort() { + return 0; + } + + public InetSocketAddress getLocalAddress() { + return null; + } + + public Iterable getConnections() { + return null; + } + + public void configure(InetSocketAddress addr, int maxClientCnxns) + throws IOException { + } + + public void closeSession(long sessionId) { + } + + public void closeAll() { + } + + @Override + public int getNumAliveConnections() { + return 0; + } + } + + private static Socket getSocketPair() throws IOException { + ServerSocket ss = new ServerSocket(); + ss.bind(null); + InetSocketAddress endPoint = (InetSocketAddress) ss + .getLocalSocketAddress(); + Socket s = new Socket(endPoint.getAddress(), endPoint.getPort()); + s.setSoTimeout(5000); + return s; + } + + private Leader createLeader(File tmpDir, QuorumPeer peer) throws IOException, + NoSuchFieldException, IllegalAccessException { + LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer); + return new Leader(peer, zk); + } + + private Leader createSimpleLeader(File tmpDir, QuorumPeer peer, + CountDownLatch learnerLatch) throws IOException, + NoSuchFieldException, IllegalAccessException { + LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer); + return new SimpleLeader(peer, zk, learnerLatch); + } + + class SimpleLeader extends Leader { + final CountDownLatch learnerLatch; + + SimpleLeader(QuorumPeer self, LeaderZooKeeperServer zk, + CountDownLatch latch) throws IOException { + super(self, zk); + this.learnerLatch = latch; + } + + @Override + void addLearnerHandler(LearnerHandler learner) { + super.addLearnerHandler(learner); + learnerLatch.countDown(); + } + } + + private LeaderZooKeeperServer prepareLeader(File tmpDir, QuorumPeer peer) + throws IOException, NoSuchFieldException, IllegalAccessException { + FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); + peer.setTxnFactory(logFactory); + Field addrField = peer.getClass().getDeclaredField("myQuorumAddr"); + addrField.setAccessible(true); + addrField.set(peer, new InetSocketAddress(PortAssignment.unique())); + ZKDatabase zkDb = new ZKDatabase(logFactory); + LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer, + new ZooKeeperServer.BasicDataTreeBuilder(), zkDb); + return zk; + } + + class SimpleLearnerZooKeeperServer extends LearnerZooKeeperServer { + boolean startupCalled; + + public SimpleLearnerZooKeeperServer(FileTxnSnapLog ftsl, + QuorumPeer self) throws IOException { + super(ftsl, 2000, 2000, 2000, null, new ZKDatabase(ftsl), self); + } + + Learner learner; + + @Override + public Learner getLearner() { + return learner; + } + + @Override + public void startup() { + startupCalled = true; + } + } + + class SimpleLearner extends Learner { + SimpleLearner(FileTxnSnapLog ftsl, QuorumPeer learner) + throws IOException { + self = learner; + zk = new SimpleLearnerZooKeeperServer(ftsl, self); + ((SimpleLearnerZooKeeperServer) zk).learner = this; + } + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java index 85817b248c2..6d5eb47c45c 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java @@ -24,6 +24,10 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.WatchedEvent; @@ -42,6 +46,8 @@ public class QuorumPeerTestBase extends ZKTestCase implements Watcher { protected static final Logger LOG = LoggerFactory .getLogger(QuorumPeerTestBase.class); + public static final int TIMEOUT = 5000; + public void process(WatchedEvent event) { // ignore for this test } @@ -60,20 +66,29 @@ public static class MainThread implements Runnable { volatile TestQPMain main; final File dataDir; CountDownLatch mainFailed; - - public MainThread(int myid, int clientPort, String quorumCfgSection) - throws IOException { - File tmpDir = ClientBase.createTmpDir(); - LOG.info("id = " + myid + " tmpDir = " + tmpDir + " clientPort = " + File baseDir; + private final int myid; + private final int clientPort; + private final String quorumCfgSection; + private final Map otherConfigs; + + public MainThread(int myid, int clientPort, String quorumCfgSection, + Map otherConfigs) throws IOException { + baseDir = ClientBase.createTmpDir(); + this.myid = myid; + this.clientPort = clientPort; + this.quorumCfgSection = quorumCfgSection; + this.otherConfigs = otherConfigs; + LOG.info("id = " + myid + " tmpDir = " + baseDir + " clientPort = " + clientPort); - confFile = new File(tmpDir, "zoo.cfg"); + confFile = new File(baseDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=4000\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); - dataDir = new File(tmpDir, "data"); + dataDir = new File(baseDir, "data"); if (!dataDir.mkdir()) { throw new IOException("Unable to mkdir " + dataDir); } @@ -87,6 +102,13 @@ public MainThread(int myid, int clientPort, String quorumCfgSection) fwriter.write("dataDir=" + dir + "\n"); fwriter.write("clientPort=" + clientPort + "\n"); + + // write extra configurations + Set> entrySet = otherConfigs.entrySet(); + for (Entry entry : entrySet) { + fwriter.write(entry.getKey() + "=" + entry.getValue() + "\n"); + } + fwriter.write(quorumCfgSection + "\n"); fwriter.flush(); fwriter.close(); @@ -98,6 +120,12 @@ public MainThread(int myid, int clientPort, String quorumCfgSection) fwriter.close(); } + public MainThread(int myid, int clientPort, String quorumCfgSection) + throws IOException { + this(myid, clientPort, quorumCfgSection, + new HashMap()); + } + Thread currentThread; synchronized public void start() { @@ -151,5 +179,28 @@ public QuorumPeer getQuorumPeer() { return main.quorumPeer; } + public void deleteBaseDir() { + ClientBase.recursiveDelete(baseDir); + } + + public int getMyid() { + return myid; + } + + public int getClientPort() { + return clientPort; + } + + public String getQuorumCfgSection() { + return quorumCfgSection; + } + + public Map getOtherConfigs() { + return otherConfigs; + } + + public File getConfFile() { + return confFile; + } } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index ab8ce420997..52e7d279c7e 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; @@ -367,7 +368,9 @@ public void testLeaderConversation(LeaderConversation conversation) throws Excep Thread.sleep(20); } - LearnerHandler lh = new LearnerHandler(leaderSocket, leader); + LearnerHandler lh = new LearnerHandler(leaderSocket, + new BufferedInputStream(leaderSocket.getInputStream()), + leader); lh.start(); leaderSocket.setSoTimeout(4000); @@ -435,8 +438,10 @@ public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversa while(leader.cnxAcceptor == null || !leader.cnxAcceptor.isAlive()) { Thread.sleep(20); } - - LearnerHandler lh = new LearnerHandler(leaderSocket, leader); + + LearnerHandler lh = new LearnerHandler(leaderSocket, + new BufferedInputStream(leaderSocket.getInputStream()), + leader); lh.start(); leaderSocket.setSoTimeout(4000); @@ -458,7 +463,6 @@ public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversa } } - public void testFollowerConversation(FollowerConversation conversation) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); @@ -473,7 +477,9 @@ public void testFollowerConversation(FollowerConversation conversation) throws E ServerSocket ss = new ServerSocket(); ss.bind(null); - follower.setLeaderSocketAddress((InetSocketAddress)ss.getLocalSocketAddress()); + QuorumServer leaderQS = new QuorumServer(1, + (InetSocketAddress) ss.getLocalSocketAddress()); + follower.setLeaderQuorumServer(leaderQS); final Follower followerForThread = follower; followerThread = new Thread() { @@ -526,7 +532,9 @@ public void testObserverConversation(ObserverConversation conversation) throws E ServerSocket ss = new ServerSocket(); ss.bind(null); - observer.setLeaderSocketAddress((InetSocketAddress)ss.getLocalSocketAddress()); + QuorumServer leaderQS = new QuorumServer(1, + (InetSocketAddress) ss.getLocalSocketAddress()); + observer.setLeaderQuorumServer(leaderQS); final Observer observerForThread = observer; observerThread = new Thread() { @@ -1338,14 +1346,14 @@ static class ConversableFollower extends Follower { super(self, zk); } - InetSocketAddress leaderAddr; - public void setLeaderSocketAddress(InetSocketAddress addr) { - leaderAddr = addr; + QuorumServer leaderQuorumServer; + public void setLeaderQuorumServer(QuorumServer quorumServer) { + leaderQuorumServer = quorumServer; } @Override - protected InetSocketAddress findLeader() { - return leaderAddr; + protected QuorumServer findLeader() { + return leaderQuorumServer; } } private ConversableFollower createFollower(File tmpDir, QuorumPeer peer) @@ -1364,14 +1372,14 @@ static class ConversableObserver extends Observer { super(self, zk); } - InetSocketAddress leaderAddr; - public void setLeaderSocketAddress(InetSocketAddress addr) { - leaderAddr = addr; + QuorumServer leaderQuorumServer; + public void setLeaderQuorumServer(QuorumServer quorumServer) { + leaderQuorumServer = quorumServer; } @Override - protected InetSocketAddress findLeader() { - return leaderAddr; + protected QuorumServer findLeader() { + return leaderQuorumServer; } } @@ -1389,7 +1397,7 @@ private ConversableObserver createObserver(File tmpDir, QuorumPeer peer) private QuorumPeer createQuorumPeer(File tmpDir) throws IOException, FileNotFoundException { - QuorumPeer peer = new QuorumPeer(); + QuorumPeer peer = QuorumPeer.testingQuorumPeer(); peer.syncLimit = SYNC_LIMIT; peer.initLimit = 2; peer.tickTime = 2000; @@ -1435,7 +1443,7 @@ public void testInitialAcceptedCurrent() throws Exception { logFactory.append(req); logFactory.commit(); ZKDatabase zkDb = new ZKDatabase(logFactory); - QuorumPeer peer = new QuorumPeer(); + QuorumPeer peer = QuorumPeer.testingQuorumPeer(); peer.setZKDatabase(zkDb); peer.setTxnFactory(logFactory); peer.getLastLoggedZxid(); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java new file mode 100644 index 00000000000..9617c70b332 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java @@ -0,0 +1,120 @@ +/** + * 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.server.quorum.auth; + +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +/* + * This code is originally from HDFS, see the similarly named file there + * in case of bug fixing, history, etc. + * + * Branch : trunk + * Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33 + */ + +/** + * KerberosSecurityTestcase provides a base class for using MiniKdc with other + * test cases. KerberosSecurityTestcase starts the MiniKdc (@Before) before + * running tests, and stop the MiniKdc (@After) after the testcases, using + * default settings (working dir and kdc configurations). + *

            + * Users can directly inherit this class and implement their own test functions + * using the default settings, or override functions getTestDir() and + * createMiniKdcConf() to provide new settings. + */ +public class KerberosSecurityTestcase extends QuorumAuthTestBase { + private static MiniKdc kdc; + private static File workDir; + private static Properties conf; + + @BeforeClass + public static void setUpSasl() throws Exception { + startMiniKdc(); + } + + @AfterClass + public static void tearDownSasl() throws Exception { + stopMiniKdc(); + FileUtils.deleteQuietly(workDir); + } + + public static void startMiniKdc() throws Exception { + createTestDir(); + createMiniKdcConf(); + + kdc = new MiniKdc(conf, workDir); + kdc.start(); + } + + /** + * Create a working directory, it should be the build directory. Under this + * directory an ApacheDS working directory will be created, this directory + * will be deleted when the MiniKdc stops. + * + * @throws IOException + */ + public static void createTestDir() throws IOException { + workDir = createTmpDir( + new File(System.getProperty("build.test.dir", "build"))); + } + + static File createTmpDir(File parentDir) throws IOException { + File tmpFile = File.createTempFile("test", ".junit", parentDir); + // don't delete tmpFile - this ensures we don't attempt to create + // a tmpDir with a duplicate name + File tmpDir = new File(tmpFile + ".dir"); + // never true if tmpfile does it's job + Assert.assertFalse(tmpDir.exists()); + Assert.assertTrue(tmpDir.mkdirs()); + return tmpDir; + } + + /** + * Create a Kdc configuration + */ + public static void createMiniKdcConf() { + conf = MiniKdc.createConf(); + } + + public static void stopMiniKdc() { + if (kdc != null) { + kdc.stop(); + } + } + + public static MiniKdc getKdc() { + return kdc; + } + + public static File getWorkDir() { + return workDir; + } + + public static Properties getConf() { + return conf; + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java new file mode 100644 index 00000000000..4a75f8336c1 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java @@ -0,0 +1,76 @@ +/** + * 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.server.quorum.auth; + +import java.io.File; +import java.util.UUID; + +import org.apache.zookeeper.util.SecurityUtils; + +public class KerberosTestUtils { + private static String keytabFile = new File(System.getProperty("test.dir", "build"), UUID.randomUUID().toString()) + .getAbsolutePath(); + + public static String getRealm() { + return "EXAMPLE.COM"; + } + + public static String getLearnerPrincipal() { + return "learner@EXAMPLE.COM"; + } + + public static String getServerPrincipal() { + return "zkquorum/localhost@EXAMPLE.COM"; + } + + public static String getHostLearnerPrincipal() { + return "learner/_HOST@EXAMPLE.COM"; + } + + public static String getHostServerPrincipal() { + return "zkquorum/_HOST@EXAMPLE.COM"; + } + + public static String getHostNamedLearnerPrincipal(String myHostname) { + return "learner/" + myHostname + "@EXAMPLE.COM"; + } + + public static String getKeytabFile() { + return keytabFile; + } + + public static String replaceHostPattern(String principal) { + String[] components = principal.split("[/@]"); + if (components == null || components.length < 2 + || !components[1].equals(SecurityUtils.QUORUM_HOSTNAME_PATTERN)) { + return principal; + } else { + return replacePattern(components, "localhost"); + } + } + + public static String replacePattern(String[] components, String hostname) { + if (components.length == 3) { + return components[0] + "/" + hostname.toLowerCase() + "@" + + components[2]; + } else { + return components[0] + "/" + hostname.toLowerCase(); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java new file mode 100644 index 00000000000..4afef41eadc --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java @@ -0,0 +1,418 @@ +/** + * 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.server.quorum.auth; +import org.apache.commons.io.Charsets; +import org.apache.kerby.kerberos.kerb.KrbException; +import org.apache.kerby.kerberos.kerb.server.KdcConfigKey; +import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; +import org.apache.kerby.util.IOUtil; +import org.apache.kerby.util.NetworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * Mini KDC based on Apache Directory Server that can be embedded in testcases + * or used from command line as a standalone KDC. + *

            + * From within testcases: + *

            + * MiniKdc sets one System property when started and un-set when stopped: + *

              + *
            • sun.security.krb5.debug: set to the debug value provided in the + * configuration
            • + *
            + * Because of this, multiple MiniKdc instances cannot be started in parallel. + * For example, running testcases in parallel that start a KDC each. To + * accomplish this a single MiniKdc should be used for all testcases running + * in parallel. + *

            + * MiniKdc default configuration values are: + *

              + *
            • org.name=EXAMPLE (used to create the REALM)
            • + *
            • org.domain=COM (used to create the REALM)
            • + *
            • kdc.bind.address=localhost
            • + *
            • kdc.port=0 (ephemeral port)
            • + *
            • instance=DefaultKrbServer
            • + *
            • max.ticket.lifetime=86400000 (1 day)
            • + *
            • max.renewable.lifetime=604800000 (7 days)
            • + *
            • transport=TCP
            • + *
            • debug=false
            • + *
            + * The generated krb5.conf forces TCP connections. + */ +/* + * This code is originally from HDFS, see the file name MiniKdc there + * in case of bug fixing, history, etc. + * + * Branch : trunk + * Github Revision: 916140604ffef59466ba30832478311d3e6249bd + */ +public class MiniKdc { + + public static final String JAVA_SECURITY_KRB5_CONF = + "java.security.krb5.conf"; + public static final String SUN_SECURITY_KRB5_DEBUG = + "sun.security.krb5.debug"; + + public static void main(String[] args) throws Exception { + if (args.length < 4) { + System.out.println("Arguments: " + + " []+"); + System.exit(1); + } + File workDir = new File(args[0]); + if (!workDir.exists()) { + throw new RuntimeException("Specified work directory does not exists: " + + workDir.getAbsolutePath()); + } + Properties conf = createConf(); + File file = new File(args[1]); + if (!file.exists()) { + throw new RuntimeException("Specified configuration does not exists: " + + file.getAbsolutePath()); + } + Properties userConf = new Properties(); + InputStreamReader r = null; + try { + r = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8); + userConf.load(r); + } finally { + if (r != null) { + r.close(); + } + } + for (Map.Entry entry : userConf.entrySet()) { + conf.put(entry.getKey(), entry.getValue()); + } + final MiniKdc miniKdc = new MiniKdc(conf, workDir); + miniKdc.start(); + File krb5conf = new File(workDir, "krb5.conf"); + if (miniKdc.getKrb5conf().renameTo(krb5conf)) { + File keytabFile = new File(args[2]).getAbsoluteFile(); + String[] principals = new String[args.length - 3]; + System.arraycopy(args, 3, principals, 0, args.length - 3); + miniKdc.createPrincipal(keytabFile, principals); + System.out.println(); + System.out.println("Standalone MiniKdc Running"); + System.out.println("---------------------------------------------------"); + System.out.println(" Realm : " + miniKdc.getRealm()); + System.out.println(" Running at : " + miniKdc.getHost() + ":" + + miniKdc.getHost()); + System.out.println(" krb5conf : " + krb5conf); + System.out.println(); + System.out.println(" created keytab : " + keytabFile); + System.out.println(" with principals : " + Arrays.asList(principals)); + System.out.println(); + System.out.println(" Do or kill to stop it"); + System.out.println("---------------------------------------------------"); + System.out.println(); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + miniKdc.stop(); + } + }); + } else { + throw new RuntimeException("Cannot rename KDC's krb5conf to " + + krb5conf.getAbsolutePath()); + } + } + + private static final Logger LOG = LoggerFactory.getLogger(MiniKdc.class); + + public static final String ORG_NAME = "org.name"; + public static final String ORG_DOMAIN = "org.domain"; + public static final String KDC_BIND_ADDRESS = "kdc.bind.address"; + public static final String KDC_PORT = "kdc.port"; + public static final String INSTANCE = "instance"; + public static final String MAX_TICKET_LIFETIME = "max.ticket.lifetime"; + public static final String MAX_RENEWABLE_LIFETIME = "max.renewable.lifetime"; + public static final String TRANSPORT = "transport"; + public static final String DEBUG = "debug"; + + private static final Set PROPERTIES = new HashSet(); + private static final Properties DEFAULT_CONFIG = new Properties(); + + static { + PROPERTIES.add(ORG_NAME); + PROPERTIES.add(ORG_DOMAIN); + PROPERTIES.add(KDC_BIND_ADDRESS); + PROPERTIES.add(KDC_BIND_ADDRESS); + PROPERTIES.add(KDC_PORT); + PROPERTIES.add(INSTANCE); + PROPERTIES.add(TRANSPORT); + PROPERTIES.add(MAX_TICKET_LIFETIME); + PROPERTIES.add(MAX_RENEWABLE_LIFETIME); + + DEFAULT_CONFIG.setProperty(KDC_BIND_ADDRESS, "localhost"); + DEFAULT_CONFIG.setProperty(KDC_PORT, "0"); + DEFAULT_CONFIG.setProperty(INSTANCE, "DefaultKrbServer"); + DEFAULT_CONFIG.setProperty(ORG_NAME, "EXAMPLE"); + DEFAULT_CONFIG.setProperty(ORG_DOMAIN, "COM"); + DEFAULT_CONFIG.setProperty(TRANSPORT, "TCP"); + DEFAULT_CONFIG.setProperty(MAX_TICKET_LIFETIME, "86400000"); + DEFAULT_CONFIG.setProperty(MAX_RENEWABLE_LIFETIME, "604800000"); + DEFAULT_CONFIG.setProperty(DEBUG, "false"); + } + + /** + * Convenience method that returns MiniKdc default configuration. + *

            + * The returned configuration is a copy, it can be customized before using + * it to create a MiniKdc. + * @return a MiniKdc default configuration. + */ + public static Properties createConf() { + return (Properties) DEFAULT_CONFIG.clone(); + } + + private Properties conf; + private SimpleKdcServer simpleKdc; + private int port; + private String realm; + private File workDir; + private File krb5conf; + private String transport; + private boolean krb5Debug; + + public void setTransport(String transport) { + this.transport = transport; + } + /** + * Creates a MiniKdc. + * + * @param conf MiniKdc configuration. + * @param workDir working directory, it should be the build directory. Under + * this directory an ApacheDS working directory will be created, this + * directory will be deleted when the MiniKdc stops. + * @throws Exception thrown if the MiniKdc could not be created. + */ + public MiniKdc(Properties conf, File workDir) throws Exception { + if (!conf.keySet().containsAll(PROPERTIES)) { + Set missingProperties = new HashSet(PROPERTIES); + missingProperties.removeAll(conf.keySet()); + throw new IllegalArgumentException("Missing configuration properties: " + + missingProperties); + } + this.workDir = new File(workDir, Long.toString(System.currentTimeMillis())); + if (!this.workDir.exists() + && !this.workDir.mkdirs()) { + throw new RuntimeException("Cannot create directory " + this.workDir); + } + LOG.info("Configuration:"); + LOG.info("---------------------------------------------------------------"); + for (Map.Entry entry : conf.entrySet()) { + LOG.info(" {}: {}", entry.getKey(), entry.getValue()); + } + LOG.info("---------------------------------------------------------------"); + this.conf = conf; + port = Integer.parseInt(conf.getProperty(KDC_PORT)); + String orgName= conf.getProperty(ORG_NAME); + String orgDomain = conf.getProperty(ORG_DOMAIN); + realm = orgName.toUpperCase(Locale.ENGLISH) + "." + + orgDomain.toUpperCase(Locale.ENGLISH); + } + + /** + * Returns the port of the MiniKdc. + * + * @return the port of the MiniKdc. + */ + public int getPort() { + return port; + } + + /** + * Returns the host of the MiniKdc. + * + * @return the host of the MiniKdc. + */ + public String getHost() { + return conf.getProperty(KDC_BIND_ADDRESS); + } + + /** + * Returns the realm of the MiniKdc. + * + * @return the realm of the MiniKdc. + */ + public String getRealm() { + return realm; + } + + public File getKrb5conf() { + krb5conf = new File(System.getProperty(JAVA_SECURITY_KRB5_CONF)); + return krb5conf; + } + + /** + * Starts the MiniKdc. + * + * @throws Exception thrown if the MiniKdc could not be started. + */ + public synchronized void start() throws Exception { + if (simpleKdc != null) { + throw new RuntimeException("Already started"); + } + simpleKdc = new SimpleKdcServer(); + prepareKdcServer(); + simpleKdc.init(); + resetDefaultRealm(); + simpleKdc.start(); + LOG.info("MiniKdc stated."); + } + + private void resetDefaultRealm() throws IOException { + InputStream templateResource = new FileInputStream( + getKrb5conf().getAbsolutePath()); + String content = IOUtil.readInput(templateResource); + content = content.replaceAll("default_realm = .*\n", + "default_realm = " + getRealm() + "\n"); + IOUtil.writeFile(content, getKrb5conf()); + } + + private void prepareKdcServer() throws Exception { + // transport + simpleKdc.setWorkDir(workDir); + simpleKdc.setKdcHost(getHost()); + simpleKdc.setKdcRealm(realm); + if (transport == null) { + transport = conf.getProperty(TRANSPORT); + } + if (port == 0) { + port = NetworkUtil.getServerPort(); + } + if (transport != null) { + if (transport.trim().equals("TCP")) { + simpleKdc.setKdcTcpPort(port); + simpleKdc.setAllowUdp(false); + } else if (transport.trim().equals("UDP")) { + simpleKdc.setKdcUdpPort(port); + simpleKdc.setAllowTcp(false); + } else { + throw new IllegalArgumentException("Invalid transport: " + transport); + } + } else { + throw new IllegalArgumentException("Need to set transport!"); + } + simpleKdc.getKdcConfig().setString(KdcConfigKey.KDC_SERVICE_NAME, + conf.getProperty(INSTANCE)); + if (conf.getProperty(DEBUG) != null) { + krb5Debug = getAndSet(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG)); + } + } + + /** + * Stops the MiniKdc + */ + public synchronized void stop() { + if (simpleKdc != null) { + try { + simpleKdc.stop(); + } catch (KrbException e) { + e.printStackTrace(); + } finally { + if(conf.getProperty(DEBUG) != null) { + System.setProperty(SUN_SECURITY_KRB5_DEBUG, + Boolean.toString(krb5Debug)); + } + } + } + delete(workDir); + try { + // Will be fixed in next Kerby version. + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + LOG.info("MiniKdc stopped."); + } + + private void delete(File f) { + if (f.isFile()) { + if (! f.delete()) { + LOG.warn("WARNING: cannot delete file " + f.getAbsolutePath()); + } + } else { + for (File c: f.listFiles()) { + delete(c); + } + if (! f.delete()) { + LOG.warn("WARNING: cannot delete directory " + f.getAbsolutePath()); + } + } + } + + /** + * Creates a principal in the KDC with the specified user and password. + * + * @param principal principal name, do not include the domain. + * @param password password. + * @throws Exception thrown if the principal could not be created. + */ + public synchronized void createPrincipal(String principal, String password) + throws Exception { + simpleKdc.createPrincipal(principal, password); + } + + /** + * Creates multiple principals in the KDC and adds them to a keytab file. + * + * @param keytabFile keytab file to add the created principals. + * @param principals principals to add to the KDC, do not include the domain. + * @throws Exception thrown if the principals or the keytab file could not be + * created. + */ + public synchronized void createPrincipal(File keytabFile, + String ... principals) + throws Exception { + simpleKdc.createPrincipals(principals); + if (keytabFile.exists() && !keytabFile.delete()) { + LOG.error("Failed to delete keytab file: " + keytabFile); + } + for (String principal : principals) { + simpleKdc.getKadmin().exportKeytab(keytabFile, principal); + } + } + + /** + * Set the System property; return the old value for caching. + * + * @param sysprop property + * @param debug true or false + * @return the previous value + */ + private boolean getAndSet(String sysprop, String debug) { + boolean old = Boolean.getBoolean(sysprop); + System.setProperty(sysprop, debug); + return old; + } +} \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java new file mode 100644 index 00000000000..a7bbf7feb93 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java @@ -0,0 +1,184 @@ +/** + * 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.server.quorum.auth; + +import org.apache.kerby.kerberos.kerb.keytab.Keytab; +import org.apache.kerby.kerberos.kerb.type.base.PrincipalName; +import org.junit.Assert; +import org.junit.Test; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import java.io.File; +import java.security.Principal; +import java.util.List; +import java.util.Set; +import java.util.Map; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Arrays; + +/* + * This code is originally from HDFS, see the file name TestMiniKdc there + * in case of bug fixing, history, etc. + * + * Branch : trunk + * Github Revision: 916140604ffef59466ba30832478311d3e6249bd + */ +public class MiniKdcTest extends KerberosSecurityTestcase { + private static final boolean IBM_JAVA = System.getProperty("java.vendor") + .contains("IBM"); + + @Test(timeout = 60000) + public void testMiniKdcStart() { + MiniKdc kdc = getKdc(); + Assert.assertNotSame(0, kdc.getPort()); + } + + @Test(timeout = 60000) + public void testKeytabGen() throws Exception { + MiniKdc kdc = getKdc(); + File workDir = getWorkDir(); + + kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo"); + List principalNameList = + Keytab.loadKeytab(new File(workDir, "keytab")).getPrincipals(); + + Set principals = new HashSet(); + for (PrincipalName principalName : principalNameList) { + principals.add(principalName.getName()); + } + + Assert.assertEquals(new HashSet(Arrays.asList( + "foo/bar@" + kdc.getRealm(), "bar/foo@" + kdc.getRealm())), + principals); + } + + private static class KerberosConfiguration extends Configuration { + private String principal; + private String keytab; + private boolean isInitiator; + + private KerberosConfiguration(String principal, File keytab, + boolean client) { + this.principal = principal; + this.keytab = keytab.getAbsolutePath(); + this.isInitiator = client; + } + + public static Configuration createClientConfig(String principal, + File keytab) { + return new KerberosConfiguration(principal, keytab, true); + } + + public static Configuration createServerConfig(String principal, + File keytab) { + return new KerberosConfiguration(principal, keytab, false); + } + + private static String getKrb5LoginModuleName() { + return System.getProperty("java.vendor").contains("IBM") + ? "com.ibm.security.auth.module.Krb5LoginModule" + : "com.sun.security.auth.module.Krb5LoginModule"; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + Map options = new HashMap(); + options.put("principal", principal); + options.put("refreshKrb5Config", "true"); + if (IBM_JAVA) { + options.put("useKeytab", keytab); + options.put("credsType", "both"); + } else { + options.put("keyTab", keytab); + options.put("useKeyTab", "true"); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("isInitiator", Boolean.toString(isInitiator)); + } + String ticketCache = System.getenv("KRB5CCNAME"); + if (ticketCache != null) { + options.put("ticketCache", ticketCache); + } + options.put("debug", "true"); + + return new AppConfigurationEntry[] { + new AppConfigurationEntry(getKrb5LoginModuleName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options) }; + } + } + + @Test(timeout = 60000) + public void testKerberosLogin() throws Exception { + MiniKdc kdc = getKdc(); + File workDir = getWorkDir(); + LoginContext loginContext = null; + try { + String principal = "foo"; + File keytab = new File(workDir, "foo.keytab"); + kdc.createPrincipal(keytab, principal); + + Set principals = new HashSet(); + principals.add(new KerberosPrincipal(principal)); + + // client login + Subject subject = new Subject(false, principals, + new HashSet(), new HashSet()); + loginContext = new LoginContext("", subject, null, + KerberosConfiguration.createClientConfig(principal, + keytab)); + loginContext.login(); + subject = loginContext.getSubject(); + Assert.assertEquals(1, subject.getPrincipals().size()); + Assert.assertEquals(KerberosPrincipal.class, + subject.getPrincipals().iterator().next().getClass()); + Assert.assertEquals(principal + "@" + kdc.getRealm(), + subject.getPrincipals().iterator().next().getName()); + loginContext.logout(); + + // server login + subject = new Subject(false, principals, new HashSet(), + new HashSet()); + loginContext = new LoginContext("", subject, null, + KerberosConfiguration.createServerConfig(principal, + keytab)); + loginContext.login(); + subject = loginContext.getSubject(); + Assert.assertEquals(1, subject.getPrincipals().size()); + Assert.assertEquals(KerberosPrincipal.class, + subject.getPrincipals().iterator().next().getClass()); + Assert.assertEquals(principal + "@" + kdc.getRealm(), + subject.getPrincipals().iterator().next().getName()); + loginContext.logout(); + + } finally { + if (loginContext != null) { + loginContext.logout(); + } + } + } + +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java new file mode 100644 index 00000000000..8978d170f26 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java @@ -0,0 +1,146 @@ +/** + * 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.server.quorum.auth; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; +import org.apache.zookeeper.test.ClientBase; +import org.junit.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * QuorumAuthTestBase provides a base class for testing quorum peer mutual + * authentication using SASL mechanisms. + */ +public class QuorumAuthTestBase extends ZKTestCase { + protected static final Logger LOG = LoggerFactory.getLogger(QuorumAuthTestBase.class); + protected List mt = new ArrayList(); + protected static File jaasConfigDir; + + public static void setupJaasConfig(String jaasEntries) { + try { + jaasConfigDir = ClientBase.createTmpDir(); + File saslConfFile = new File(jaasConfigDir, "jaas.conf"); + FileWriter fwriter = new FileWriter(saslConfFile); + fwriter.write(jaasEntries); + fwriter.close(); + System.setProperty("java.security.auth.login.config", + saslConfFile.getAbsolutePath()); + } catch (IOException ioe) { + LOG.error("Failed to create tmp directory to hold JAAS conf file", ioe); + // could not create tmp directory to hold JAAS conf file : test will + // fail now. + } + } + + public static void cleanupJaasConfig() { + if (jaasConfigDir != null) { + FileUtils.deleteQuietly(jaasConfigDir); + } + } + + protected String startQuorum(final int serverCount, + Map authConfigs, int authServerCount) throws IOException { + StringBuilder connectStr = new StringBuilder(); + final int[] clientPorts = startQuorum(serverCount, connectStr, + authConfigs, authServerCount); + for (int i = 0; i < serverCount; i++) { + Assert.assertTrue("waiting for server " + i + " being up", + ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], + ClientBase.CONNECTION_TIMEOUT)); + } + return connectStr.toString(); + } + + protected int[] startQuorum(final int serverCount, StringBuilder connectStr, + Map authConfigs, int authServerCount) throws IOException { + final int clientPorts[] = new int[serverCount]; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < serverCount; i++) { + clientPorts[i] = PortAssignment.unique(); + String server = String.format( + "server.%d=localhost:%d:%d:participant", i, + PortAssignment.unique(), PortAssignment.unique()); + sb.append(server + "\n"); + connectStr.append("127.0.0.1:" + clientPorts[i]); + if (i < serverCount - 1) { + connectStr.append(","); + } + } + String quorumCfg = sb.toString(); + // servers with authentication interfaces configured + int i = 0; + for (; i < authServerCount; i++) { + startServer(authConfigs, clientPorts, quorumCfg, i); + } + // servers without any authentication configured + for (int j = 0; j < serverCount - authServerCount; j++, i++) { + MainThread mthread = new MainThread(i, clientPorts[i], quorumCfg); + mt.add(mthread); + mthread.start(); + } + return clientPorts; + } + + private void startServer(Map authConfigs, + final int[] clientPorts, String quorumCfg, int i) + throws IOException { + MainThread mthread = new MainThread(i, clientPorts[i], quorumCfg, + authConfigs); + mt.add(mthread); + mthread.start(); + } + + protected void startServer(MainThread restartPeer, + Map authConfigs) throws IOException { + MainThread mthread = new MainThread(restartPeer.getMyid(), + restartPeer.getClientPort(), restartPeer.getQuorumCfgSection(), + authConfigs); + mt.add(mthread); + mthread.start(); + } + + void shutdownAll() { + for (int i = 0; i < mt.size(); i++) { + shutdown(i); + } + } + + MainThread shutdown(int index) { + MainThread mainThread = mt.get(index); + try { + mainThread.shutdown(); + } catch (InterruptedException e) { + } finally { + mt.remove(index); + } + mainThread.deleteBaseDir(); + return mainThread; + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java new file mode 100644 index 00000000000..359324549e2 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java @@ -0,0 +1,239 @@ +/** + * 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.server.quorum.auth; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientTest; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; + +/** + * Rolling upgrade should do in three steps: + * + * step-1) Stop the server and set the flags and restart the server. + * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false and quorum.auth.serverRequireSasl=false + * Ensure that all the servers should complete this step. Now, move to next step. + * + * step-2) Stop the server one by one and change the flags and restart the server. + * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=false + * Ensure that all the servers should complete this step. Now, move to next step. + * + * step-3) Stop the server one by one and change the flags and restart the server. + * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=true + * Now, all the servers are fully upgraded and running in secured mode. + */ +public class QuorumAuthUpgradeTest extends QuorumAuthTestBase { + static { + String jaasEntries = new String("" + "QuorumServer {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_test=\"mypassword\";\n" + "};\n" + + "QuorumLearner {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"test\"\n" + + " password=\"mypassword\";\n" + "};\n"); + setupJaasConfig(jaasEntries); + } + + @After + public void tearDown() throws Exception { + shutdownAll(); + } + + @AfterClass + public static void cleanup() { + cleanupJaasConfig(); + } + + /** + * Test to verify that servers are able to start without any authentication. + * peer0 -> quorum.auth.enableSasl=false + * peer1 -> quorum.auth.enableSasl=false + */ + @Test(timeout = 30000) + public void testNullAuthLearnerServer() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); + + String connectStr = startQuorum(2, authConfigs, 0); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.close(); + } + + /** + * Test to verify that servers are able to form quorum. + * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false + * peer1 -> quorum.auth.enableSasl=false, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false + */ + @Test(timeout = 30000) + public void testAuthLearnerAgainstNullAuthServer() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + + String connectStr = startQuorum(2, authConfigs, 1); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.close(); + } + + /** + * Test to verify that servers are able to form quorum. + * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false + * peer1 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false + */ + @Test(timeout = 30000) + public void testAuthLearnerAgainstNoAuthRequiredServer() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + + String connectStr = startQuorum(2, authConfigs, 2); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.close(); + } + + /** + * Test to verify that servers are able to form quorum. + * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true + * peer1 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true + */ + @Test(timeout = 30000) + public void testAuthLearnerServer() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + + String connectStr = startQuorum(2, authConfigs, 2); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + zk.close(); + } + + /** + * Rolling upgrade should do in three steps: + * + * step-1) Stop the server and set the flags and restart the server. + * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false and quorum.auth.serverRequireSasl=false + * Ensure that all the servers should complete this step. Now, move to next step. + * + * step-2) Stop the server one by one and change the flags and restart the server. + * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=false + * Ensure that all the servers should complete this step. Now, move to next step. + * + * step-3) Stop the server one by one and change the flags and restart the server. + * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=true + * Now, all the servers are fully upgraded and running in secured mode. + */ + @Test(timeout = 90000) + public void testRollingUpgrade() throws Exception { + // Start peer0,1,2 servers with quorum.auth.enableSasl=false and + // quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false + // Assume this is an existing cluster. + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); + + String connectStr = startQuorum(3, authConfigs, 0); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + + //1. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and + // quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false"); + restartServer(authConfigs, 0, zk, watcher); + restartServer(authConfigs, 1, zk, watcher); + restartServer(authConfigs, 2, zk, watcher); + + //2. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and + // quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=false + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); + restartServer(authConfigs, 0, zk, watcher); + restartServer(authConfigs, 1, zk, watcher); + restartServer(authConfigs, 2, zk, watcher); + + //3. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and + // quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + restartServer(authConfigs, 0, zk, watcher); + restartServer(authConfigs, 1, zk, watcher); + restartServer(authConfigs, 2, zk, watcher); + + //4. Restart peer2 with quorum.auth.learnerEnableSasl=false and + // quorum.auth.serverRequireSasl=false. It should fail to join the + // quorum as this needs auth. + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); + MainThread m = shutdown(2); + startServer(m, authConfigs); + Assert.assertFalse("waiting for server 2 being up", ClientBase + .waitForServerUp("127.0.0.1:" + m.getClientPort(), 5000)); + } + + private void restartServer(Map authConfigs, int index, + ZooKeeper zk, CountdownWatcher watcher) throws IOException, + KeeperException, InterruptedException, TimeoutException { + LOG.info("Restarting server myid=" + index); + MainThread m = shutdown(index); + startServer(m, authConfigs); + Assert.assertTrue("waiting for server" + index + "being up", + ClientBase.waitForServerUp("127.0.0.1:" + m.getClientPort(), + ClientBase.CONNECTION_TIMEOUT)); + watcher.waitForConnected(ClientTest.CONNECTION_TIMEOUT); + zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java new file mode 100644 index 00000000000..4d66beb72d6 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java @@ -0,0 +1,221 @@ +/** + * 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.server.quorum.auth; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.quorum.QuorumPeerMain; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; + +public class QuorumDigestAuthTest extends QuorumAuthTestBase { + + static { + String jaasEntries = new String("" + + "QuorumServer {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " user_test=\"mypassword\";\n" + "};\n" + + "QuorumLearner {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"test\"\n" + + " password=\"mypassword\";\n" + "};\n" + + "QuorumLearnerInvalid {\n" + + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + + " username=\"test\"\n" + + " password=\"invalid\";\n" + "};" + "\n"); + setupJaasConfig(jaasEntries); + } + + @After + public void tearDown() throws Exception { + for (MainThread mainThread : mt) { + mainThread.shutdown(); + mainThread.deleteBaseDir(); + } + } + + @AfterClass + public static void cleanup(){ + cleanupJaasConfig(); + } + + /** + * Test to verify that server is able to start with valid credentials + */ + @Test(timeout = 30000) + public void testValidCredentials() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + + String connectStr = startQuorum(3, authConfigs, 3); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + for (int i = 0; i < 10; i++) { + zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + zk.close(); + } + + /** + * Test to verify that server is able to start with invalid credentials if + * the configuration is set to quorum.auth.serverRequireSasl=false. + * Quorum will talk each other even if the authentication is not succeeded + */ + @Test(timeout = 30000) + public void testSaslNotRequiredWithInvalidCredentials() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid"); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); + String connectStr = startQuorum(3, authConfigs, 3); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + for (int i = 0; i < 10; i++) { + zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + zk.close(); + } + + /** + * Test to verify that server shouldn't start with invalid credentials + * if the configuration is set to quorum.auth.serverRequireSasl=true, + * quorum.auth.learnerRequireSasl=true + */ + @Test(timeout = 30000) + public void testSaslRequiredInvalidCredentials() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid"); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + int serverCount = 2; + final int[] clientPorts = startQuorum(serverCount, new StringBuilder(), + authConfigs, serverCount); + for (int i = 0; i < serverCount; i++) { + boolean waitForServerUp = ClientBase.waitForServerUp( + "127.0.0.1:" + clientPorts[i], QuorumPeerTestBase.TIMEOUT); + Assert.assertFalse("Shouldn't start server with invalid credentials", + waitForServerUp); + } + } + + /** + * If quorumpeer learner is not auth enabled then self won't be able to join + * quorum. So this test is ensuring that the quorumpeer learner is also auth + * enabled while enabling quorum server require sasl. + */ + @Test(timeout = 10000) + public void testEnableQuorumServerRequireSaslWithoutQuorumLearnerRequireSasl() + throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, + "QuorumLearner"); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false"); + MainThread mthread = new MainThread(1, PortAssignment.unique(), "", + authConfigs); + String args[] = new String[1]; + args[0] = mthread.getConfFile().toString(); + try { + new QuorumPeerMain() { + @Override + protected void initializeAndRun(String[] args) + throws ConfigException, IOException { + super.initializeAndRun(args); + } + }.initializeAndRun(args); + Assert.fail("Must throw exception as quorumpeer learner is not enabled!"); + } catch (ConfigException e) { + // expected + } + } + + + /** + * If quorumpeer learner is not auth enabled then self won't be able to join + * quorum. So this test is ensuring that the quorumpeer learner is also auth + * enabled while enabling quorum server require sasl. + */ + @Test(timeout = 10000) + public void testEnableQuorumAuthenticationConfigurations() + throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, + "QuorumLearner"); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); + + // case-1) 'quorum.auth.enableSasl' is off. Tries to enable server sasl. + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false"); + MainThread mthread = new MainThread(1, PortAssignment.unique(), "", + authConfigs); + String args[] = new String[1]; + args[0] = mthread.getConfFile().toString(); + try { + new QuorumPeerMain() { + @Override + protected void initializeAndRun(String[] args) + throws ConfigException, IOException { + super.initializeAndRun(args); + } + }.initializeAndRun(args); + Assert.fail("Must throw exception as quorum sasl is not enabled!"); + } catch (ConfigException e) { + // expected + } + + // case-1) 'quorum.auth.enableSasl' is off. Tries to enable learner sasl. + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + try { + new QuorumPeerMain() { + @Override + protected void initializeAndRun(String[] args) + throws ConfigException, IOException { + super.initializeAndRun(args); + } + }.initializeAndRun(args); + Assert.fail("Must throw exception as quorum sasl is not enabled!"); + } catch (ConfigException e) { + // expected + } + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java new file mode 100644 index 00000000000..2cc56a76794 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.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.server.quorum.auth; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; + +public class QuorumKerberosAuthTest extends KerberosSecurityTestcase { + private static File keytabFile; + static { + String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true); + String jaasEntries = new String("" + + "QuorumServer {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " useKeyTab=true\n" + + " keyTab=\"" + keytabFilePath + "\"\n" + + " storeKey=true\n" + + " useTicketCache=false\n" + + " debug=false\n" + + " principal=\"" + KerberosTestUtils.getServerPrincipal() + "\";\n" + "};\n" + + "QuorumLearner {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " useKeyTab=true\n" + + " keyTab=\"" + keytabFilePath + "\"\n" + + " storeKey=true\n" + + " useTicketCache=false\n" + + " debug=false\n" + + " principal=\"" + KerberosTestUtils.getLearnerPrincipal() + "\";\n" + "};\n"); + setupJaasConfig(jaasEntries); + } + + @Before + public void setUp() throws Exception { + // create keytab + keytabFile = new File(KerberosTestUtils.getKeytabFile()); + String learnerPrincipal = KerberosTestUtils.getLearnerPrincipal(); + String serverPrincipal = KerberosTestUtils.getServerPrincipal(); + learnerPrincipal = learnerPrincipal.substring(0, learnerPrincipal.lastIndexOf("@")); + serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@")); + getKdc().createPrincipal(keytabFile, learnerPrincipal, serverPrincipal); + } + + @After + public void tearDown() throws Exception { + for (MainThread mainThread : mt) { + mainThread.shutdown(); + mainThread.deleteBaseDir(); + } + } + + @AfterClass + public static void cleanup() { + if(keytabFile != null){ + FileUtils.deleteQuietly(keytabFile); + } + cleanupJaasConfig(); + } + + /** + * Test to verify that server is able to start with valid credentials + */ + @Test(timeout = 120000) + public void testValidCredentials() throws Exception { + String serverPrincipal = KerberosTestUtils.getServerPrincipal(); + serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@")); + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); + String connectStr = startQuorum(3, authConfigs, 3); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + for (int i = 0; i < 10; i++) { + zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + zk.close(); + } +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java new file mode 100644 index 00000000000..fcb76919f1b --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java @@ -0,0 +1,184 @@ +/** + * 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.server.quorum.auth; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import junit.framework.Assert; + +public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase { + private static File keytabFile; + private static String hostServerPrincipal = KerberosTestUtils.getHostServerPrincipal(); + private static String hostLearnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal(); + private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myHost"); + static { + setupJaasConfigEntries(hostServerPrincipal, hostLearnerPrincipal, hostNamedLearnerPrincipal); + } + + private static void setupJaasConfigEntries(String hostServerPrincipal, + String hostLearnerPrincipal, String hostNamedLearnerPrincipal) { + String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true); + String jaasEntries = new String("" + + "QuorumServer {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " useKeyTab=true\n" + + " keyTab=\"" + keytabFilePath + "\"\n" + + " storeKey=true\n" + + " useTicketCache=false\n" + + " debug=false\n" + + " principal=\"" + KerberosTestUtils.replaceHostPattern(hostServerPrincipal) + "\";\n" + "};\n" + + "QuorumLearner {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " useKeyTab=true\n" + + " keyTab=\"" + keytabFilePath + "\"\n" + + " storeKey=true\n" + + " useTicketCache=false\n" + + " debug=false\n" + + " principal=\"" + KerberosTestUtils.replaceHostPattern(hostLearnerPrincipal) + "\";\n" + "};\n" + + "QuorumLearnerMyHost {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " useKeyTab=true\n" + + " keyTab=\"" + keytabFilePath + "\"\n" + + " storeKey=true\n" + + " useTicketCache=false\n" + + " debug=false\n" + + " principal=\"" + hostNamedLearnerPrincipal + "\";\n" + "};\n"); + setupJaasConfig(jaasEntries); + } + + @BeforeClass + public static void setUp() throws Exception { + // create keytab + keytabFile = new File(KerberosTestUtils.getKeytabFile()); + + // Creates principals in the KDC and adds them to a keytab file. + String learnerPrincipal = hostLearnerPrincipal.substring(0, hostLearnerPrincipal.lastIndexOf("@")); + learnerPrincipal = KerberosTestUtils.replaceHostPattern(learnerPrincipal); + String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); + serverPrincipal = KerberosTestUtils.replaceHostPattern(serverPrincipal); + + // learner with ipaddress in principal + String learnerPrincipal2 = hostNamedLearnerPrincipal.substring(0, hostNamedLearnerPrincipal.lastIndexOf("@")); + getKdc().createPrincipal(keytabFile, learnerPrincipal, learnerPrincipal2, serverPrincipal); + } + + @After + public void tearDown() throws Exception { + for (MainThread mainThread : mt) { + mainThread.shutdown(); + mainThread.deleteBaseDir(); + } + } + + @AfterClass + public static void cleanup() { + if(keytabFile != null){ + FileUtils.deleteQuietly(keytabFile); + } + cleanupJaasConfig(); + } + + /** + * Test to verify that server is able to start with valid credentials + */ + @Test(timeout = 120000) + public void testValidCredentials() throws Exception { + String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); + String connectStr = startQuorum(3, authConfigs, 3); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + for (int i = 0; i < 10; i++) { + zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + zk.close(); + } + + /** + * Test to verify that the bad server connection to the quorum should be rejected. + */ + @Test(timeout = 120000) + public void testConnectBadServer() throws Exception { + String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); + String connectStr = startQuorum(3, authConfigs, 3); + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + for (int i = 0; i < 10; i++) { + zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + } + zk.close(); + + String quorumCfgSection = mt.get(0).getQuorumCfgSection(); + StringBuilder sb = new StringBuilder(); + sb.append(quorumCfgSection); + + int myid = mt.size() + 1; + final int clientPort = PortAssignment.unique(); + String server = String.format("server.%d=localhost:%d:%d:participant", + myid, PortAssignment.unique(), PortAssignment.unique()); + sb.append(server + "\n"); + quorumCfgSection = sb.toString(); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, + "QuorumLearnerMyHost"); + MainThread badServer = new MainThread(myid, clientPort, quorumCfgSection, + authConfigs); + badServer.start(); + watcher = new CountdownWatcher(); + connectStr = "127.0.0.1:" + clientPort; + zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); + try{ + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT/3); + Assert.fail("Must throw exception as the myHost is not an authorized one!"); + } catch (TimeoutException e){ + // expected + } finally { + zk.close(); + badServer.shutdown(); + badServer.deleteBaseDir(); + } + } +} diff --git a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java index 80885050f5d..0ecac6e6c74 100644 --- a/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java +++ b/src/java/test/org/apache/zookeeper/test/FLEPredicateTest.java @@ -41,7 +41,7 @@ public class FLEPredicateTest extends ZKTestCase { class MockFLE extends FastLeaderElection { MockFLE(QuorumPeer peer){ - super(peer, new QuorumCnxManager(peer)); + super(peer, peer.createCnxnManager()); } boolean predicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch){ diff --git a/src/zookeeper.jute b/src/zookeeper.jute index 6521e54cd1c..27a5a7ed44f 100644 --- a/src/zookeeper.jute +++ b/src/zookeeper.jute @@ -220,6 +220,11 @@ module org.apache.zookeeper.server.quorum { buffer data; // Only significant when type is request vector authinfo; } + class QuorumAuthPacket { + long magic; + int status; + buffer token; + } } module org.apache.zookeeper.server.persistence { From 9d89dd1134e43c9a4d5e76e2314557e889f2a171 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 12 Dec 2016 23:02:22 +0000 Subject: [PATCH 363/444] ZOOKEEPER-2542: Update NOTICE file with Netty notice in 3.4 Author: Rakesh Radhakrishnan Reviewers: hanm , fpj Closes #121 from rakeshadr/ZK-2542 --- src/NOTICE.txt | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/NOTICE.txt b/src/NOTICE.txt index 7e4c7de34ac..a902c6ab1e8 100644 --- a/src/NOTICE.txt +++ b/src/NOTICE.txt @@ -4,3 +4,97 @@ Copyright 2009-2016 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). +This product includes software developed by +The Netty Project (http://netty.io/) +Copyright 2011 The Netty Project + +The Netty NOTICE file contains the following items: +This product contains the extensions to Java Collections Framework which has +been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: + + * LICENSE: + * license/LICENSE.jsr166y.txt (Public Domain) + * HOMEPAGE: + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ + * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ + +This product contains a modified version of Robert Harder's Public Domain +Base64 Encoder and Decoder, which can be obtained at: + + * LICENSE: + * license/LICENSE.base64.txt (Public Domain) + * HOMEPAGE: + * http://iharder.sourceforge.net/current/java/base64/ + +This product contains a modified version of 'JZlib', a re-implementation of +zlib in pure Java, which can be obtained at: + + * LICENSE: + * license/LICENSE.jzlib.txt (BSD Style License) + * HOMEPAGE: + * http://www.jcraft.com/jzlib/ + +This product contains a modified version of 'Webbit', a Java event based +WebSocket and HTTP server: + + * LICENSE: + * license/LICENSE.webbit.txt (BSD License) + * HOMEPAGE: + * https://github.com/joewalnes/webbit + +This product optionally depends on 'Protocol Buffers', Google's data +interchange format, which can be obtained at: + + * LICENSE: + * license/LICENSE.protobuf.txt (New BSD License) + * HOMEPAGE: + * http://code.google.com/p/protobuf/ + +This product optionally depends on 'Bouncy Castle Crypto APIs' to generate +a temporary self-signed X.509 certificate when the JVM does not provide the +equivalent functionality. It can be obtained at: + + * LICENSE: + * license/LICENSE.bouncycastle.txt (MIT License) + * HOMEPAGE: + * http://www.bouncycastle.org/ + +This product optionally depends on 'SLF4J', a simple logging facade for Java, +which can be obtained at: + + * LICENSE: + * license/LICENSE.slf4j.txt (MIT License) + * HOMEPAGE: + * http://www.slf4j.org/ + +This product optionally depends on 'Apache Commons Logging', a logging +framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-logging.txt (Apache License 2.0) + * HOMEPAGE: + * http://commons.apache.org/logging/ + +This product optionally depends on 'Apache Log4J', a logging framework, +which can be obtained at: + + * LICENSE: + * license/LICENSE.log4j.txt (Apache License 2.0) + * HOMEPAGE: + * http://logging.apache.org/log4j/ + +This product optionally depends on 'JBoss Logging', a logging framework, +which can be obtained at: + + * LICENSE: + * license/LICENSE.jboss-logging.txt (GNU LGPL 2.1) + * HOMEPAGE: + * http://anonsvn.jboss.org/repos/common/common-logging-spi/ + +This product optionally depends on 'Apache Felix', an open source OSGi +framework implementation, which can be obtained at: + + * LICENSE: + * license/LICENSE.felix.txt (Apache License 2.0) + * HOMEPAGE: + * http://felix.apache.org/ From 48adec48be35f10a8fa45ecf6a7bdfc3a10a7f3c Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Thu, 15 Dec 2016 21:17:59 +0530 Subject: [PATCH 364/444] ZOOKEEPER-2552: Revisit release doc and remove the issues not covered by release Author: Edward Ribeiro Reviewers: Rakesh Radhakrishnan Closes #124 from eribeiro/ZOOKEEPER-2552 --- docs/releasenotes.html | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 71d70720c0c..337a7ea9c7f 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -287,21 +287,10 @@

            Bug
          • [ZOOKEEPER-2360] - Update commons collections version used by tests/releaseaudit
          • -
          • [ZOOKEEPER-2391] - setMin/MaxSessionTimeout of ZookeeperServer are implemented in quite a weak way -
          • [ZOOKEEPER-2412] - leader zk out of memory, and leader db lastZxid is not update when process set data.
          • -
          • [ZOOKEEPER-2468] - SetQuota and DelQuota -
          • -

            Improvement -

            -
              -
            • [ZOOKEEPER-2512] - Allow Jetty dependency to use HTTPS and Basic Auth -
            • -
            - Release Notes - ZooKeeper - Version 3.4.7

            Sub-task @@ -691,10 +680,6 @@

            Bug
          • [ZOOKEEPER-2015] - I found memory leak in zk client for c++
          • -
          • [ZOOKEEPER-2236] - Zookeeper truncates file to 0bytes -
          • -
          • [ZOOKEEPER-2327] - "ConnectionLoss for /dog" -
          • Improvement @@ -728,8 +713,6 @@

            Improvement
          • [ZOOKEEPER-1771] - ZooInspector authentication
          • -
          • [ZOOKEEPER-2313] - Refactor ZooKeeperServerBean and its subclasses (LeaderBean, ObserverBean, FollowerBean) -
          • Task From 52d365041759229ddc46798e34ee845fc1e26732 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 20 Dec 2016 13:52:50 +0000 Subject: [PATCH 365/444] ZOOKEEPER-2479: Add 'electionTimeTaken' value in LeaderMXBean and FollowerMXBean This PR is against branch-3.4, please review. Thanks! Author: Rakesh Radhakrishnan Reviewers: fpj Closes #130 from rakeshadr/ZK-2479-br-3-4 --- .../zookeeper/server/quorum/Follower.java | 5 +- .../zookeeper/server/quorum/FollowerBean.java | 7 ++- .../server/quorum/FollowerMXBean.java | 5 ++ .../zookeeper/server/quorum/Leader.java | 5 +- .../zookeeper/server/quorum/LeaderBean.java | 4 ++ .../zookeeper/server/quorum/LeaderMXBean.java | 5 ++ .../zookeeper/server/quorum/QuorumPeer.java | 23 +++++++++ .../test/HierarchicalQuorumTest.java | 40 +++++++++++++++- .../org/apache/zookeeper/test/JMXEnv.java | 48 +++++++++++++++++++ 9 files changed, 136 insertions(+), 6 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java index 9aa0d0b5985..a17af496e94 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java @@ -59,8 +59,9 @@ public String toString() { */ void followLeader() throws InterruptedException { self.end_fle = System.currentTimeMillis(); - LOG.info("FOLLOWING - LEADER ELECTION TOOK - " + - (self.end_fle - self.start_fle)); + long electionTimeTaken = self.end_fle - self.start_fle; + self.setElectionTimeTaken(electionTimeTaken); + LOG.info("FOLLOWING - LEADER ELECTION TOOK - {}", electionTimeTaken); self.start_fle = 0; self.end_fle = 0; fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java index fd31fa2b6ef..43d7cb7b922 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerBean.java @@ -22,7 +22,7 @@ import org.apache.zookeeper.server.ZooKeeperServerBean; /** - * Follower MBean inteface implementation + * Follower MBean interface implementation. */ public class FollowerBean extends ZooKeeperServerBean implements FollowerMXBean { private final Follower follower; @@ -47,4 +47,9 @@ public String getLastQueuedZxid() { public int getPendingRevalidationCount() { return follower.getPendingRevalidationsCount(); } + + @Override + public long getElectionTimeTaken() { + return follower.self.getElectionTimeTaken(); + } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java index ded0e1ca0e1..45c7fd8e2e7 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FollowerMXBean.java @@ -38,4 +38,9 @@ public interface FollowerMXBean extends ZooKeeperServerMXBean { * @return count of pending revalidations */ public int getPendingRevalidationCount(); + + /** + * @return time taken for leader election in milliseconds. + */ + public long getElectionTimeTaken(); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 710745d4a4a..44e6b4f4e56 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -366,8 +366,9 @@ public void halt() { */ void lead() throws IOException, InterruptedException { self.end_fle = System.currentTimeMillis(); - LOG.info("LEADING - LEADER ELECTION TOOK - " + - (self.end_fle - self.start_fle)); + long electionTimeTaken = self.end_fle - self.start_fle; + self.setElectionTimeTaken(electionTimeTaken); + LOG.info("LEADING - LEADER ELECTION TOOK - {}", electionTimeTaken); self.start_fle = 0; self.end_fle = 0; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java index b5a3a10ecd0..6ab2c307d92 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderBean.java @@ -50,4 +50,8 @@ public String followerInfo() { return sb.toString(); } + @Override + public long getElectionTimeTaken() { + return leader.self.getElectionTimeTaken(); + } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java b/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java index bf08104ec7d..66428a4f286 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LeaderMXBean.java @@ -33,4 +33,9 @@ public interface LeaderMXBean extends ZooKeeperServerMXBean { * @return information on current followers */ public String followerInfo(); + + /** + * @return time taken for leader election in milliseconds. + */ + public long getElectionTimeTaken(); } 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 2dbedcf802e..889ee62a851 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -395,6 +395,12 @@ synchronized void setBCVote(Vote v) { */ protected int quorumCnxnThreadsSize = QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE; + /** + * Keeps time taken for leader election in milliseconds. Sets the value to + * this variable only after the completion of leader election. + */ + private long electionTimeTaken = -1; + /** * @deprecated As of release 3.4.0, this class has been deprecated, since * it is used with one of the udp-based versions of leader election, which @@ -1459,4 +1465,21 @@ public QuorumCnxManager createCnxnManager() { this.quorumCnxnThreadsSize, this.isQuorumSaslAuthEnabled()); } + + /** + * Sets the time taken for leader election in milliseconds. + * + * @param electionTimeTaken + * time taken for leader election + */ + void setElectionTimeTaken(long electionTimeTaken) { + this.electionTimeTaken = electionTimeTaken; + } + + /** + * @return the time taken for leader election in milliseconds. + */ + long getElectionTimeTaken() { + return electionTimeTaken; + } } diff --git a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java index c6573f46292..a771c3acd42 100644 --- a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java @@ -20,9 +20,10 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Properties; import java.util.Set; @@ -30,8 +31,11 @@ import org.slf4j.LoggerFactory; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; +import org.apache.zookeeper.jmx.CommonNames; import org.apache.zookeeper.server.quorum.QuorumPeer; +import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.junit.Assert; import org.junit.Test; @@ -139,23 +143,28 @@ void startServers(boolean withObservers) throws Exception { : QuorumPeer.LearnerType.PARTICIPANT)); LOG.info("creating QuorumPeer 1 port " + port1); + List qps = new ArrayList<>(); QuorumHierarchical hq1 = new QuorumHierarchical(qp); s1 = new QuorumPeer(peers, s1dir, s1dir, port1, 3, 1, tickTime, initLimit, syncLimit, hq1); + qps.add(s1); Assert.assertEquals(port1, s1.getClientPort()); LOG.info("creating QuorumPeer 2 port " + port2); QuorumHierarchical hq2 = new QuorumHierarchical(qp); s2 = new QuorumPeer(peers, s2dir, s2dir, port2, 3, 2, tickTime, initLimit, syncLimit, hq2); + qps.add(s2); Assert.assertEquals(port2, s2.getClientPort()); LOG.info("creating QuorumPeer 3 port " + port3); QuorumHierarchical hq3 = new QuorumHierarchical(qp); s3 = new QuorumPeer(peers, s3dir, s3dir, port3, 3, 3, tickTime, initLimit, syncLimit, hq3); + qps.add(s3); Assert.assertEquals(port3, s3.getClientPort()); LOG.info("creating QuorumPeer 4 port " + port4); QuorumHierarchical hq4 = new QuorumHierarchical(qp); s4 = new QuorumPeer(peers, s4dir, s4dir, port4, 3, 4, tickTime, initLimit, syncLimit, hq4); + qps.add(s4); if (withObservers) { s4.setLearnerType(QuorumPeer.LearnerType.OBSERVER); } @@ -164,6 +173,7 @@ void startServers(boolean withObservers) throws Exception { LOG.info("creating QuorumPeer 5 port " + port5); QuorumHierarchical hq5 = new QuorumHierarchical(qp); s5 = new QuorumPeer(peers, s5dir, s5dir, port5, 3, 5, tickTime, initLimit, syncLimit, hq5); + qps.add(s5); if (withObservers) { s5.setLearnerType(QuorumPeer.LearnerType.OBSERVER); } @@ -219,6 +229,7 @@ void startServers(boolean withObservers) throws Exception { ensureNames.add("name0=ReplicatedServer_id" + i); } JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()])); + verifyElectionTimeTakenJMXAttribute(qps); } @Override @@ -264,6 +275,33 @@ protected TestableZooKeeper createClient(String hp) return createClient(watcher, hp); } + private void verifyElectionTimeTakenJMXAttribute(List peers) + throws Exception { + LOG.info("Verify QuorumPeer#electionTimeTaken jmx bean attribute"); + + for (int i = 1; i <= peers.size(); i++) { + QuorumPeer qp = peers.get(i - 1); + if (qp.getLearnerType() == LearnerType.OBSERVER) { + continue; // Observer don't have electionTimeTaken attribute. + } + Long electionTimeTaken = -1L; + String bean = ""; + if (qp.getPeerState() == ServerState.FOLLOWING) { + bean = String.format( + "%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Follower", + CommonNames.DOMAIN, i, i); + } else if (qp.getPeerState() == ServerState.LEADING) { + bean = String.format( + "%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Leader", + CommonNames.DOMAIN, i, i); + } + electionTimeTaken = (Long) JMXEnv.ensureBeanAttribute(bean, + "ElectionTimeTaken"); + Assert.assertTrue("Wrong electionTimeTaken value!", + electionTimeTaken >= 0); + } + } + @Test public void testHierarchicalQuorum() throws Throwable { cht.runHammer(5, 10); diff --git a/src/java/test/org/apache/zookeeper/test/JMXEnv.java b/src/java/test/org/apache/zookeeper/test/JMXEnv.java index f9cdef74992..f67c67c7744 100644 --- a/src/java/test/org/apache/zookeeper/test/JMXEnv.java +++ b/src/java/test/org/apache/zookeeper/test/JMXEnv.java @@ -37,6 +37,7 @@ import org.apache.zookeeper.jmx.CommonNames; import org.apache.zookeeper.jmx.MBeanRegistry; +import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -274,4 +275,51 @@ private static boolean compare(String bean, String name) { } return false; } + + /** + * Ensure that the specified bean name and its attribute is registered. Note + * that these are components of the name. It waits in a loop up to 60 + * seconds before failing if there is a mismatch. This will return the beans + * which are not matched. + * + * @param expectedName + * - expected bean + * @param expectedAttribute + * - expected attribute + * @return the value of the attribute + * + * @throws Exception + */ + public static Object ensureBeanAttribute(String expectedName, + String expectedAttribute) throws Exception { + String value = ""; + LOG.info("ensure bean:{}, attribute:{}", new Object[] { expectedName, + expectedAttribute }); + + Set beans; + int nTry = 0; + do { + if (nTry++ > 0) { + Thread.sleep(500); + } + try { + beans = conn().queryNames( + new ObjectName(CommonNames.DOMAIN + ":*"), null); + } catch (MalformedObjectNameException e) { + throw new RuntimeException(e); + } + LOG.info("expect:" + expectedName); + for (ObjectName bean : beans) { + // check the existence of name in bean + if (bean.toString().equals(expectedName)) { + LOG.info("found:{} {}", new Object[] { expectedName, bean }); + return conn().getAttribute(bean, expectedAttribute); + } + } + } while (nTry < 120); + Assert.fail("Failed to find bean:" + expectedName + ", attribute:" + + expectedAttribute); + return value; + } + } From 5de4fa3d8acdfdac353bb54fcaa8c8e732432891 Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Wed, 21 Dec 2016 19:10:14 -0800 Subject: [PATCH 366/444] ZOOKEEPER-2470: ServerConfig#parse(String[]) ignores tickTime Author: Edward Ribeiro Reviewers: Michael Han , Rakesh Radhakrishnan Closes #125 from eribeiro/ZOOKEEPER-2470 --- .../apache/zookeeper/server/ServerConfig.java | 7 +-- .../apache/zookeeper/ServerConfigTest.java | 58 +++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/ServerConfigTest.java diff --git a/src/java/main/org/apache/zookeeper/server/ServerConfig.java b/src/java/main/org/apache/zookeeper/server/ServerConfig.java index ec710cd3a15..626ae113cc4 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/ServerConfig.java @@ -47,20 +47,19 @@ public class ServerConfig { /** * Parse arguments for server configuration - * @param args clientPort dataDir and optional tickTime + * @param args clientPort dataDir and optional tickTime and maxClientCnxns * @return ServerConfig configured wrt arguments * @throws IllegalArgumentException on invalid usage */ public void parse(String[] args) { if (args.length < 2 || args.length > 4) { - throw new IllegalArgumentException("Invalid args:" - + Arrays.toString(args)); + throw new IllegalArgumentException("Invalid number of arguments:" + Arrays.toString(args)); } clientPortAddress = new InetSocketAddress(Integer.parseInt(args[0])); dataDir = args[1]; dataLogDir = dataDir; - if (args.length == 3) { + if (args.length >= 3) { tickTime = Integer.parseInt(args[2]); } if (args.length == 4) { diff --git a/src/java/test/org/apache/zookeeper/ServerConfigTest.java b/src/java/test/org/apache/zookeeper/ServerConfigTest.java new file mode 100644 index 00000000000..d4855fd81fe --- /dev/null +++ b/src/java/test/org/apache/zookeeper/ServerConfigTest.java @@ -0,0 +1,58 @@ +/** + * 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.server.ServerConfig; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ServerConfigTest { + + private ServerConfig serverConfig; + + @Before + public void setUp() { + serverConfig = new ServerConfig(); + } + + @Test(expected=IllegalArgumentException.class) + public void testFewArguments() { + String[] args = {"2181"}; + serverConfig.parse(args); + } + + @Test + public void testValidArguments() { + String[] args = {"2181", "/data/dir", "60000", "10000"}; + serverConfig.parse(args); + + assertEquals(2181, serverConfig.getClientPortAddress().getPort()); + assertEquals("/data/dir", serverConfig.getDataDir()); + assertEquals(60000, serverConfig.getTickTime()); + assertEquals(10000, serverConfig.getMaxClientCnxns()); + } + + @Test(expected=IllegalArgumentException.class) + public void testTooManyArguments() { + String[] args = {"2181", "/data/dir", "60000", "10000", "9999"}; + serverConfig.parse(args); + } +} From 7fd0884b0d6577162d71aedc460a588576cd483a Mon Sep 17 00:00:00 2001 From: fpj Date: Wed, 21 Dec 2016 20:48:24 -0800 Subject: [PATCH 367/444] ZOOKEEPER-2646: Java target in branch 3.4 doesn't match documentation Author: fpj Reviewers: Michael Han Closes #127 from fpj/ZK-2646 --- build.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.xml b/build.xml index 5c4fab25474..facf8c34943 100644 --- a/build.xml +++ b/build.xml @@ -36,8 +36,8 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - - + + @@ -611,7 +611,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + From 3b65d7513b69c436fb8461a630d2f35bf418fb6c Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Wed, 21 Dec 2016 22:32:39 -0800 Subject: [PATCH 368/444] ZOOKEEPER-2652: Fix HierarchicalQuorumTest.java Author: Rakesh Radhakrishnan Reviewers: Raul Gutierrez Segales Closes #132 from rakeshadr/ZK-2652 --- .../test/org/apache/zookeeper/test/HierarchicalQuorumTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java index a771c3acd42..051da1330d9 100644 --- a/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java +++ b/src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java @@ -143,7 +143,7 @@ void startServers(boolean withObservers) throws Exception { : QuorumPeer.LearnerType.PARTICIPANT)); LOG.info("creating QuorumPeer 1 port " + port1); - List qps = new ArrayList<>(); + List qps = new ArrayList(); QuorumHierarchical hq1 = new QuorumHierarchical(qp); s1 = new QuorumPeer(peers, s1dir, s1dir, port1, 3, 1, tickTime, initLimit, syncLimit, hq1); qps.add(s1); From c8cc54c7271327aef19f75039472e3250cae63ea Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Thu, 22 Dec 2016 21:58:23 -0800 Subject: [PATCH 369/444] =?UTF-8?q?ZOOKEEPER-2656:=20Fix=20ServerConfigTes?= =?UTF-8?q?t#testValidArguments=20test=20case=20fai=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …lures Author: Rakesh Radhakrishnan Reviewers: Michael Han Closes #134 from rakeshadr/ZK-2656 (cherry picked from commit 085a7ab4efe04e566e952af1123e98eda609bd1b) Signed-off-by: Michael Han --- src/java/test/org/apache/zookeeper/ServerConfigTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/java/test/org/apache/zookeeper/ServerConfigTest.java b/src/java/test/org/apache/zookeeper/ServerConfigTest.java index d4855fd81fe..e2d0da8307c 100644 --- a/src/java/test/org/apache/zookeeper/ServerConfigTest.java +++ b/src/java/test/org/apache/zookeeper/ServerConfigTest.java @@ -24,6 +24,8 @@ import static org.junit.Assert.assertEquals; +import java.io.File; + public class ServerConfigTest { private ServerConfig serverConfig; @@ -45,7 +47,7 @@ public void testValidArguments() { serverConfig.parse(args); assertEquals(2181, serverConfig.getClientPortAddress().getPort()); - assertEquals("/data/dir", serverConfig.getDataDir()); + assertEquals(new File("/data/dir"), serverConfig.getDataDir()); assertEquals(60000, serverConfig.getTickTime()); assertEquals(10000, serverConfig.getMaxClientCnxns()); } From 5c9299d60f66afb00072ebeadead49f4410aa11f Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Fri, 30 Dec 2016 14:58:56 -0800 Subject: [PATCH 370/444] ZOOKEEPER-2465: Documentation copyright notice is out of date. Author: Edward Ribeiro Reviewers: Michael Han Closes #138 from eribeiro/ZOOKEEPER-2465 --- src/docs/src/documentation/skinconf.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/src/documentation/skinconf.xml b/src/docs/src/documentation/skinconf.xml index 685a86a7cdb..43f3a490bcd 100644 --- a/src/docs/src/documentation/skinconf.xml +++ b/src/docs/src/documentation/skinconf.xml @@ -84,7 +84,7 @@ which will be used to configure the chosen Forrest skin. images/favicon.ico - 2008-2013 + The Apache Software Foundation. http://www.apache.org/licenses/ From d99d93a46cba7caf5ed44dfe4c2447a34ad0e383 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 3 Jan 2017 09:53:23 -0800 Subject: [PATCH 371/444] ZOOKEEPER-2383: Startup race in ZooKeeperServer Author: Rakesh Radhakrishnan Reviewers: Michael Han Closes #135 from rakeshadr/ZK-2383-br-3.4 and squashes the following commits: 79c8c82 [Rakesh Radhakrishnan] ZOOKEEPER-2383: Startup race in ZooKeeperServer 08d2651 [Rakesh Radhakrishnan] ZOOKEEPER-2383: Startup race in ZooKeeperServer --- .../zookeeper/server/NIOServerCnxn.java | 30 +- .../zookeeper/server/NettyServerCnxn.java | 28 +- .../server/ZooKeeperServerStartupTest.java | 302 ++++++++++++++++++ 3 files changed, 337 insertions(+), 23 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index 0ed1c644d39..746102dda3d 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -412,7 +412,7 @@ public void enableRecv() { } private void readConnectRequest() throws IOException, InterruptedException { - if (zkServer == null) { + if (!isZKServerRunning()) { throw new IOException("ZooKeeperServer not running"); } zkServer.processConnectRequest(this, incomingBuffer); @@ -575,7 +575,7 @@ private class ConfCommand extends CommandThread { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { zkServer.dumpConf(pw); @@ -590,7 +590,7 @@ public StatResetCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { @@ -607,7 +607,7 @@ public CnxnStatResetCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { synchronized(factory.cnxns){ @@ -627,7 +627,7 @@ public DumpCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { @@ -649,7 +649,7 @@ public StatCommand(PrintWriter pw, int len) { @SuppressWarnings("unchecked") @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { @@ -691,7 +691,7 @@ public ConsCommand(PrintWriter pw) { @SuppressWarnings("unchecked") @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { // clone should be faster than iteration @@ -718,7 +718,7 @@ public WatchCommand(PrintWriter pw, int len) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { DataTree dt = zkServer.getZKDatabase().getDataTree(); @@ -742,7 +742,7 @@ private class MonitorCommand extends CommandThread { @Override public void commandRun() { - if(zkServer == null) { + if(!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); return; } @@ -804,7 +804,7 @@ public IsroCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.print("null"); } else if (zkServer instanceof ReadOnlyZooKeeperServer) { pw.print("ro"); @@ -928,7 +928,7 @@ private boolean readLength(SelectionKey k) throws IOException { if (len < 0 || len > BinaryInputArchive.maxBuffer) { throw new IOException("Len error " + len); } - if (zkServer == null) { + if (!isZKServerRunning()) { throw new IOException("ZooKeeperServer not running"); } incomingBuffer = ByteBuffer.allocate(len); @@ -1154,10 +1154,16 @@ public InetSocketAddress getRemoteSocketAddress() { @Override protected ServerStats serverStats() { - if (zkServer == null) { + if (!isZKServerRunning()) { return null; } return zkServer.serverStats(); } + /** + * @return true if the server is running, false otherwise. + */ + boolean isZKServerRunning() { + return zkServer != null && zkServer.isRunning(); + } } diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index 35c85185a66..32fc371e6de 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -382,7 +382,7 @@ private class ConfCommand extends CommandThread { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { zkServer.dumpConf(pw); @@ -397,7 +397,7 @@ public StatResetCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { @@ -414,7 +414,7 @@ public CnxnStatResetCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { synchronized(factory.cnxns){ @@ -434,7 +434,7 @@ public DumpCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { @@ -455,7 +455,7 @@ public StatCommand(PrintWriter pw, int len) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { @@ -495,7 +495,7 @@ public ConsCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { // clone should be faster than iteration @@ -522,7 +522,7 @@ public WatchCommand(PrintWriter pw, int len) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { DataTree dt = zkServer.getZKDatabase().getDataTree(); @@ -546,7 +546,7 @@ private class MonitorCommand extends CommandThread { @Override public void commandRun() { - if(zkServer == null) { + if(!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); return; } @@ -608,7 +608,7 @@ public IsroCommand(PrintWriter pw) { @Override public void commandRun() { - if (zkServer == null) { + if (!isZKServerRunning()) { pw.print("null"); } else if (zkServer instanceof ReadOnlyZooKeeperServer) { pw.print("ro"); @@ -735,7 +735,7 @@ public void receiveMessage(ChannelBuffer message) { bb.flip(); ZooKeeperServer zks = this.zkServer; - if (zks == null) { + if (zks == null || !zks.isRunning()) { throw new IOException("ZK down"); } if (initialized) { @@ -846,10 +846,16 @@ public void sendCloseSession() { @Override protected ServerStats serverStats() { - if (zkServer == null) { + if (!isZKServerRunning()) { return null; } return zkServer.serverStats(); } + /** + * @return true if the server is running, false otherwise. + */ + boolean isZKServerRunning() { + return zkServer != null && zkServer.isRunning(); + } } diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java new file mode 100644 index 00000000000..9435711b357 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java @@ -0,0 +1,302 @@ +/** + * 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.server; + +import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class tests the startup behavior of ZooKeeper server. + */ +public class ZooKeeperServerStartupTest extends ZKTestCase { + private static final Logger LOG = LoggerFactory + .getLogger(ZooKeeperServerStartupTest.class); + private static int PORT = PortAssignment.unique(); + private static String HOST = "127.0.0.1"; + private static String HOSTPORT = HOST + ":" + PORT; + private static final String ZK_NOT_SERVING = "This ZooKeeper instance is not currently serving requests"; + + private ServerCnxnFactory servcnxnf; + private ZooKeeperServer zks; + private File tmpDir; + private CountDownLatch startupDelayLatch = new CountDownLatch(1); + + @After + public void teardown() throws Exception { + // count down to avoid infinite blocking call due to this latch, if + // any. + startupDelayLatch.countDown(); + + if (servcnxnf != null) { + servcnxnf.shutdown(); + } + if (zks != null) { + zks.shutdown(); + } + if (zks.getZKDatabase() != null) { + zks.getZKDatabase().close(); + } + if (tmpDir != null) { + ClientBase.recursiveDelete(tmpDir); + } + } + + /** + * Test case for + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. + */ + @Test(timeout = 30000) + public void testClientConnectionRequestDuringStartupWithNIOServerCnxn() + throws Exception { + tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + + startSimpleZKServer(startupDelayLatch); + SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks; + Assert.assertTrue( + "Failed to invoke zks#startup() method during server startup", + simplezks.waitForStartupInvocation(10)); + + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zkClient = new ZooKeeper(HOSTPORT, + ClientBase.CONNECTION_TIMEOUT, watcher); + + Assert.assertFalse( + "Since server is not fully started, zks#createSession() shouldn't be invoked", + simplezks.waitForSessionCreation(5)); + + LOG.info( + "Decrements the count of the latch, so that server will proceed with startup"); + startupDelayLatch.countDown(); + + Assert.assertTrue("waiting for server being up ", ClientBase + .waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); + + Assert.assertTrue( + "Failed to invoke zks#createSession() method during client session creation", + simplezks.waitForSessionCreation(5)); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zkClient.close(); + } + + /** + * Test case for + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. + */ + @Test(timeout = 30000) + public void testClientConnectionRequestDuringStartupWithNettyServerCnxn() + throws Exception { + tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + + String originalServerCnxnFactory = System + .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); + try { + System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, + NettyServerCnxnFactory.class.getName()); + startSimpleZKServer(startupDelayLatch); + SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks; + Assert.assertTrue( + "Failed to invoke zks#startup() method during server startup", + simplezks.waitForStartupInvocation(10)); + + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zkClient = new ZooKeeper(HOSTPORT, + ClientBase.CONNECTION_TIMEOUT, watcher); + + Assert.assertFalse( + "Since server is not fully started, zks#createSession() shouldn't be invoked", + simplezks.waitForSessionCreation(5)); + + LOG.info( + "Decrements the count of the latch, so that server will proceed with startup"); + startupDelayLatch.countDown(); + + Assert.assertTrue("waiting for server being up ", ClientBase + .waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); + + Assert.assertTrue( + "Failed to invoke zks#createSession() method during client session creation", + simplezks.waitForSessionCreation(5)); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zkClient.close(); + } finally { + // reset cnxn factory + if (originalServerCnxnFactory == null) { + System.clearProperty( + ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); + } else { + System.setProperty( + ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, + originalServerCnxnFactory); + } + } + } + + /** + * Test case for + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. + */ + @Test(timeout = 30000) + public void testFourLetterWordsWithNIOServerCnxn() throws Exception { + startSimpleZKServer(startupDelayLatch); + verify("conf", ZK_NOT_SERVING); + verify("crst", ZK_NOT_SERVING); + verify("cons", ZK_NOT_SERVING); + verify("dump", ZK_NOT_SERVING); + verify("mntr", ZK_NOT_SERVING); + verify("stat", ZK_NOT_SERVING); + verify("srst", ZK_NOT_SERVING); + verify("wchs", ZK_NOT_SERVING); + verify("isro", "null"); + } + + /** + * Test case for + * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-2383}. + */ + @Test(timeout = 30000) + public void testFourLetterWordsWithNettyServerCnxn() throws Exception { + String originalServerCnxnFactory = System + .getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); + try { + startSimpleZKServer(startupDelayLatch); + verify("conf", ZK_NOT_SERVING); + verify("crst", ZK_NOT_SERVING); + verify("cons", ZK_NOT_SERVING); + verify("dump", ZK_NOT_SERVING); + verify("mntr", ZK_NOT_SERVING); + verify("stat", ZK_NOT_SERVING); + verify("srst", ZK_NOT_SERVING); + verify("wchs", ZK_NOT_SERVING); + verify("isro", "null"); + } finally { + // reset cnxn factory + if (originalServerCnxnFactory == null) { + System.clearProperty( + ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); + } else { + System.setProperty( + ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, + originalServerCnxnFactory); + } + } + } + + private void verify(String cmd, String expected) + throws IOException { + String resp = sendRequest(cmd); + LOG.info("cmd " + cmd + " expected " + expected + " got " + resp); + Assert.assertTrue("Unexpected response: " + resp, + resp.contains(expected)); + } + + private String sendRequest(String cmd) + throws IOException { + return send4LetterWord(HOST, PORT, cmd); + } + + private void startSimpleZKServer(CountDownLatch startupDelayLatch) + throws IOException { + zks = new SimpleZooKeeperServer(tmpDir, tmpDir, 3000, + startupDelayLatch); + SyncRequestProcessor.setSnapCount(100); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + + servcnxnf = ServerCnxnFactory.createFactory(PORT, -1); + Thread startupThread = new Thread() { + public void run() { + try { + servcnxnf.startup(zks); + } catch (IOException e) { + LOG.error("Unexcepted exception during server startup", e); + // Ignoring exception. If there is an ioexception + // then one of the following assertion will fail + } catch (InterruptedException e) { + LOG.error("Unexcepted exception during server startup", e); + // Ignoring exception. If there is an interrupted exception + // then one of the following assertion will fail + } + }; + }; + LOG.info("Starting zk server {}", HOSTPORT); + startupThread.start(); + } + + private static class SimpleZooKeeperServer extends ZooKeeperServer { + private CountDownLatch startupDelayLatch; + private CountDownLatch startupInvokedLatch = new CountDownLatch(1); + private CountDownLatch createSessionInvokedLatch = new CountDownLatch( + 1); + + public SimpleZooKeeperServer(File snapDir, File logDir, int tickTime, + CountDownLatch startupDelayLatch) throws IOException { + super(snapDir, logDir, tickTime); + this.startupDelayLatch = startupDelayLatch; + } + + @Override + public synchronized void startup() { + try { + startupInvokedLatch.countDown(); + // Delaying the zk server startup so that + // ZooKeeperServer#sessionTracker reference won't be + // initialized. In the defect scenario, while processing the + // connection request zkServer needs sessionTracker reference, + // but this is not yet initialized and the server is still in + // the startup phase, resulting in NPE. + startupDelayLatch.await(); + } catch (InterruptedException e) { + Assert.fail( + "Unexpected InterruptedException while startinng up!"); + } + super.startup(); + } + + @Override + long createSession(ServerCnxn cnxn, byte[] passwd, int timeout) { + createSessionInvokedLatch.countDown(); + return super.createSession(cnxn, passwd, timeout); + } + + boolean waitForStartupInvocation(long timeout) + throws InterruptedException { + return startupInvokedLatch.await(timeout, TimeUnit.SECONDS); + } + + boolean waitForSessionCreation(long timeout) + throws InterruptedException { + return createSessionInvokedLatch.await(timeout, TimeUnit.SECONDS); + } + } +} \ No newline at end of file From 8b75543a5375832333468345ca68d3ae6aa88f64 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 3 Jan 2017 21:24:11 -0800 Subject: [PATCH 372/444] Test Improvement by adding more QuorumPeer Auth related test cases Author: Rakesh Radhakrishnan Reviewers: Raul Gutierrez Segales Closes #131 from rakeshadr/ZK-2650 --- .../server/quorum/QuorumCnxManager.java | 1 + .../quorum/auth/SaslQuorumAuthLearner.java | 5 +- .../quorum/auth/SaslQuorumAuthServer.java | 6 +- .../quorum/auth/QuorumAuthTestBase.java | 78 +++++++- .../quorum/auth/QuorumDigestAuthTest.java | 175 +++++++++++++++++- 5 files changed, 248 insertions(+), 17 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 74d1c1e621c..6a5ebb4f343 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -456,6 +456,7 @@ private void handleConnection(Socket sock, DataInputStream din) } // do authenticating learner + LOG.debug("Authenticating learner server.id: {}", sid); authServer.authenticate(sock, din); //If wins the challenge, then close the new connection. diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java index fffb55ac496..2643808202e 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java @@ -108,7 +108,10 @@ public void authenticate(Socket sock, String hostName) throws IOException { learnerLogin); // we're done; don't expect to send another BIND if (responseToken != null) { - throw new SaslException("Protocol error: attempting to send response after completion"); + throw new SaslException( + "Protocol error: attempting to send response after completion" + + ". Server addr: " + + sock.getRemoteSocketAddress()); } break; case IN_PROGRESS: diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java index 8430da20ded..0a67b79a8ea 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java @@ -78,8 +78,10 @@ public void authenticate(Socket sock, DataInputStream din) try { if (!QuorumAuth.nextPacketIsAuth(din)) { if (quorumRequireSasl) { - throw new SaslException("Learner not trying to authenticate" - + " and authentication is required"); + throw new SaslException( + "Learner " + sock.getRemoteSocketAddress() + + " not trying to authenticate" + + " and authentication is required"); } else { // let it through, we don't require auth return; diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java index 8978d170f26..4d4b071b304 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java @@ -68,7 +68,7 @@ public static void cleanupJaasConfig() { protected String startQuorum(final int serverCount, Map authConfigs, int authServerCount) throws IOException { StringBuilder connectStr = new StringBuilder(); - final int[] clientPorts = startQuorum(serverCount, connectStr, + final int[] clientPorts = startQuorum(serverCount, 0, connectStr, authConfigs, authServerCount); for (int i = 0; i < serverCount; i++) { Assert.assertTrue("waiting for server " + i + " being up", @@ -78,15 +78,73 @@ protected String startQuorum(final int serverCount, return connectStr.toString(); } - protected int[] startQuorum(final int serverCount, StringBuilder connectStr, - Map authConfigs, int authServerCount) throws IOException { + /** + * Starts the given number of quorum servers and will wait for the quorum + * formation. + * + * @param serverCount + * total server count includes participants + observers + * @param observerCount + * number of observers + * @param authConfigs + * configuration parameters for authentication + * @param authServerCount + * number of auth enabled servers + * @return client port for the respective servers + * @throws IOException + */ + protected String startQuorum(final int serverCount, int observerCount, + Map authConfigs, int authServerCount) + throws IOException { + StringBuilder connectStr = new StringBuilder(); + final int[] clientPorts = startQuorum(serverCount, observerCount, + connectStr, authConfigs, authServerCount); + for (int i = 0; i < serverCount; i++) { + Assert.assertTrue("waiting for server " + i + " being up", + ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], + ClientBase.CONNECTION_TIMEOUT)); + } + return connectStr.toString(); + } + + /** + * Starts the given number of quorum servers and won't wait for the quorum + * formation. + * + * @param serverCount + * total server count includes participants + observers + * @param observerCount + * number of observers + * @param connectStr + * connection string where clients can used for connection + * establishment + * @param authConfigs + * configuration parameters for authentication + * @param authServerCount + * number of auth enabled servers + * @return client port for the respective servers + * @throws IOException + */ + protected int[] startQuorum(final int serverCount, int observerCount, + StringBuilder connectStr, Map authConfigs, + int authServerCount) throws IOException { final int clientPorts[] = new int[serverCount]; StringBuilder sb = new StringBuilder(); + + // If there are any Observers then the Observer server details will be + // placed first in the configuration section. for (int i = 0; i < serverCount; i++) { clientPorts[i] = PortAssignment.unique(); - String server = String.format( - "server.%d=localhost:%d:%d:participant", i, - PortAssignment.unique(), PortAssignment.unique()); + String server = ""; + if (observerCount > 0 && i < observerCount) { + // add observer learner type + server = String.format("server.%d=localhost:%d:%d:observer", + i, PortAssignment.unique(), PortAssignment.unique()); + } else { + // add participant learner type + server = String.format("server.%d=localhost:%d:%d:participant", + i, PortAssignment.unique(), PortAssignment.unique()); + } sb.append(server + "\n"); connectStr.append("127.0.0.1:" + clientPorts[i]); if (i < serverCount - 1) { @@ -97,10 +155,18 @@ protected int[] startQuorum(final int serverCount, StringBuilder connectStr, // servers with authentication interfaces configured int i = 0; for (; i < authServerCount; i++) { + if (observerCount > 0 && i < observerCount) { + String obsCfgSection = quorumCfg + "\npeerType=observer"; + quorumCfg = obsCfgSection; + } startServer(authConfigs, clientPorts, quorumCfg, i); } // servers without any authentication configured for (int j = 0; j < serverCount - authServerCount; j++, i++) { + if (observerCount > 0 && i < observerCount) { + String obsCfgSection = quorumCfg + "\npeerType=observer"; + quorumCfg = obsCfgSection; + } MainThread mthread = new MainThread(i, clientPorts[i], quorumCfg); mt.add(mthread); mthread.start(); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java index 4d66beb72d6..18d1b92f5cf 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java @@ -18,16 +18,21 @@ package org.apache.zookeeper.server.quorum.auth; +import static org.junit.Assert.assertNotNull; + import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerMain; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.test.ClientBase; @@ -39,6 +44,7 @@ public class QuorumDigestAuthTest extends QuorumAuthTestBase { + private ZooKeeper zk; static { String jaasEntries = new String("" + "QuorumServer {\n" @@ -61,6 +67,9 @@ public void tearDown() throws Exception { mainThread.shutdown(); mainThread.deleteBaseDir(); } + if (zk != null) { + zk.close(); + } } @AfterClass @@ -80,14 +89,12 @@ public void testValidCredentials() throws Exception { String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); - ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, - watcher); + zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } - zk.close(); } /** @@ -103,14 +110,12 @@ public void testSaslNotRequiredWithInvalidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); - ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, - watcher); + zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } - zk.close(); } /** @@ -126,8 +131,8 @@ public void testSaslRequiredInvalidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); int serverCount = 2; - final int[] clientPorts = startQuorum(serverCount, new StringBuilder(), - authConfigs, serverCount); + final int[] clientPorts = startQuorum(serverCount, 0, + new StringBuilder(), authConfigs, serverCount); for (int i = 0; i < serverCount; i++) { boolean waitForServerUp = ClientBase.waitForServerUp( "127.0.0.1:" + clientPorts[i], QuorumPeerTestBase.TIMEOUT); @@ -218,4 +223,158 @@ protected void initializeAndRun(String[] args) // expected } } + + /** + * Test to verify that Observer server is able to join quorum. + */ + @Test(timeout = 30000) + public void testObserverWithValidCredentials() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + + // Starting auth enabled 5-node cluster. 3-Participants and 2-Observers. + int totalServerCount = 5; + int observerCount = 2; + String connectStr = startQuorum(totalServerCount, observerCount, + authConfigs, totalServerCount); + CountdownWatcher watcher = new CountdownWatcher(); + zk = new ZooKeeper(connectStr.toString(), ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + + /** + * Test to verify that non-auth enabled Observer server should be rejected + * by the auth enabled quorum servers. + */ + @Test(timeout = 30000) + public void testNonAuthEnabledObserverJoiningAuthEnabledQuorum() + throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + + // Starting auth enabled 3-node cluster. + int totalServerCount = 3; + String connectStr = startQuorum(totalServerCount, authConfigs, + totalServerCount); + + CountdownWatcher watcher = new CountdownWatcher(); + zk = new ZooKeeper(connectStr.toString(), ClientBase.CONNECTION_TIMEOUT, + watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + + // Adding a non-auth enabled Observer to the 3-node auth cluster. + String quorumCfgSection = mt.get(0).getQuorumCfgSection(); + int observerMyid = totalServerCount + 1; + StringBuilder newObsCfgSection = new StringBuilder(quorumCfgSection); + newObsCfgSection.append("\n"); + newObsCfgSection.append(String.format( + "server.%d=localhost:%d:%d:observer", observerMyid, + PortAssignment.unique(), PortAssignment.unique())); + newObsCfgSection.append("\npeerType=observer"); + newObsCfgSection.append("\n"); + int clientPort = PortAssignment.unique(); + newObsCfgSection.append("127.0.0.1:" + clientPort); + MainThread mthread = new MainThread(observerMyid, clientPort, + newObsCfgSection.toString()); + mt.add(mthread); + mthread.start(); + + boolean waitForServerUp = ClientBase.waitForServerUp( + "127.0.0.1:" + clientPort, QuorumPeerTestBase.TIMEOUT); + Assert.assertFalse( + "Non-auth enabled Observer shouldn't be able join auth-enabled quorum", + waitForServerUp); + + // quorum shouldn't be disturbed due to rejection. + zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + } + + /** + * Test to verify that server is able to reform quorum if the Leader goes + * down. + */ + @Test(timeout = 30000) + public void testRelectionWithValidCredentials() throws Exception { + Map authConfigs = new HashMap(); + authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); + authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); + authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); + + String connectStr = startQuorum(3, authConfigs, 3); + CountdownWatcher watcher = new CountdownWatcher(); + zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + watcher.reset(); + + // Shutdown Leader to trigger re-election + QuorumPeer leaderQP = getLeaderQuorumPeer(mt); + LOG.info("Shutdown Leader sid:{} to trigger quorum leader-election", + leaderQP.getId()); + shutdownQP(leaderQP); + + // Wait for quorum formation + QuorumPeer newLeaderQP = waitForLeader(); + assertNotNull("New leader must have been elected by now", newLeaderQP); + watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + zk.create("/myTestRoot", new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + } + + private QuorumPeer waitForLeader() throws InterruptedException { + int retryCnt = 0; + QuorumPeer newLeaderQP = null; + while (retryCnt < 30) { + newLeaderQP = getLeaderQuorumPeer(mt); + if (newLeaderQP != null) { + LOG.info("Number of retries:{} to findout new Leader", + retryCnt); + break; + } + retryCnt--; + Thread.sleep(500); + } + return newLeaderQP; + } + + private void shutdownQP(QuorumPeer qp) throws InterruptedException { + assertNotNull("QuorumPeer doesn't exist!", qp); + qp.shutdown(); + + int retryCnt = 30; + while (retryCnt > 0) { + if (qp.getPeerState() == ServerState.LOOKING) { + LOG.info("Number of retries:{} to change the server state to {}", + retryCnt, ServerState.LOOKING); + break; + } + Thread.sleep(500); + retryCnt--; + } + Assert.assertEquals( + "After shutdown, QuorumPeer should change its state to LOOKING", + ServerState.LOOKING, qp.getPeerState()); + } + + private QuorumPeer getLeaderQuorumPeer(List mtList) { + for (MainThread mt : mtList) { + QuorumPeer quorumPeer = mt.getQuorumPeer(); + if (null != quorumPeer + && ServerState.LEADING == quorumPeer.getPeerState()) { + return quorumPeer; + } + } + return null; + } } From b7873a59433b252c54c6c926cda711a6abf55374 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 5 Jan 2017 13:32:53 -0800 Subject: [PATCH 373/444] ZOOKEEPER-2620: Add comments to testReadOnlySnapshotDir and testReadOnlyTxnLogDir indicating that the tests will fail when run as root Author: Abraham Fine Reviewers: Michael Han Closes #142 from afine/ZOOKEEPER-2620 --- .../zookeeper/server/ZooKeeperServerMainTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java index aa94fb6802b..2ffb91ef47d 100644 --- a/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java @@ -207,6 +207,12 @@ public void commit() throws IOException { main.deleteDirs(); } + /** + * Tests that the ZooKeeper server will fail to start if the + * snapshot directory is read only. + * + * This test will fail if it is executed as root user. + */ @Test(timeout = 30000) public void testReadOnlySnapshotDir() throws Exception { ClientBase.setupTestEnv(); @@ -241,6 +247,12 @@ public void testReadOnlySnapshotDir() throws Exception { main.deleteDirs(); } + /** + * Tests that the ZooKeeper server will fail to start if the + * transaction log directory is read only. + * + * This test will fail if it is executed as root user. + */ @Test(timeout = 30000) public void testReadOnlyTxnLogDir() throws Exception { ClientBase.setupTestEnv(); From ff6a2be0851511a686ddc190d09ae9eb51e9f0a6 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Fri, 6 Jan 2017 17:41:31 +0530 Subject: [PATCH 374/444] ZOOKEEPER-2656: Fix ServerConfigTest#testValidArguments test case. ServerConfig.getDataDir returns type String in branch-3.4 but return type File in branch-3.5 and master. So we need to deal with this difference accordingly in our test. This PR is intended to be merged in master, branch-3.5, and branch-3.4. rakeshadr PTAL Author: Michael Han Reviewers: Edward Ribeiro , Rakesh Radhakrishnan Closes #140 from hanm/ZOOKEEPER-2565 (cherry picked from commit 1d38d30cfd1aa47f22451f4df53ae2363ea20a82) Signed-off-by: Rakesh Radhakrishnan --- .../org/apache/zookeeper/ServerConfigTest.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/ServerConfigTest.java b/src/java/test/org/apache/zookeeper/ServerConfigTest.java index e2d0da8307c..27faa745b6f 100644 --- a/src/java/test/org/apache/zookeeper/ServerConfigTest.java +++ b/src/java/test/org/apache/zookeeper/ServerConfigTest.java @@ -23,6 +23,8 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.io.File; @@ -47,7 +49,7 @@ public void testValidArguments() { serverConfig.parse(args); assertEquals(2181, serverConfig.getClientPortAddress().getPort()); - assertEquals(new File("/data/dir"), serverConfig.getDataDir()); + assertTrue(checkEquality("/data/dir", serverConfig.getDataDir())); assertEquals(60000, serverConfig.getTickTime()); assertEquals(10000, serverConfig.getMaxClientCnxns()); } @@ -57,4 +59,16 @@ public void testTooManyArguments() { String[] args = {"2181", "/data/dir", "60000", "10000", "9999"}; serverConfig.parse(args); } -} + + boolean checkEquality(String a, String b) { + assertNotNull(a); + assertNotNull(b); + return a.equals(b); + } + + boolean checkEquality(String a, File b) { + assertNotNull(a); + assertNotNull(b); + return new File(a).equals(b); + } +} \ No newline at end of file From cded802708fac417369affbd25bf9ad2016a904d Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 10 Jan 2017 21:27:38 -0800 Subject: [PATCH 375/444] ZOOKEEPER-2651: Missing src/pom.template in release Author: Rakesh Radhakrishnan Reviewers: Edward Ribeiro , Michael Han Closes #145 from rakeshadr/ZOOKEEPER-2651 (cherry picked from commit 5f60374d060c18ccad322c7f18883284dbac0fed) Signed-off-by: Michael Han --- build.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.xml b/build.xml index facf8c34943..aec3aa5f285 100644 --- a/build.xml +++ b/build.xml @@ -763,6 +763,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + @@ -881,6 +882,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + From 3b026478e552875b876c99b52384c55f8281ae50 Mon Sep 17 00:00:00 2001 From: Raghavendra Prabhu Date: Sun, 22 Jan 2017 16:42:36 -0800 Subject: [PATCH 376/444] ZOOKEEPER-2633: contrib/zkfuse build fix with gcc 6.2. The build fails in two places: https://gist.github.com/ronin13/3e08569dd6c69bf2ad92fa39fa85f7ee One is boost related, and other is due to false being passed where NULL is required. JIRA: https://issues.apache.org/jira/browse/ZOOKEEPER-2633 Author: Raghavendra Prabhu Author: Raghavendra Prabhu Reviewers: Michael Han Closes #110 from ronin13/c++-build-fix (cherry picked from commit 3bd4ed98021b033acdd5eae583d84e86fc3430e4) Signed-off-by: Michael Han --- src/contrib/zkfuse/src/event.h | 2 +- src/contrib/zkfuse/src/zkadapter.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/contrib/zkfuse/src/event.h b/src/contrib/zkfuse/src/event.h index 0506932f7a3..936ecc60453 100644 --- a/src/contrib/zkfuse/src/event.h +++ b/src/contrib/zkfuse/src/event.h @@ -213,7 +213,7 @@ class GenericEvent { /** * The event represented as abstract wrapper. */ - shared_ptr m_eventWrapper; + boost::shared_ptr m_eventWrapper; }; diff --git a/src/contrib/zkfuse/src/zkadapter.cc b/src/contrib/zkfuse/src/zkadapter.cc index 7dfb9078fc3..7f02fa33b72 100644 --- a/src/contrib/zkfuse/src/zkadapter.cc +++ b/src/contrib/zkfuse/src/zkadapter.cc @@ -673,7 +673,7 @@ ZooKeeperAdapter::deleteNode(const string &path, LOG_WARN( LOG, "Error %d for %s", rc, path.c_str() ); //get all children and delete them recursively... vector nodeList; - getNodeChildren( nodeList, path, false ); + getNodeChildren( nodeList, path, NULL ); for (vector::const_iterator i = nodeList.begin(); i != nodeList.end(); ++i) { From e93d5b27f1cc2c66df39b45be321e90c798bdc9b Mon Sep 17 00:00:00 2001 From: Abhishek Rai Date: Sun, 22 Jan 2017 17:13:37 -0800 Subject: [PATCH 377/444] ZOOKEEPER-2574: PurgeTxnLog can inadvertently delete required txn log files. This fix includes patch from Ed Rowe for ZOOKEEPER-2420, which is the same issue as ZOOKEEPER-2574. Author: Abhishek Rai Reviewers: Rakesh Radhakrishnan , Arshad Mohammad , Abraham Fine , Allan Lyu , Michael Han Closes #111 from abhishekrai/ZOOKEEPER-2574 --- docs/zookeeperAdmin.html | 31 ++- .../apache/zookeeper/server/PurgeTxnLog.java | 51 ++++- .../server/persistence/FileSnap.java | 12 +- .../server/persistence/FileTxnSnapLog.java | 8 +- .../apache/zookeeper/server/PurgeTxnTest.java | 192 +++++++++++++++--- 5 files changed, 245 insertions(+), 49 deletions(-) diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html index df1599c4350..affe107613a 100644 --- a/docs/zookeeperAdmin.html +++ b/docs/zookeeperAdmin.html @@ -934,10 +934,14 @@

            Ongoing Data Directory Cleanup

            of the znodes stored by a particular serving ensemble. These are the snapshot and transactional log files. As changes are made to the znodes these changes are appended to a - transaction log, occasionally, when a log grows large, a + transaction log. Occasionally, when a log grows large, a snapshot of the current state of all znodes will be written - to the filesystem. This snapshot supercedes all previous - logs. + to the filesystem and a new transaction log file is created + for future transactions. During snapshotting, ZooKeeper may + continue appending incoming transactions to the old log file. + Therefore, some transactions which are newer than a snapshot + may be found in the last transaction log preceding the + snapshot.

            A ZooKeeper server will not remove old snapshots and log files when using the default @@ -1147,8 +1151,10 @@

            Advanced Configuration

            (Java system property: zookeeper.snapCount)

            ZooKeeper logs transactions to a transaction log. After snapCount transactions are written to a log - file a snapshot is started and a new transaction log - file is created. The default snapCount is + file a snapshot is started. It also influences rollover + of the current transaction log to a new file. However, + the creation of a new snapshot and rollover of transaction + log proceed independently. The default snapCount is 100,000.

            @@ -1945,8 +1951,11 @@

            The Log Directory

            The Log Directory contains the ZooKeeper transaction logs. Before any update takes place, ZooKeeper ensures that the transaction that represents the update is written to non-volatile storage. A new - log file is started each time a snapshot is begun. The log file's - suffix is the first zxid written to that log.

            + log file is started when the number of transactions written to the + current log file reaches a (variable) threshold. The threshold is + computed using the same parameter which influences the frequency of + snapshotting (see snapCount above). The log file's suffix is the first + zxid written to that log.

            File Management

            The format of snapshot and log files does not change between @@ -1961,8 +1970,12 @@

            File Management

            The ZooKeeper server creates snapshot and log files, but never deletes them. The retention policy of the data and log files is implemented outside of the ZooKeeper server. The - server itself only needs the latest complete fuzzy snapshot - and the log files from the start of that snapshot. See the + server itself only needs the latest complete fuzzy snapshot, all log + files following it, and the last log file preceding it. The latter + requirement is necessary to include updates which happened after this + snapshot was started but went into the existing log file at that time. + This is possible because snapshotting and rolling over of logs + proceed somewhat independently in Zookeeper. See the maintenance section in this document for more details on setting a retention policy and maintenance of ZooKeeper storage. diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java index 25b949aaf7e..d4effafb036 100644 --- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java @@ -24,10 +24,14 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * this class is used to clean up the @@ -38,6 +42,7 @@ * and the corresponding logs. */ public class PurgeTxnLog { + private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnLog.class); private static final String COUNT_ERR_MSG = "count should be greater than or equal to 3"; @@ -72,18 +77,43 @@ public static void purge(File dataDir, File snapDir, int num) throws IOException FileTxnSnapLog txnLog = new FileTxnSnapLog(dataDir, snapDir); List snaps = txnLog.findNRecentSnapshots(num); - retainNRecentSnapshots(txnLog, snaps); + int numSnaps = snaps.size(); + if (numSnaps > 0) { + purgeOlderSnapshots(txnLog, snaps.get(numSnaps - 1)); + } } // VisibleForTesting - static void retainNRecentSnapshots(FileTxnSnapLog txnLog, List snaps) { - // found any valid recent snapshots? - if (snaps.size() == 0) - return; - File snapShot = snaps.get(snaps.size() -1); + static void purgeOlderSnapshots(FileTxnSnapLog txnLog, File snapShot) { final long leastZxidToBeRetain = Util.getZxidFromName( snapShot.getName(), PREFIX_SNAPSHOT); + /** + * We delete all files with a zxid in their name that is less than leastZxidToBeRetain. + * This rule applies to both snapshot files as well as log files, with the following + * exception for log files. + * + * A log file with zxid less than X may contain transactions with zxid larger than X. More + * precisely, a log file named log.(X-a) may contain transactions newer than snapshot.X if + * there are no other log files with starting zxid in the interval (X-a, X]. Assuming the + * latter condition is true, log.(X-a) must be retained to ensure that snapshot.X is + * recoverable. In fact, this log file may very well extend beyond snapshot.X to newer + * snapshot files if these newer snapshots were not accompanied by log rollover (possible in + * the learner state machine at the time of this writing). We can make more precise + * determination of whether log.(leastZxidToBeRetain-a) for the smallest 'a' is actually + * needed or not (e.g. not needed if there's a log file named log.(leastZxidToBeRetain+1)), + * but the complexity quickly adds up with gains only in uncommon scenarios. It's safe and + * simple to just preserve log.(leastZxidToBeRetain-a) for the smallest 'a' to ensure + * recoverability of all snapshots being retained. We determine that log file here by + * calling txnLog.getSnapshotLogs(). + */ + final Set retainedTxnLogs = new HashSet(); + retainedTxnLogs.addAll(Arrays.asList(txnLog.getSnapshotLogs(leastZxidToBeRetain))); + + /** + * Finds all candidates for deletion, which are files with a zxid in their name that is less + * than leastZxidToBeRetain. There's an exception to this rule, as noted above. + */ class MyFileFilter implements FileFilter{ private final String prefix; MyFileFilter(String prefix){ @@ -92,6 +122,9 @@ class MyFileFilter implements FileFilter{ public boolean accept(File f){ if(!f.getName().startsWith(prefix + ".")) return false; + if (retainedTxnLogs.contains(f)) { + return false; + } long fZxid = Util.getZxidFromName(f.getName(), prefix); if (fZxid >= leastZxidToBeRetain) { return false; @@ -108,9 +141,11 @@ public boolean accept(File f){ // remove the old files for(File f: files) { - System.out.println("Removing file: "+ + final String msg = "Removing file: "+ DateFormat.getDateTimeInstance().format(f.lastModified())+ - "\t"+f.getPath()); + "\t"+f.getPath(); + LOG.info(msg); + System.out.println(msg); if(!f.delete()){ System.err.println("Failed to remove "+f.getPath()); } diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java b/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java index 8f57338417e..cf464a12742 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileSnap.java @@ -177,19 +177,21 @@ private List findNValidSnapshots(int n) throws IOException { /** * find the last n snapshots. this does not have * any checks if the snapshot might be valid or not - * @param the number of most recent snapshots + * @param the number of most recent snapshots * @return the last n snapshots * @throws IOException */ public List findNRecentSnapshots(int n) throws IOException { List files = Util.sortDataDir(snapDir.listFiles(), "snapshot", false); - int i = 0; + int count = 0; List list = new ArrayList(); for (File f: files) { - if (i==n) + if (count == n) break; - i++; - list.add(f); + if (Util.getZxidFromName(f.getName(), "snapshot") != -1) { + count++; + list.add(f); + } } return list; } diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java index 7841efad581..24614c02240 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java @@ -302,9 +302,11 @@ public List findNRecentSnapshots(int n) throws IOException { } /** - * get the snapshot logs that are greater than - * the given zxid - * @param zxid the zxid that contains logs greater than + * get the snapshot logs which may contain transactions newer than the given zxid. + * This includes logs with starting zxid greater than given zxid, as well as the + * newest transaction log with starting zxid less than given zxid. The latter log + * file may contain transactions beyond given zxid. + * @param zxid the zxid that contains logs greater than * zxid * @return */ diff --git a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java index 3e00e0091ed..fe321c8af7b 100644 --- a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java +++ b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java @@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; @@ -42,6 +43,7 @@ import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.test.ClientBase; import org.junit.After; import org.junit.Assert; @@ -174,10 +176,17 @@ public void testFindNRecentSnapshots() throws Exception { int nRecentSnap = 4; // n recent snap shots int nRecentCount = 30; int offset = 0; + tmpDir = ClientBase.createTmpDir(); File version2 = new File(tmpDir.toString(), "version-2"); Assert.assertTrue("Failed to create version_2 dir:" + version2.toString(), version2.mkdir()); + + // Test that with no snaps, findNRecentSnapshots returns empty list + FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); + List foundSnaps = txnLog.findNRecentSnapshots(1); + assertEquals(0, foundSnaps.size()); + List expectedNRecentSnapFiles = new ArrayList(); int counter = offset + (2 * nRecentCount); for (int i = 0; i < nRecentCount; i++) { @@ -196,14 +205,25 @@ public void testFindNRecentSnapshots() throws Exception { } } - FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); + // Test that when we ask for recent snaps we get the number we asked for and + // the files we expected List nRecentSnapFiles = txnLog.findNRecentSnapshots(nRecentSnap); - txnLog.close(); Assert.assertEquals("exactly 4 snapshots ", 4, nRecentSnapFiles.size()); expectedNRecentSnapFiles.removeAll(nRecentSnapFiles); Assert.assertEquals("Didn't get the recent snap files", 0, expectedNRecentSnapFiles.size()); + + // Test that when asking for more snaps than we created, we still only get snaps + // not logs or anything else (per ZOOKEEPER-2420) + nRecentSnapFiles = txnLog.findNRecentSnapshots(nRecentCount + 5); + assertEquals(nRecentCount, nRecentSnapFiles.size()); + for (File f: nRecentSnapFiles) { + Assert.assertTrue("findNRecentSnapshots() returned a non-snapshot: " + f.getPath(), + (Util.getZxidFromName(f.getName(), "snapshot") != -1)); + } + + txnLog.close(); } /** @@ -227,14 +247,21 @@ public void testSnapFilesGreaterThanToRetain() throws Exception { List logs = new ArrayList(); List snapsAboveRecentFiles = new ArrayList(); List logsAboveRecentFiles = new ArrayList(); - createDataDirFiles(offset, fileToPurgeCount, version2, snapsToPurge, + createDataDirFiles(offset, fileToPurgeCount, false, version2, snapsToPurge, logsToPurge); - createDataDirFiles(offset, nRecentCount, version2, snaps, logs); - createDataDirFiles(offset, fileAboveRecentCount, version2, + createDataDirFiles(offset, nRecentCount, false, version2, snaps, logs); + logs.add(logsToPurge.remove(0)); // log that precedes first retained snapshot is also retained + createDataDirFiles(offset, fileAboveRecentCount, false, version2, snapsAboveRecentFiles, logsAboveRecentFiles); + /** + * The newest log file preceding the oldest retained snapshot is not removed as it may + * contain transactions newer than the oldest snapshot. + */ + logsToPurge.remove(0); + FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); - PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps); + PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1)); txnLog.close(); verifyFilesAfterPurge(snapsToPurge, false); verifyFilesAfterPurge(logsToPurge, false); @@ -245,11 +272,24 @@ public void testSnapFilesGreaterThanToRetain() throws Exception { } /** - * Tests purge where the data directory contains snap files equals to the + * Tests purge where the data directory contains snap files and log files equals to the * number of files to be retained */ @Test public void testSnapFilesEqualsToRetain() throws Exception { + internalTestSnapFilesEqualsToRetain(false); + } + + /** + * Tests purge where the data directory contains snap files equals to the + * number of files to be retained, and a log file that precedes the earliest snapshot + */ + @Test + public void testSnapFilesEqualsToRetainWithPrecedingLog() throws Exception { + internalTestSnapFilesEqualsToRetain(true); + } + + public void internalTestSnapFilesEqualsToRetain(boolean testWithPrecedingLogFile) throws Exception { int nRecentCount = 3; AtomicInteger offset = new AtomicInteger(0); tmpDir = ClientBase.createTmpDir(); @@ -258,10 +298,10 @@ public void testSnapFilesEqualsToRetain() throws Exception { version2.mkdir()); List snaps = new ArrayList(); List logs = new ArrayList(); - createDataDirFiles(offset, nRecentCount, version2, snaps, logs); + createDataDirFiles(offset, nRecentCount, testWithPrecedingLogFile, version2, snaps, logs); FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); - PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps); + PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1)); txnLog.close(); verifyFilesAfterPurge(snaps, true); verifyFilesAfterPurge(logs, true); @@ -284,12 +324,19 @@ public void testSnapFilesLessThanToRetain() throws Exception { List logsToPurge = new ArrayList(); List snaps = new ArrayList(); List logs = new ArrayList(); - createDataDirFiles(offset, fileToPurgeCount, version2, snapsToPurge, + createDataDirFiles(offset, fileToPurgeCount, false, version2, snapsToPurge, logsToPurge); - createDataDirFiles(offset, nRecentCount, version2, snaps, logs); + createDataDirFiles(offset, nRecentCount, false, version2, snaps, logs); + logs.add(logsToPurge.remove(0)); // log that precedes first retained snapshot is also retained + + /** + * The newest log file preceding the oldest retained snapshot is not removed as it may + * contain transactions newer than the oldest snapshot. + */ + logsToPurge.remove(0); FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); - PurgeTxnLog.retainNRecentSnapshots(txnLog, snaps); + PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1)); txnLog.close(); verifyFilesAfterPurge(snapsToPurge, false); verifyFilesAfterPurge(logsToPurge, false); @@ -329,15 +376,16 @@ public void testPurgeTxnLogWithDataDir() snapFile.createNewFile(); } - int numberOfFilesToKeep = 10; + int numberOfSnapFilesToKeep = 10; // scenario where four parameter are passed String[] args = new String[] { dataLogDir.getAbsolutePath(), dataDir.getAbsolutePath(), "-n", - Integer.toString(numberOfFilesToKeep) }; + Integer.toString(numberOfSnapFilesToKeep) }; PurgeTxnLog.main(args); - assertEquals(numberOfFilesToKeep, dataDirVersion2.listFiles().length); - assertEquals(numberOfFilesToKeep, dataLogDirVersion2.listFiles().length); + assertEquals(numberOfSnapFilesToKeep, dataDirVersion2.listFiles().length); + // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept + assertEquals(numberOfSnapFilesToKeep, dataLogDirVersion2.listFiles().length); ClientBase.recursiveDelete(tmpDir); } @@ -373,28 +421,121 @@ public void testPurgeTxnLogWithoutDataDir() snapFile.createNewFile(); } - int numberOfFilesToKeep = 10; + int numberOfSnapFilesToKeep = 10; // scenario where only three parameter are passed String[] args = new String[] { dataLogDir.getAbsolutePath(), "-n", - Integer.toString(numberOfFilesToKeep) }; + Integer.toString(numberOfSnapFilesToKeep) }; PurgeTxnLog.main(args); - assertEquals(numberOfFilesToKeep + numberOfFilesToKeep, + assertEquals(numberOfSnapFilesToKeep * 2, // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept dataLogDirVersion2.listFiles().length); ClientBase.recursiveDelete(tmpDir); } - private void createDataDirFiles(AtomicInteger offset, int limit, + /** + * Verifies that purge does not delete any log files which started before the oldest retained + * snapshot but which might extend beyond it. + * @throws Exception an exception might be thrown here + */ + @Test + public void testPurgeDoesNotDeleteOverlappingLogFile() throws Exception { + // Setting used for snapRetainCount in this test. + final int SNAP_RETAIN_COUNT = 3; + // Number of znodes this test creates in each snapshot. + final int NUM_ZNODES_PER_SNAPSHOT = 100; + /** + * Set a sufficiently high snapCount to ensure that we don't rollover the log. Normally, + * the default value (100K at time of this writing) would ensure this, but we make that + * dependence explicit here to make the test future-proof. Not rolling over the log is + * important for this test since we are testing retention of the one and only log file which + * predates each retained snapshot. + */ + SyncRequestProcessor.setSnapCount(SNAP_RETAIN_COUNT * NUM_ZNODES_PER_SNAPSHOT * 10); + + // Create Zookeeper and connect to it. + tmpDir = ClientBase.createTmpDir(); + ClientBase.setupTestEnv(); + ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); + ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + Assert.assertTrue("waiting for server being up ", + ClientBase.waitForServerUp(HOSTPORT,CONNECTION_TIMEOUT)); + ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); + + // Unique identifier for each znode that we create. + int unique = 0; + try { + /** + * Create some znodes and take a snapshot. Repeat this until we have SNAP_RETAIN_COUNT + * snapshots. Do not rollover the log. + */ + for (int snapshotCount = 0; snapshotCount < SNAP_RETAIN_COUNT; snapshotCount++) { + for (int i = 0; i< 100; i++, unique++) { + zk.create("/snap-" + unique, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + zks.takeSnapshot(); + } + // Create some additional znodes without taking a snapshot afterwards. + for (int i = 0; i< 100; i++, unique++) { + zk.create("/snap-" + unique, new byte[0], Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + } + } finally { + zk.close(); + } + + // Shutdown Zookeeper. + f.shutdown(); + zks.getTxnLogFactory().close(); + zks.shutdown(); + Assert.assertTrue("waiting for server to shutdown", + ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT)); + + // Purge snapshot and log files. + PurgeTxnLog.purge(tmpDir, tmpDir, SNAP_RETAIN_COUNT); + + // Initialize Zookeeper again from the same dataDir. + zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); + f = ServerCnxnFactory.createFactory(PORT, -1); + f.startup(zks); + zk = ClientBase.createZKClient(HOSTPORT); + + /** + * Verify that the last znode that was created above exists. This znode's creation was + * captured by the transaction log which was created before any of the above + * SNAP_RETAIN_COUNT snapshots were created, but it's not captured in any of these + * snapshots. So for it it exist, the (only) existing log file should not have been purged. + */ + final String lastZnode = "/snap-" + (unique - 1); + final Stat stat = zk.exists(lastZnode, false); + Assert.assertNotNull("Last znode does not exist: " + lastZnode, stat); + + // Shutdown for the last time. + f.shutdown(); + zks.getTxnLogFactory().close(); + zks.shutdown(); + } + + private File createDataDirLogFile(File version_2, int Zxid) throws IOException { + File logFile = new File(version_2 + "/log." + Long.toHexString(Zxid)); + Assert.assertTrue("Failed to create log File:" + logFile.toString(), + logFile.createNewFile()); + return logFile; + } + + private void createDataDirFiles(AtomicInteger offset, int limit, boolean createPrecedingLogFile, File version_2, List snaps, List logs) throws IOException { int counter = offset.get() + (2 * limit); + if (createPrecedingLogFile) { + counter++; + } offset.set(counter); for (int i = 0; i < limit; i++) { // simulate log file - File logFile = new File(version_2 + "/log." + Long.toHexString(--counter)); - Assert.assertTrue("Failed to create log File:" + logFile.toString(), - logFile.createNewFile()); - logs.add(logFile); + logs.add(createDataDirLogFile(version_2, --counter)); // simulate snapshot file File snapFile = new File(version_2 + "/snapshot." + Long.toHexString(--counter)); @@ -402,6 +543,9 @@ private void createDataDirFiles(AtomicInteger offset, int limit, snapFile.createNewFile()); snaps.add(snapFile); } + if (createPrecedingLogFile) { + logs.add(createDataDirLogFile(version_2, --counter)); + } } private void verifyFilesAfterPurge(List logs, boolean exists) { From 88e4d9dcda14e83445c4198aa9a3096d202f1cf7 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 23 Jan 2017 12:21:44 +0530 Subject: [PATCH 378/444] ZOOKEEPER-2671: Fix compilation error in branch-3.4 Creating the PR on behalf of Arshad. Author: Rakesh Radhakrishnan Reviewers: Arshad Mohammad , Edward Ribeiro Closes #153 from rakeshadr/ZK-2671 --- .../org/apache/zookeeper/test/ClientBase.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index fdcfac49c6e..6037ff2f3ca 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -664,4 +664,32 @@ public static String join(String separator, Object[] parts) { } return sb.toString(); } + + public static ZooKeeper createZKClient(String cxnString) throws Exception { + return createZKClient(cxnString, CONNECTION_TIMEOUT); + } + + /** + * Returns ZooKeeper client after connecting to ZooKeeper Server. Session + * timeout is {@link #CONNECTION_TIMEOUT} + * + * @param cxnString + * connection string in the form of host:port + * @param sessionTimeout + * @throws IOException + * in cases of network failure + */ + public static ZooKeeper createZKClient(String cxnString, int sessionTimeout) throws IOException { + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(cxnString, sessionTimeout, watcher); + try { + watcher.waitForConnected(CONNECTION_TIMEOUT); + } catch (InterruptedException e) { + Assert.fail("ZooKeeper client can not connect to " + cxnString); + } + catch (TimeoutException e) { + Assert.fail("ZooKeeper client can not connect to " + cxnString); + } + return zk; + } } From 3271f3d03d75c675ec1e466434eee0834cb9841a Mon Sep 17 00:00:00 2001 From: tedyu Date: Fri, 13 Jan 2017 21:32:15 -0800 Subject: [PATCH 379/444] ZOOKEEPER-2664: ClientPortBindTest#testBindByAddress may fail due to "No such device" exception The following stack trace was observed intermittently: ``` Stacktrace java.net.SocketException: No such device at java.net.NetworkInterface.isLoopback0(Native Method) at java.net.NetworkInterface.isLoopback(NetworkInterface.java:390) at org.apache.zookeeper.test.ClientPortBindTest.testBindByAddress(ClientPortBindTest.java:61) at org.apache.zookeeper.JUnit4ZKTestRunner$LoggedInvokeMethod.evaluate(JUnit4ZKTestRunner.java:52) Standard Output ``` Proposed fix is to catch exception from isLoopback() call and skip the test in that case. Author: tedyu Reviewers: Edward Ribeiro , Michael Han Closes #149 from tedyu/master --- .../apache/zookeeper/test/ClientPortBindTest.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java b/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java index 22f9dde8702..d6cb1e22f1d 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java @@ -24,6 +24,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; +import java.net.SocketException; import java.util.Enumeration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -58,15 +59,19 @@ public void testBindByAddress() throws Exception { // if we have a loopback and it has an address use it while(intfs.hasMoreElements()) { NetworkInterface i = intfs.nextElement(); - if (i.isLoopback()) { - Enumeration addrs = i.getInetAddresses(); - while (addrs.hasMoreElements()) { + try { + if (i.isLoopback()) { + Enumeration addrs = i.getInetAddresses(); + while (addrs.hasMoreElements()) { InetAddress a = addrs.nextElement(); if(a.isLoopbackAddress()) { - bindAddress = a.getHostAddress(); - break; + bindAddress = a.getHostAddress(); + break; } + } } + } catch (SocketException se) { + LOG.warn("Couldn't find loopback interface: " + se.getMessage()); } } if (bindAddress == null) { From d6bbfd76d24c044073764c5d074a9198c69fafab Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Wed, 25 Jan 2017 17:27:25 +0530 Subject: [PATCH 380/444] ZOOKEEPER-2573: Modify Info.REVISION to adapt git repo rakeshadr Hi, I have created this PR. The commit can be cherry picked on master too. In fact, I guess when you merge this then #137 will be automatically closed. I tested quickly and was able to cherry-pick this commit on branch-3.4 too, so you may want to give it a try. :smiley: Author: Edward Ribeiro Author: Edward Ribeiro Reviewers: Mohammad Arshad , Michael Han Closes #155 from eribeiro/ZOOKEEPER-2573-3.5 (cherry picked from commit 41da3c8e3c39f81aa0f667199c5f4eb3d5a28adc) Signed-off-by: Rakesh Radhakrishnan --- build.xml | 4 +-- .../main/org/apache/zookeeper/Version.java | 13 ++++++++- .../apache/zookeeper/version/util/VerGen.java | 29 ++++++++++--------- .../test/org/apache/zookeeper/VerGenTest.java | 2 +- src/lastRevision.bat | 5 ++-- src/lastRevision.sh | 2 +- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/build.xml b/build.xml index aec3aa5f285..864c5a40065 100644 --- a/build.xml +++ b/build.xml @@ -309,7 +309,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> includes="org/apache/zookeeper/version/util/**" debug="on" /> - + @@ -324,7 +324,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/src/java/main/org/apache/zookeeper/Version.java b/src/java/main/org/apache/zookeeper/Version.java index 46573717b0e..1f5cf1aa2df 100644 --- a/src/java/main/org/apache/zookeeper/Version.java +++ b/src/java/main/org/apache/zookeeper/Version.java @@ -20,10 +20,21 @@ public class Version implements org.apache.zookeeper.version.Info { + /* + * Since the SVN to Git port this field doesn't return the revision anymore + * TODO: remove this method and associated field declaration in VerGen + * @see {@link #getHashRevision()} + * @return the default value -1 + */ + @Deprecated public static int getRevision() { return REVISION; } + public static String getRevisionHash() { + return REVISION_HASH; + } + public static String getBuildDate() { return BUILD_DATE; } @@ -34,7 +45,7 @@ public static String getVersion() { } public static String getVersionRevision() { - return getVersion() + "-" + getRevision(); + return getVersion() + "-" + getRevisionHash(); } public static String getFullVersion() { diff --git a/src/java/main/org/apache/zookeeper/version/util/VerGen.java b/src/java/main/org/apache/zookeeper/version/util/VerGen.java index d4cbeb64eaa..93a07106856 100644 --- a/src/java/main/org/apache/zookeeper/version/util/VerGen.java +++ b/src/java/main/org/apache/zookeeper/version/util/VerGen.java @@ -34,7 +34,7 @@ static void printUsage() { System.exit(1); } - public static void generateFile(File outputDir, Version version, int rev, String buildDate) + public static void generateFile(File outputDir, Version version, String rev, String buildDate) { String path = PACKAGE_NAME.replaceAll("\\.", "/"); File pkgdir = new File(outputDir, path); @@ -76,18 +76,19 @@ public static void generateFile(File outputDir, Version version, int rev, String w.write("\n"); w.write("package " + PACKAGE_NAME + ";\n\n"); w.write("public interface " + TYPE_NAME + " {\n"); - w.write(" public static final int MAJOR=" + version.maj + ";\n"); - w.write(" public static final int MINOR=" + version.min + ";\n"); - w.write(" public static final int MICRO=" + version.micro + ";\n"); - w.write(" public static final String QUALIFIER=" + w.write(" int MAJOR=" + version.maj + ";\n"); + w.write(" int MINOR=" + version.min + ";\n"); + w.write(" int MICRO=" + version.micro + ";\n"); + w.write(" String QUALIFIER=" + (version.qualifier == null ? null : "\"" + version.qualifier + "\"") + ";\n"); - if (rev < 0) { + if (rev.equals("-1")) { System.out.println("Unknown REVISION number, using " + rev); } - w.write(" public static final int REVISION=" + rev + ";\n"); - w.write(" public static final String BUILD_DATE=\"" + buildDate + w.write(" int REVISION=-1; //TODO: remove as related to SVN VCS\n"); + w.write(" String REVISION_HASH=\"" + rev + "\";\n"); + w.write(" String BUILD_DATE=\"" + buildDate + "\";\n"); w.write("}\n"); } catch (IOException e) { @@ -146,7 +147,7 @@ public static Version parseVersionString(String input) { *

          • min - minor version number *
          • micro - minor minor version number *
          • qualifier - optional qualifier (dash followed by qualifier text) - *
          • rev - current SVN revision number + *
          • rev - current Git revision number *
          • buildDate - date the build * */ @@ -160,11 +161,11 @@ public static void main(String[] args) { "Invalid version number format, must be \"x.y.z(-.*)?\""); System.exit(1); } - int rev; - try { - rev = Integer.parseInt(args[1]); - } catch (NumberFormatException e) { - rev = -1; + String rev = args[1]; + if (rev == null || rev.trim().isEmpty()) { + rev = "-1"; + } else { + rev = rev.trim(); } generateFile(new File("."), version, rev, args[2]); } catch (NumberFormatException e) { diff --git a/src/java/test/org/apache/zookeeper/VerGenTest.java b/src/java/test/org/apache/zookeeper/VerGenTest.java index 13d3abdd806..607edbb538c 100644 --- a/src/java/test/org/apache/zookeeper/VerGenTest.java +++ b/src/java/test/org/apache/zookeeper/VerGenTest.java @@ -72,7 +72,7 @@ public void testParser() { public void testGenFile() throws Exception { VerGen.Version v = VerGen.parseVersionString(input); File outputDir = ClientBase.createTmpDir(); - VerGen.generateFile(outputDir, v, 1, "Nov1"); + VerGen.generateFile(outputDir, v, "1", "Nov1"); ClientBase.recursiveDelete(outputDir); } } diff --git a/src/lastRevision.bat b/src/lastRevision.bat index e31a6b96c34..68999471db6 100644 --- a/src/lastRevision.bat +++ b/src/lastRevision.bat @@ -16,8 +16,7 @@ rem See the License for the specific language governing permissions and rem limitations under the License. rem Find the current revision, store it in a file, for DOS -svn info | findstr Revision > %1 -For /F "tokens=1,2 delims= " %%a In (%1) Do ( - echo lastRevision=%%b> %1 +for /f "delims=" %%i in ('git rev-parse HEAD') do set rev=%%i + echo lastRevision=%rev% > %1 ) diff --git a/src/lastRevision.sh b/src/lastRevision.sh index a462990e742..0690c7da916 100755 --- a/src/lastRevision.sh +++ b/src/lastRevision.sh @@ -16,6 +16,6 @@ # Find the current revision, store it in a file FILE=$1 -LASTREV=`svn info | grep '^Revision' | sed -e 's/Revision: *//'` +LASTREV=`git rev-parse HEAD` echo "lastRevision=${LASTREV}" > $FILE From f0ef24a5e5df110ae569c1d3085e3cea8b454d9c Mon Sep 17 00:00:00 2001 From: Flavio Junqueira Date: Sat, 28 Jan 2017 11:13:53 +0530 Subject: [PATCH 381/444] ZOOKEEPER-2622: ZooTrace.logQuorumPacket does nothing Author: fpj Reviewers: Edward Ribeiro Closes #95 from fpj/ZK-2622 (cherry picked from commit edf75b5e31f0d9e2fbfadbd95bae9d1d6c4737f6) Signed-off-by: arshad --- .../main/org/apache/zookeeper/server/ZooTrace.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/ZooTrace.java b/src/java/main/org/apache/zookeeper/server/ZooTrace.java index ac14fe22c8c..946a4bf73b4 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooTrace.java +++ b/src/java/main/org/apache/zookeeper/server/ZooTrace.java @@ -20,7 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.server.quorum.QuorumPacket; /** @@ -75,12 +75,10 @@ public static void logTraceMessage(Logger log, long mask, String msg) { static public void logQuorumPacket(Logger log, long mask, char direction, QuorumPacket qp) { - return; - - // if (isTraceEnabled(log, mask)) { - // logTraceMessage(LOG, mask, direction + " " - // + FollowerHandler.packetToString(qp)); - // } + if (isTraceEnabled(log, mask)) { + logTraceMessage(log, mask, direction + + " " + LearnerHandler.packetToString(qp)); + } } static public void logRequest(Logger log, long mask, From 4b9fe5ea71d73ba26ca722e3919b8d0afe84ab86 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Wed, 1 Feb 2017 03:23:42 +0530 Subject: [PATCH 382/444] ZOOKEEPER-2044: CancelledKeyException in zookeeper branch-3.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch from Germán Blanco, test case from Flavio. Author: Michael Han Reviewers: Rakesh Radhakrishnan Closes #156 from hanm/ZOOKEEPER-2044 and squashes the following commits: f1f9790 [Michael Han] Use createZKClient to set session timeout value directly. 831e560 [Michael Han] Make test run faster by reducing client cnx timeouts; otherwise client will wait for server until session timeout because the channel has been closed on server side. 736091a [Michael Han] Address Rakesh's code review comments. a220d95 [Michael Han] ZOOKEEPER-2044:CancelledKeyException in zookeeper 3.4.5. Patch from Germán Blanco, test case from Flavio. --- .../zookeeper/server/NIOServerCnxn.java | 71 +++++++++++-------- .../zookeeper/server/NIOServerCnxnTest.java | 39 +++++++++- 2 files changed, 78 insertions(+), 32 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index 746102dda3d..4ea7fb2736d 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -62,7 +62,7 @@ public class NIOServerCnxn extends ServerCnxn { final SocketChannel sock; - private final SelectionKey sk; + protected final SelectionKey sk; boolean initialized; @@ -74,7 +74,7 @@ public class NIOServerCnxn extends ServerCnxn { int sessionTimeout; - private final ZooKeeperServer zkServer; + protected final ZooKeeperServer zkServer; /** * The number of requests that have been submitted but not yet responded to. @@ -144,38 +144,49 @@ void sendBufferSync(ByteBuffer bb) { public void sendBuffer(ByteBuffer bb) { try { - if (bb != ServerCnxnFactory.closeConn) { - // We check if write interest here because if it is NOT set, - // nothing is queued, so we can try to send the buffer right - // away without waking up the selector - if ((sk.interestOps() & SelectionKey.OP_WRITE) == 0) { - try { - sock.write(bb); - } catch (IOException e) { - // we are just doing best effort right now - } - } - // if there is nothing left to send, we are done - if (bb.remaining() == 0) { - packetSent(); - return; + internalSendBuffer(bb); + } catch(Exception e) { + LOG.error("Unexpected Exception: ", e); + } + } + + /** + * This method implements the internals of sendBuffer. We + * have separated it from send buffer to be able to catch + * exceptions when testing. + * + * @param bb Buffer to send. + */ + protected void internalSendBuffer(ByteBuffer bb) { + if (bb != ServerCnxnFactory.closeConn) { + // We check if write interest here because if it is NOT set, + // nothing is queued, so we can try to send the buffer right + // away without waking up the selector + if(sk.isValid() && + ((sk.interestOps() & SelectionKey.OP_WRITE) == 0)) { + try { + sock.write(bb); + } catch (IOException e) { + // we are just doing best effort right now } } + // if there is nothing left to send, we are done + if (bb.remaining() == 0) { + packetSent(); + return; + } + } - synchronized(this.factory){ - sk.selector().wakeup(); - if (LOG.isTraceEnabled()) { - LOG.trace("Add a buffer to outgoingBuffers, sk " + sk - + " is valid: " + sk.isValid()); - } - outgoingBuffers.add(bb); - if (sk.isValid()) { - sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE); - } + synchronized(this.factory){ + sk.selector().wakeup(); + if (LOG.isTraceEnabled()) { + LOG.trace("Add a buffer to outgoingBuffers, sk " + sk + + " is valid: " + sk.isValid()); + } + outgoingBuffers.add(bb); + if (sk.isValid()) { + sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE); } - - } catch(Exception e) { - LOG.error("Unexpected Exception: ", e); } } diff --git a/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java b/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java index 5c94ed70ebe..bdee20f5f30 100644 --- a/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java +++ b/src/java/test/org/apache/zookeeper/server/NIOServerCnxnTest.java @@ -18,14 +18,15 @@ package org.apache.zookeeper.server; import java.io.IOException; - -import junit.framework.Assert; +import java.nio.ByteBuffer; +import java.nio.channels.CancelledKeyException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; +import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,4 +69,38 @@ public void testOperationsAfterCnxnClose() throws IOException, zk.close(); } } + + /** + * Mock extension of NIOServerCnxn to test for + * CancelledKeyException (ZOOKEEPER-2044). + */ + private static class MockNIOServerCnxn extends NIOServerCnxn { + public MockNIOServerCnxn(NIOServerCnxn cnxn) + throws IOException { + super(cnxn.zkServer, cnxn.sock, cnxn.sk, cnxn.factory); + } + + public void mockSendBuffer(ByteBuffer bb) throws Exception { + super.internalSendBuffer(bb); + } + } + + @Test(timeout = 30000) + public void testValidSelectionKey() throws Exception { + final ZooKeeper zk = createZKClient(hostPort, 3000); + try { + Iterable connections = serverFactory.getConnections(); + for (ServerCnxn serverCnxn : connections) { + MockNIOServerCnxn mock = new MockNIOServerCnxn((NIOServerCnxn) serverCnxn); + // Cancel key + ((NIOServerCnxn) serverCnxn).sock.keyFor(((NIOServerCnxnFactory) serverFactory).selector).cancel();; + mock.mockSendBuffer(ByteBuffer.allocate(8)); + } + } catch (CancelledKeyException e) { + LOG.error("Exception while sending bytes!", e); + Assert.fail(e.toString()); + } finally { + zk.close(); + } + } } From 053a9d29973a22ca60a02614911fc6e164d52244 Mon Sep 17 00:00:00 2001 From: Mohammad Arshad Date: Tue, 7 Feb 2017 13:41:54 +0530 Subject: [PATCH 383/444] ZOOKEEPER-2682: Make it optional to fail build on test failure Author: Mohammad Arshad Reviewers: Rakesh Radhakrishnan Closes #164 from arshadmohammad/ZOOKEEPER-2682-branch-3.4 --- build.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 864c5a40065..9c134744ed1 100644 --- a/build.xml +++ b/build.xml @@ -84,6 +84,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + @@ -1324,6 +1325,9 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> + + + Tests failed! @@ -1567,7 +1571,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - Tests failed! + From 286bf6913f3eed1ea9ee3e3523b2e8b6bdea56ba Mon Sep 17 00:00:00 2001 From: Michael Han Date: Fri, 10 Feb 2017 14:25:30 +0530 Subject: [PATCH 384/444] ZOOKEEPER-2672: Remove CHANGE.txt The CHANGE.txt is already not the source of truth of what's changed after we migrating to git - most of the git commits in recent couple of months don't update CHANGE.txt. The option of updating CHANGE.txt during commit flow automatically is none trivial, and do that manually is cumbersome and error prone. The consensus is we would rely on source control revision logs instead of CHANGE.txt moving forward; see https://www.mail-archive.com/devzookeeper.apache.org/msg37108.html for more details. Author: Michael Han Reviewers: Edward Ribeiro , Flavio Junqueira , Rakesh Radhakrishnan Closes #168 from hanm/ZOOKEEPER-2672-branch-3.4 --- CHANGES.txt | 2616 --------------------------------------------------- 1 file changed, 2616 deletions(-) delete mode 100644 CHANGES.txt diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index eb9ef86af24..00000000000 --- a/CHANGES.txt +++ /dev/null @@ -1,2616 +0,0 @@ -Release 3.4.10 - Unreleased - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-2502: Flaky Test: - org.apache.zookeeper.server.quorum.CnxManagerTest.testCnxFromFutureVersion - (Michael Han via phunt) - - ZOOKEEPER-2558: Potential memory leak in recordio.c - (Michael Han via phunt) - - ZOOKEEPER-2579: ZooKeeper server should verify that dataDir and - snapDir are writeable before starting (Abraham Fine via phunt) - - ZOOKEEPER-2467: NullPointerException when redo Command is passed negative value - (Rakesh Kumar Singh via rakeshr) - -IMPROVEMENTS: - - ZOOKEEPER-2507: C unit test improvement: line break between - 'ZooKeeper server started' and 'Running' (Michael Han via phunt) - - ZOOKEEPER-2557: Update gitignore to account for other file extensions. - (Edward Ribeiro via cnauroth) - - ZOOKEEPER-2594: Use TLS for downloading artifacts during build - (Olaf Flebbe via phunt) - - ZOOKEEPER-2606: SaslServerCallbackHandler#handleAuthorizeCallback() should - log the exception (Ted Yu via fpj) - -NEW FEATURE: - - ZOOKEEPER-1045: Support Quorum Peer mutual authentication via SASL - (rakeshr via phunt) - - -Release 3.4.9 - 2016-08-23 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-2375: Prevent multiple initialization of login object in each - ZooKeeperSaslClient instance (yuemeng via rakeshr) - - ZOOKEEPER-2379: recent commit broke findbugs qabot check - (rakeshr via cnauroth) - - ZOOKEEPER-2294 Ant target generate-clover-reports is broken - (charlie via phunt) - - ZOOKEEPER-2378 upgrade ivy to recent version (phunt) - - ZOOKEEPER-2373 Licenses section missing from pom file (phunt) - - ZOOKEEPER-2133 zkperl: Segmentation fault if getting a node with - null value (Botond Hejj via phunt) - - ZOOKEEPER-2283 traceFile property is not used in the ZooKeeper, it - should be removed from documentation (Arshad Mohammad via phunt) - - ZOOKEEPER-2385 Zookeeper trunk build is failing on windows - (Arshad Mohammad via phunt) - - ZOOKEEPER-2195 fsync.warningthresholdms in zoo.cfg not working - (Biju Nair via phunt) - - ZOOKEEPER-2141 ACL cache in DataTree never removes entries - (Adam Milne-Smith via camille) - - ZOOKEEPER-2423: Upgrade Netty version due to security vulnerability - (CVE-2014-3488) (Michael Han via phunt) - - ZOOKEEPER-2405: getTGT() in Login.java mishandles confidential - information (Michael Han via phunt) - - ZOOKEEPER-2477: documentation should refer to Java cli shell and not - C cli shell (Abraham Fine via phunt) - - ZOOKEEPER-1256: ClientPortBindTest is failing on Mac OS X - (Camille via phunt) - - ZOOKEEPER-2498: Potential resource leak in C client when processing - unexpected / out of order response (Michael Han via rgs) - - Fix command handling in the C client shell (phunt via fpj) - - ZOOKEEPER-2452: Back-port ZOOKEEPER-1460 to 3.4 for IPv6 literal - address support. (Abraham Fine via cnauroth) - - ZOOKEEPER-2247: Zookeeper service becomes unavailable when leader - fails to write transaction log (Rakesh via fpj) - -IMPROVEMENTS: - - ZOOKEEPER-2240 Make the three-node minimum more explicit in - documentation and on website (Shawn Heisey and Arshad Mohammad via phunt) - - ZOOKEEPER-2514: Simplify releasenotes creation for 3.4 branch - - consistent with newer branches. (phunt via cnauroth) - -Release 3.4.8 - 2016-02-05 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1929: std::length_error on update children - (Charles Strahan via rgs) - - ZOOKEEPER-2211: PurgeTxnLog does not correctly purge when snapshots and - logs are at different locations (Arshad Mohammad via rgs) - - ZOOKEEPER-2311: assert in setup_random - (Marshall McMullen via rgs) - - ZOOKEEPER-2295: TGT refresh time logic is wrong - (Arshad Mohammad via rgs) - - ZOOKEEPER-2281: ZK Server startup fails if there are spaces in the JAVA_HOME - path (Neha Bathra via cnauroth) - - ZOOKEEPER-2340: JMX is disabled even if JMXDISABLE is false - (Arshad Mohammad via rgs) - - ZOOKEEPER-2229: Several four-letter words are undocumented - (Chris Nauroth via rgs) - - ZOOKEEPER-2347: Deadlock shutting down zookeeper - (Rakesh R via rgs) - - ZOOKEEPER-2360: Update commons collections version used by tests/releaseaudit - (phunt via cnauroth) - - ZOOKEEPER-2243: Supported platforms is completely out of date (cnauroth) - -Release 3.4.7 - 2015-11-08 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1888. ZkCli.cmd commands fail with "'java' is not recognized as an - internal or external command" (Ivan Mitic via michim) - - ZOOKEEPER-1878. Inconsistent behavior in autocreation of dataDir and - dataLogDir (Rakesh R via michim) - - ZOOKEEPER-1901. [JDK8] Sort children for comparison in AsyncOps tests - (Andrew Purtell via michim) - - ZOOKEEPER-1906. zkpython: invalid data in GetData for empty node - (Nikita Vetoshkin via michim) - - ZOOKEEPER-1897. ZK Shell/Cli not processing commands (stack via michim) - - ZOOKEEPER-1913. Invalid manifest files due to bogus revision property value - (Raul Gutierrez Segales via michim) - - ZOOKEEPER-1911. REST contrib module does not include all required files when - packaged (Sean Mackrory via michim) - - ZOOKEEPER-1926. Unit tests should only use build/test/data for data (Enis - Soztutar via michim) - - ZOOKEEPER-1062. Net-ZooKeeper: Net::ZooKeeper consumes 100% cpu on wait - (Botond Hejj via michim) - - ZOOKEEPER-1797. PurgeTxnLog may delete data logs during roll (Rakesh R via - michim) - - ZOOKEEPER-1945. deb - zkCli.sh, zkServer.sh and zkEnv.sh regression caused - by ZOOKEEPER-1663 (Mark Flickinger via fpj) - - ZOOKEEPER-1939. ReconfigRecoveryTest.testNextConfigUnreachable is - failing (Rakesh R via phunt) - - ZOOKEEPER-1222. getACL should only call DataTree.copyStat when passed in - stat is not null (Michi Mutsuzaki via rakeshr) - - ZOOKEEPER-2146 BinaryInputArchive readString should check length before - allocating memory (Hongchao Deng via michim) - - ZOOKEEPER-2039. Jute compareBytes incorrect comparison index (Ian Dimayuga via fpj) - - ZOOKEEPER-2047 testTruncationNullLog fails on windows (flavio via rakeshr) - - ZOOKEEPER-2026 Startup order in ServerCnxnFactory-ies is wrong (Stevo Slavic via rakeshr) - - ZOOKEEPER-2049 Yosemite build failure: htonll conflict (Till Toenshoff via - michim) - - ZOOKEEPER-2052 Unable to delete a node when the node has no children - (Hongchao Deng and Yip Ng via rakeshr) - - ZOOKEEPER-2060 Trace bug in NettyServerCnxnFactory (Ian via fpj) - - ZOOKEEPER-2064 Prevent resource leak in various classes (Ted Yu via fpj) - - ZOOKEEPER-1949 recipes jar not included in the distribution package (Rakesh R - via michim) - - ZOOKEEPER-2114 jute generated allocate_* functions are not externally visible - (Tim Crowder via michim) - - ZOOKEEPER-2073 Memory leak on zookeeper_close (Dave Gosselin via michim) - - ZOOKEEPER-2056 Zookeeper 3.4.x and 3.5.0-alpha is not OSGi compliant - (Deiwin Sarjas via rgs) - - ZOOKEEPER-2174 JUnit4ZKTestRunner logs test failure for all exceptions even - if the test method is annotated with an expected exception (Chris Nauroth - via rgs) - - ZOOKEEPER-1077: C client lib doesn't build on Solaris (Chris Nauroth via rgs) - - ZOOKEEPER-2186 QuorumCnxManager#receiveConnection may crash with random input - (rgs via michim) - - ZOOKEEPER-2179: Typo in Watcher.java (Archana T via rgs) - - ZOOKEEPER-2096: C client builds with incorrect error codes in VisualStudio 2010+ - (Vitaly Stakhovsky via rgs) - - ZOOKEEPER-2194: Let DataNode.getChildren() return an unmodifiable view of its children set - (Hitoshi Mitake via rgs) - - ZOOKEEPER-2201: Network issues can cause cluster to hang due to near-deadlock - (Donny Nadolny via rgs) - - ZOOKEEPER-2213: Empty path in Set crashes server and prevents restart - (Hongchao Deng via rgs) - - ZOOKEEPER-706: Large numbers of watches can cause session re-establishment to fail - (Chris Thunes via rgs) - - ZOOKEEPER-602: log all exceptions not caught by ZK threads - (Rakesh R via rgs) - - ZOOKEEPER-2224: Four letter command hangs when network is slow - (Arshad Mohammad via rgs) - - ZOOKEEPER-2235: License update (fpj via michim) - - ZOOKEEPER-2239: JMX State from LocalPeerBean incorrect - (Kevin Lee via rgs) - - ZOOKEEPER-1927: zkServer.sh fails to read dataDir (and others) - from zoo.cfg on Solaris 10 (grep issue, manifests as FAILED TO WRITE PID) - (Chris Nauroth via rgs) - - ZOOKEEPER-2033: zookeeper follower fails to start after - a restart immediately following a new epoch (Asad Saeed via fpj) - - ZOOKEEPER-2256: Zookeeper is not using specified JMX port in zkEnv.sh - (Arshad Mohammad via rakeshr) - - ZOOKEEPER-1506: Re-try DNS hostname -> IP resolution if node connection - fails (Robert Thille via fpj) - - ZOOKEEPER-2279: QuorumPeer loadDataBase() error message is incorrect - (Arshad Mohammad via rakeshr) - - ZOOKEEPER-1803: Add description for pzxid in programmer's guide - (Arshad Mohammad via rakeshr) - - ZOOKEEPER-2253: C asserts ordering of ping requests, while Java client does not - (Chris Chen via rgs) - - ZOOKEEPER-2268: Zookeeper doc creation fails on windows - (Arshad Mohammad via cnauroth) - - ZOOKEEPER-2296: compilation broken for 3.4 - (Raul Gutierrez Segales via cnauroth) - - ZOOKEEPER-1029: C client bug in zookeeper_init (if bad hostname is given) - (fpj via cnauroth) - - ZOOKEEPER-2142: JMX ObjectName is incorrect for observers (Edward Ribeiro - via michim) - - ZOOKEEPER-1853: zkCli.sh can't issue a CREATE command containing - spaces in the data (Jun Gong via rgs) - - ZOOKEEPER-2227: stmk four-letter word fails execution at server while reading - trace mask argument (Chris Nauroth via rgs) - -IMPROVEMENTS: - - ZOOKEEPER-1575. adding .gitattributes to prevent CRLF and LF mismatches for - source and text files (Raja Aluri via michim) - - ZOOKEEPER-657. Cut down the running time of ZKDatabase corruption - (Michi Mutsuzaki via rakeshr) - - ZOOKEEPER-1746. AsyncCallback.*Callback don't have any Javadoc - (Hongchao Deng via phunt) - - ZOOKEEPER-1917. Apache Zookeeper logs cleartext admin passwords (fpj via michim) - - ZOOKEEPER-1948 Enable JMX remote monitoring (Biju Nair via rakeshr) - - ZOOKEEPER-2126 Improve exit log messsage of EventThread and SendThread by - adding SessionId (surendra singh lilhore via rakeshr) - - ZOOKEEPER-2124 Allow Zookeeper version string to have underscore '_' - (Chris Nauroth via michim) - - ZOOKEEPER-2205: Log type of unexpected quorum packet in learner handler loop - (Hitoshi Mitake via rgs) - - ZOOKEEPER-1907 Improve Thread handling - (Rakesh R via hdeng) - - ZOOKEEPER-2270: Allow MBeanRegistry to be overridden for better unit tests - (Jordan Zimmerman via rgs) - - ZOOKEEPER-2040: Server to log underlying cause of SASL connection problems. - (Steve Loughran via cnauroth) - - ZOOKEEPER-2245: SimpleSysTest test cases fails (Arshad Mohammad via rakeshr) - - ZOOKEEPER-2315: Change client connect zk service timeout log level from Info - to Warn level (Lin Yiqun via rgs) - - ZOOKEEPER-2240: Make the three-node minimum more explicit in documentation - and on website (Shawn Heisey via rgs) - -NEW FEATURES: - - ZOOKEEPER-2237 Port async multi to 3.4 branch (Ivan Kelly via rakeshr) - -Release 3.4.6 - 2014-03-10 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1900. NullPointerException in truncate (Camille Fournier) - - ZOOKEEPER-1474. Cannot build Zookeeper with IBM Java: use of Sun - MXBean classes (Adalberto Medeiros via phunt) - - ZOOKEEPER-1596. Zab1_0Test should ensure that the file is closed - (Enis Soztutar via phunt) - - ZOOKEEPER-1513. "Unreasonable length" exception while starting a - server (Skye W-M via phunt) - - ZOOKEEPER-1581. change copyright in notice to 2012 (breed via phunt) - - ZOOKEEPER-753. log4j dependency in the pom needs to have exclusion - lists (Sean Busbey via phunt) - - ZOOKEEPER-1553. Findbugs configuration is missing some dependencies - (Sean Busbey via phunt) - - ZOOKEEPER-1478. Small bug in QuorumTest.testFollowersStartAfterLeader( ) - (Alexander Shraer via fpj, breed, phunt) - - ZOOKEEPER-1387. Wrong epoch file created - (Benjamin Busjaeger via breed, phunt) - - ZOOKEEPER-1578. org.apache.zookeeper.server.quorum.Zab1_0Test failed due to - hard code with 33556 port. (Li Ping via mahadev) - - ZOOKEEPER-1334. Zookeeper 3.4.x is not OSGi compliant - MANIFEST.MF - is flawed (Claus Ibsen via phunt) - - ZOOKEEPER-1535. ZK Shell/Cli re-executes last command on exit - (Edward Ribeiro via camille) - - ZOOKEEPER-1495. ZK client hangs when using a function not available - on the server. (Skye W-M via phunt) - - ZOOKEEPER-1613. The documentation still points to 2008 in the - copyright notice (Edward Ribeiro via phunt) - - ZOOKEEPER-1562. Memory leaks in zoo_multi API - (Deepak Jagtap via phunt) - - ZOOKEEPER-1645. ZooKeeper OSGi package imports not complete - (Arnoud Glimmerveen via phunt) - - ZOOKEEPER-1648. Fix WatcherTest in JDK7 - (Thawan Kooburat via phunt) - - ZOOKEEPER-1606. intermittent failures in ZkDatabaseCorruptionTest on - jenkins (lixiaofeng via phunt) - - ZOOKEEPER-1647. OSGi package import/export changes not applied to - bin-jar (Arnoud Glimmerveen via phunt) - - ZOOKEEPER-1633. Introduce a protocol version to connection initiation - message (Alexander Shraer via michim) - - ZOOKEEPER-1697. large snapshots can cause continuous quorum failure - (phunt via fpj) - - ZOOKEEPER-1706. Typo in Double Barriers example (Jingguo Yao via fpj) - - ZOOKEEPER-1642. Leader Loading Database Twice (fpj via camille) - - ZOOKEEPER-1663. scripts don't work when path contains spaces (Amichai Rothman via camille) - - ZOOKEEPER-1714 perl client segfaults if ZOO_READ_ACL_UNSAFE constant is used - (Botond Hejj via camille) - - ZOOKEEPER-1719. zkCli.sh, zkServer.sh and zkEnv.sh regression caused by ZOOKEEPER-1663 - (Marshall McMullen via camille) - - ZOOKEEPER-1702. ZooKeeper client may write operation packets before - receiving successful response to connection request, can cause TCP - RST (Chris Nauroth via phunt) - - ZOOKEEPER-1629. testTransactionLogCorruption occasionally fails. (shralex via camille) - - ZOOKEEPER-1731. Unsynchronized access to ServerCnxnFactory.connectionBeans results in - deadlock. (Dave Latham via camille) - - ZOOKEEPER-1713. wrong time calculation in zkfuse.cc (german via fpj) - - ZOOKEEPER-1379. 'printwatches, redo, history and connect '. client commands always print usage. This is not necessary (edward via fpj) - - ZOOKEEPER-1448: Node+Quota creation in transaction log can crash leader startup (Botond Hejj via fpj) - - ZOOKEEPER-1664. Kerberos auth doesn't work with native platform GSS integration. (Boaz Kelmer via camille) - - ZOOKEEPER-1750. Race condition producing NPE in NIOServerCnxn.toString - (Rakesh R via michim) - - ZOOKEEPER-1754. Read-only server allows to create znode (Rakesh R via fpj) - - ZOOKEEPER-1751. ClientCnxn#run could miss the second ping or connection get - dropped before a ping. (Jeffrey Zhong via mahadev) - - ZOOKEEPER-1657. Increased CPU usage by unnecessary SASL checks (Philip K. Warren via fpj) - - ZOOKEEPER-1753. ClientCnxn is not properly releasing the resources, - which are used to ping RwServer (Rakesh R via fpj) - - ZOOKEEPER-1096. Leader communication should listen on specified IP, not wildcard address (Jared Cantwell, German Blanco via fpj) - - ZOOKEEPER-87. Follower does not shut itself down if its too far behind the leader. (German Blanco via fpj) - - ZOOKEEPER-1603. StaticHostProviderTest testUpdateClientMigrateOrNot hangs (fpj) - - ZOOKEEPER-1696. Fail to run zookeeper client on Weblogic application server. - (Jeffrey Zhong via mahadev) - - ZOOKEEPER-1770. NullPointerException in SnapshotFormatter - (Germán Blanco via phunt) - - ZOOKEEPER-732. Improper translation of error into Python exception (Andrei Savu, Lei Zhang, fpj via fpj) - - ZOOKEEPER-1551. Observers ignore txns that come after snapshot and UPTODATE - (thawan, fpj via thawan) - - ZOOKEEPER-1774. QuorumPeerMainTest fails consistently with - "complains about host" assertion failure (phunt) - - ZOOKEEPER-877. zkpython does not work with python3.1 - (Daniel Enman via phunt) - - ZOOKEEPER-1624. PrepRequestProcessor abort multi-operation incorrectly. (thawan via camille) - - ZOOKEEPER-1610. Some classes are using == or != to compare - Long/String objects instead of .equals() (Edward Ribeiro via phunt) - - ZOOKEEPER-1646. mt c client tests fail on Ubuntu Raring (phunt) - - ZOOKEEPER-1558. Leader should not snapshot uncommitted state (fpj) - - ZOOKEEPER-1732. ZooKeeper server unable to join established - ensemble (German Blanco via fpj) - - ZOOKEEPER-1667. Watch event isn't handled correctly when - a client reestablish to a server (jacky007, fpj via fpj) - - ZOOKEEPER-1799. SaslAuthFailDesignatedClientTest.testAuth fails - frequently on SUSE (Jeffrey Zhong via phunt) - - ZOOKEEPER-1557. jenkins jdk7 test failure in - testBadSaslAuthNotifiesWatch (Eugene Koontz via phunt) - - ZOOKEEPER-1744. clientPortAddress breaks "zkServer.sh status" - (Nick Ohanian via phunt) - - ZOOKEEPER-1798. Fix race condition in testNormalObserverRun - (thawan, fpj via thawan) - - ZOOKEEPER-1808. Add version to FLE notifications for 3.4 branch (fpj) - - ZOOKEEPER-1812. ZooInspector reconnection always fails if first - connection fails (Benjamin Jaton via phunt) - - ZOOKEEPER-1597. Windows build failing (michim via fpj) - - ZOOKEEPER-1817. Fix don't care for b3.4 (fpj) - - ZOOKEEPER-1653. zookeeper fails to start because of inconsistent - epoch (michim via fpj) - - ZOOKEEPER-1821. very ugly warning when compiling load_gen.c - (german blanco via fpj) - - ZOOKEEPER-1632. fix memory leaks in cli_st (fpj via michim) - - ZOOKEEPER-1459. Standalone ZooKeeperServer is not closing - the transaction log files on shutdown (Rakesh R via fpj) - - ZOOKEEPER-1019. zkfuse doesn't list dependency on boost in README - (Raul Gutierrez Segales via michim) - - ZOOKEEPER-1834. Catch IOException in FileTxnLog (fpj via michim) - - ZOOKEEPER-1382. Zookeeper server holds onto dead/expired session ids in the watch data structures - (Germán Blanco and Michael Morello via camille) - - ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions) - (Germán Blanco via fpj) - - ZOOKEEPER-1839. Deadlock in NettyServerCnxn (Rakesh R via michim) - - ZOOKEEPER-1622. session ids will be negative in the year 2022 - (Eric Newton via phunt) - - ZOOKEEPER-1756. zookeeper_interest() in C client can return a timeval of 0 - (Eric Lindvall via michim) - - ZOOKEEPER-1388. Client side 'PathValidation' is missing for the - multi-transaction api. (Rakesh R via marshallm, phunt) - - ZOOKEEPER-1841. problem in QuorumTest (Germán via fpj) - - ZOOKEEPER-1733 FLETest#testLE is flaky oo.l windows boxes - (michim, Jeffrey Zhong via fpj) - - ZOOKEEPER-1849. Need to properly tear down tests in various - cases (Germán via fpj) - - ZOOKEEPER-1179. NettyServerCnxn does not properly close - socket on 4 letter word requests (Rakesh R, Germán Blanco - via fpj) - - ZOOKEEPER-1852. ServerCnxnFactory instance is not properly - cleanedup (Rakesh R via fpj) - - ZOOKEEPER-1414. QuorumPeerMainTest.testQuorum, testBadPackets are failing - intermittently (Rakesh R via michim) - - ZOOKEEPER-1057. zookeeper c-client, connection to offline server fails to - successfully fallback to second zk host (Germán Blanco via michim) - - ZOOKEEPER-1857. PrepRequestProcessotTest doesn't shutdown ZooKeeper server - (Germán Blanco via michim) - - ZOOKEEPER-1238. when the linger time was changed for NIO the patch missed - Netty (Skye Wanderman-Milne via fpj) - - ZOOKEEPER-1837. Fix JMXEnv checks (potential race conditions) - (Germán Blanco via fpj) - - ZOOKEEPER-1858. JMX checks - potential race conditions while stopping - and starting server (Rakesh R via fpj) - - ZOOKEEPER-1867. Bug in ZkDatabaseCorruptionTest (fpj) - - ZOOKEEPER-1872. QuorumPeer is not shutdown in few cases - (Rakesh R via fpj) - - ZOOKEEPER-1573. Unable to load database due to missing parent node - (Vinayakumar B via phunt, fpj) - - ZOOKEEPER-1811. The ZooKeeperSaslClient service name principal is - hardcoded to "zookeeper" (Harsh J via phunt) - - ZOOKEEPER-1873. Unnecessarily InstanceNotFoundException is coming when - unregister failed jmxbeans (Rakesh R via michim) - - ZOOKEEPER-1844. TruncateTest fails on windows (Rakesh R via fpj) - - ZOOKEEPER-1755. Concurrent operations of four letter 'dump' ephemeral - command and killSession causing NPE (Rakesh R via camille) - - ZOOKEEPER-1904. WatcherTest#testWatchAutoResetWithPending is failing - (Rakesh R via michim) - -IMPROVEMENTS: - - ZOOKEEPER-1564. Allow JUnit test build with IBM Java - (Paulo Ricardo Paz Vital via phunt) - - ZOOKEEPER-1598. Ability to support more digits in the version string - (Raja Aluri via phunt) - - ZOOKEEPER-1583. Document maxClientCnxns in conf/zoo_sample.cfg - (Christopher Tubbs via phunt) - - ZOOKEEPER-1584. Adding mvn-install target for deploying the - zookeeper artifacts to .m2 repository (Ashish Singh via phunt) - - ZOOKEEPER-1324. Remove Duplicate NEWLEADER packets from the - Leader to the Follower. (thawan, fpj via fpj) - - ZOOKEEPER-1615. minor typos in ZooKeeper Programmer's Guide web page - (Evan Zacks via phunt) - - ZOOKEEPER-1552. Enable sync request processor in Observer (thawan, fpj) - - ZOOKEEPER-1758. Add documentation for zookeeper.observer.syncEnabled flag - (thawan, fpj via thawan) - - ZOOKEEPER-1771. ZooInspector authentication (Benjamin Jaton via phunt) - - ZOOKEEPER-1627. Add org.apache.zookeeper.common to exported packages - in OSGi MANIFEST (Arnoud Glimmerveen via phunt) - - ZOOKEEPER-1666. Avoid Reverse DNS lookup if the hostname in - connection string is literal IP address. (George Cao via camille) - - ZOOKEEPER-1786. ZooKeeper data model documentation is incorrect - (Niraj Tolia via fpj) - - ZOOKEEPER-1715. Upgrade netty version (Sean Bridges via michim) - - ZOOKEEPER-1430. add maven deploy support to the build - (Giridharan Kesavan via phunt) - -Release 3.4.5 - 2012-09-30 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1376. zkServer.sh does not correctly check for - $SERVER_JVMFLAGS (Skye W-M via henryr) - - ZOOKEEPER-1550. ZooKeeperSaslClient does not finish anonymous login on - OpenJDK. (Eugene Koontz via mahadev) - - -Release 3.4.4 - 2012-09-17 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1466. QuorumCnxManager.shutdown missing synchronization. - (Patrick Hunt via mahadev) - - ZOOKEEPER-1386. avoid flaky URL redirection in "ant javadoc" : - replace "http://java.sun.com/javase/6/docs/api/" with - "http://download.oracle.com/javase/6/docs/api/" (Eugene Koontz via camille) - - ZOOKEEPER-1354. AuthTest.testBadAuthThenSendOtherCommands fails - intermittently (phunt via camille) - - ZOOKEEPER-1277. servers stop serving when lower 32bits of zxid roll - over (phunt) - - ZOOKEEPER-1412. java client watches inconsistently triggered on - reconnect (phunt) - - ZOOKEEPER-1344. ZooKeeper client multi-update command is not - considering the Chroot request (Rakesh R via phunt) - - ZOOKEEPER-1307. zkCli.sh is exiting when an Invalid ACL exception is - thrown from setACL command through client (Kavita Sharma via phunt) - - ZOOKEEPER-1390. some expensive debug code not protected by a check - for debug (breed via camille) - - ZOOKEEPER-1406. dpkg init scripts don't restart - missing - check_priv_sep_dir (Chris Beauchamp via phunt) - - ZOOKEEPER-1403. zkCli.sh script quoting issue (James Page via phunt) - - ZOOKEEPER-1384. test-cppunit overrides LD_LIBRARY_PATH and fails if - gcc is in non-standard location (Jay Shrauner via phunt) - - ZOOKEEPER-1419. Leader election never settles for a 5-node cluster - (flavio via camille) - - ZOOKEEPER-1256. ClientPortBindTest is failing on Mac OS X - (Daniel Gómez Ferro via phunt) - - ZOOKEEPER-1433. improve ZxidRolloverTest (test seems flakey) (phunt via henryr) - - ZOOKEEPER-1395. node-watcher double-free redux (Mike Lundy via henryr) - - ZOOKEEPER-1450. Backport of ZOOKEEPER-1294 fix to 3.4 and 3.3 (Norman Bishop via camille) - - ZOOKEEPER-1048. addauth command does not work in cli_mt/cli_st (allengao via michim) - - ZOOKEEPER-1339. C client doesn't build with --enable-debug (Eric Liang via michim) - - ZOOKEEPER-1318. In Python binding, get_children (and get and exists, and probably others) - with expired session doesn't raise exception properly (henryr via michim) - - ZOOKEEPER-1431. zkpython async calls leak memory (Kapil Thangavelu and Andre Cruz via henryr) - - ZOOKEEPER-1163. Memory leak in zk_hashtable.c:do_insert_watcher_object() - (Anupam Chanda via michim) - - ZOOKEEPER-1490. If the configured log directory does not exist - zookeeper will not start. Better to create the directory and start - (suja s via phunt) - - ZOOKEEPER-1210. Can't build ZooKeeper RPM with RPM >= 4.6.0 (i.e. on - RHEL 6 and Fedora >= 10) (Tadeusz Andrzej Kadłubowski via phunt) - - ZOOKEEPER-1236. Security uses proprietary Sun APIs - (Adalberto Medeiros via phunt) - - ZOOKEEPER-1471. Jute generates invalid C++ code - (Michi Mutsuzaki via phunt) - - ZOOKEEPER-1465. Cluster availability following new leader election - takes a long time with large datasets - is correlated to dataset size - (fpj and Thawan Kooburat via camille) - - ZOOKEEPER-1427. Writing to local files is done non-atomically (phunt) - - ZOOKEEPER-1489. Data loss after truncate on transaction log (phunt) - - ZOOKEEPER-1521. LearnerHandler initLimit/syncLimit problems - specifying follower socket timeout limits (phunt) - - ZOOKEEPER-1493. C Client: zookeeper_process doesn't invoke - completion callback if zookeeper_close has been called - (Michi Mutsuzaki via phunt and mahadev) - - ZOOKEEPER-1522. intermittent failures in Zab test due to NPE in - recursiveDelete test function (phunt via flavio) - - ZOOKEEPER-1514. FastLeaderElection - leader ignores the round - information when joining a quorum (flavio via henryr) - - ZOOKEEPER-1536 c client : memory leak in winport.c (brooklin via michim) - - ZOOKEEPER-1481 allow the C cli to run exists with a watcher (phunt via michim) - - ZOOKEEPER-1380. zkperl: _zk_release_watch doesn't remove items properly from - the watch list. (Botond Hejj via mahadev) - - ZOOKEEPER-1501. Nagios plugin always returns OK when it cannot connect to - zookeeper. (Brian Sutherland via mahadev) - - ZOOKEEPER-1494. C client: socket leak after receive timeout in - zookeeper_interest() (Michi Mutsuzaki via mahadev) - - ZOOKEEPER-1483. Fix leader election recipe documentation. (Michi Mutsuzaki - via mahadev) - - ZOOKEEPER-1496. Ephemeral node not getting cleared even after client has - exited (Rakesh R via mahadev) - -IMPROVEMENTS: - - ZOOKEEPER-1389. it would be nice if start-foreground used exec $JAVA - in order to get rid of the intermediate shell process - (Roman Shaposhnik via phunt) - - ZOOKEEPER-1377. add support for dumping a snapshot file content (similar to LogFormatter). (phunt via camille) - - ZOOKEEPER-1454. Document how to run autoreconf if cppunit is - installed in a non-standard directory (Michi Mutsuzaki via phunt) - - ZOOKEEPER-1503. remove redundant JAAS configuration code in SaslAuthTest and - SaslAuthFailTest (Eugene Koontz via phunt) - - ZOOKEEPER-1510. Should not log SASL errors for non-secure usage - (Todd Lipcon via phunt) - - ZOOKEEPER-1497. Allow server-side SASL login with JAAS configuration - to be programmatically set (rather than only by reading JAAS - configuration file) (Matteo Bertozzi via phunt) - - ZOOKEEPER-1437. Client uses session before SASL authentication complete. - (Eugene Koontz via mahadev) - - ZOOKEEPER-1361. Leader.lead iterates over 'learners' set without proper - synchronisation. (Henry Robinson via mahadev) - -Release 3.4.3 - 2012-02-06 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1089. zkServer.sh status does not work due to invalid - option of nc (Roman Shaposhnik via phunt) - - ZOOKEEPER-1345. Add a .gitignore file with general exclusions and - Eclipse project files excluded (Harsh J via phunt) - - ZOOKEEPER-1343. getEpochToPropose should check if lastAcceptedEpoch is greater or equal than epoch (fpj via breed) - - ZOOKEEPER-1358. In StaticHostProviderTest.java, testNextDoesNotSleepForZero tests that hostProvider.next(0) - doesn't sleep by checking that the latency of this call is less than 10sec (Alex Shraer via camille) - - ZOOKEEPER-1351. invalid test verification in MultiTransactionTest (phunt via camille) - - ZOOKEEPER-973. bind() could fail on Leader because it does not - setReuseAddress on its ServerSocket (Harsh J via phunt) - - ZOOKEEPER-1367. Data inconsistencies and unexpired ephemeral nodes after cluster restart. - (Bejamin Reed via mahadev) - - ZOOKEEPER-1353. C client test suite fails consistently. - (Clint Byrum via mahadev) - - ZOOKEEPER-1373. Hardcoded SASL login context name clashes with Hadoop security - configuration override. (Eugene Koontz and Thomas Weise via mahadev) - - ZOOKEEPER-1352. server.InvalidSnapshotTest is using connection timeouts that - are too short. (phunt via mahadev) - - ZOOKEEPER-1336. javadoc for multi is confusing, references functionality that doesn't - seem to exist (phunt via mahadev) - - ZOOKEEPER-1340. multi problem - typical user operations are - generating ERROR level messages in the server (phunt via mahadev) - - ZOOKEEPER-1374. C client multi-threaded test suite fails to - compile on ARM architectures. (James Page via mahadev) - - ZOOKEEPER-1337. multi's "Transaction" class is missing tests. - (phunt and camille via mahadev) - - ZOOKEEPER-1338. class cast exceptions may be thrown by multi - ErrorResult class (invalid equals) (phunt via mahadev) - -IMPROVEMENTS: - - ZOOKEEPER-1322. Cleanup/fix logging in Quorum code. (phunt via mahadev) - - ZOOKEEPER-1327. there are still remnants of hadoop urls. - (Harsh J via mahadev) - - -Release 3.4.2 - 2011-12-21 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1323. c client doesn't compile on freebsd - (michi mutsuzaki via phunt) - - ZOOKEEPER-1333. NPE in FileTxnSnapLog when restarting a cluster. - (Patrick Hunt and Andrew Mc Nair via mahadev) - -Release 3.4.1 - 2011-12-12 - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-1311. ZooKeeper test jar is broken (Ivan Kelly via phunt) - - ZOOKEEPER-1305. zookeeper.c:prepend_string func can dereference null ptr. - (Daniel Lescohier via mahadev) - - ZOOKEEPER-1316. zookeeper_init leaks memory if chroot is just '/'. - (Akira Kitada via mahadev) - - ZOOKEEPER-1315. zookeeper_init always reports sessionPasswd=. - (Akira Kitada via mahadev) - - ZOOKEEPER-1317. Possible segfault in zookeeper_init. - (Akira Kitada via mahadev) - - ZOOKEEPER-1319. Missing data after restarting+expanding a cluster. - (Patrick Hunt and Ben Reed via mahadev) - - ZOOKEEPER-1269. Multi deserialization issues (Camille Fournier via - mahadev) - -Release 3.4.0 - 2011-10-25 - -Non-backward compatible changes: - -BUGFIXES: - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-735. cppunit test testipv6 assumes that the machine is ipv6 - enabled. (mahadev) - - ZOOKEEPER-720. Use zookeeper-{version}-sources.jar instead of - zookeeper-{version}-src.jar to publish sources in the Maven repository - (paolo via phunt) - - ZOOKEEPER-722. zkServer.sh uses sh's builtin echo on BSD, behaves - incorrectly. (Ivan Kelly via phunt) - - ZOOKEEPER-741. root level create on REST proxy fails (phunt) - - ZOOKEEPER-631. zkpython's C code could do with a style clean-up - (henry robinson via phunt) - - ZOOKEEPER-746. learner outputs session id to log in dec (phunt via - henryr) - - ZOOKEEPER-738. zookeeper.jute.h fails to compile with -pedantic - (Jozef Hatala via phunt) - - ZOOKEEPER-734. QuorumPeerTestBase.java and ZooKeeperServerMainTest.java - do not handle windows path correctly (Vishal K via phunt) - - ZOOKEEPER-754. numerous misspellings "succesfully" - (Andrei Savu via phunt) - - ZOOKEEPER-749. OSGi metadata not included in binary only jar (phunt - via henryr) - - ZOOKEEPER-750. move maven artifacts into "dist-maven" subdir of the - release (package target) (phunt via henryr) - - ZOOKEEPER-758. zkpython segfaults on invalid acl with missing key - (Kapil Thangavelu via henryr) - - ZOOKEEPER-737. some 4 letter words may fail with netcat (nc). (mahadev) - - ZOOKEEPER-764. Observer elected leader due to inconsistent voting view - (henry via mahadev) - - ZOOKEEPER-763. Deadlock on close w/ zkpython / c client - (henry via phunt) - - ZOOKEEPER-774. Recipes tests are slightly outdated: they do not compile - against JUnit 4.8 (Sergey Doroshenko via phunt) - - ZOOKEEPER-772. zkpython segfaults when watcher from async get children is - invoked. (henry via phunt) - - ZOOKEEPER-636. configure.ac has instructions which override the contents of - CFLAGS and CXXFLAGS. (Maxim P. Dementiev via phunt) - - ZOOKEEPER-796. zkServer.sh should support an external PIDFILE variable - (Alex Newman via phunt) - - ZOOKEEPER-719. Add throttling to BookKeeper client (fpj via breed) - - ZOOKEEPER-814. monitoring scripts are missing apache license headers - (andrei savu via mahadev) - - ZOOKEEPER-783. committedLog in ZKDatabase is not properly synchronized - (henry via mahadev) - - ZOOKEEPER-790. Last processed zxid set prematurely while establishing - leadership (flavio via mahadev) - - ZOOKEEPER-795. eventThread isn't shutdown after a connection - "session expired" event coming (Sergey Doroshenko and Ben via mahadev) - - ZOOKEEPER-792. zkpython memory leak (Lei Zhang via henryr) - - ZOOKEEPER-854. BookKeeper does not compile due to changes in the ZooKeeper - code (Flavio via mahadev) - - ZOOKEEPER-861. Missing the test SSL certificate used for running junit tests. - (erwin tam via mahadev) - - ZOOKEEPER-867. ClientTest is failing on hudson - fd cleanup (phunt) - - ZOOKEEPER-785. Zookeeper 3.3.1 shouldn't infinite loop if someone creates a - server.0 line (phunt and Andrei Savu via breed) - - ZOOKEEPER-785. Zookeeper 3.3.1 shouldn't infinite loop if someone creates a - server.0 line (part 2) (phunt) - - ZOOKEEPER-870. Zookeeper trunk build broken. (mahadev via phunt) - - ZOOKEEPER-831. BookKeeper: Throttling improved for reads (breed via fpj) - - ZOOKEEPER-846. zookeeper client doesn't shut down cleanly on the close call - (phunt) - - ZOOKEEPER-804. c unit tests failing due to "assertion cptr failed" (michi - mutsuzaki via mahadev) - - ZOOKEEPER-844. handle auth failure in java client - (Camille Fournier via phunt) - - ZOOKEEPER-822. Leader election taking a long time to complete - (Vishal K via phunt) - - ZOOKEEPER-866. Hedwig Server stays in "disconnected" state when - connection to ZK dies but gets reconnected (erwin tam via breed) - - ZOOKEEPER-881. ZooKeeperServer.loadData loads database twice - (jared cantwell via breed) - - ZOOKEEPER-855. clientPortBindAddress should be clientPortAddress - (Jared Cantwell via fpj) - - ZOOKEEPER-888. c-client / zkpython: Double free corruption on - node watcher (Austin Shoemaker via henryr) - - ZOOKEEPER-893. ZooKeeper high cpu usage when invalid requests - (Thijs Terlouw via phunt) - - ZOOKEEPER-804. c unit tests failing due to "assertion cptr failed" - (second try - Jared Cantwell via phunt) - - ZOOKEEPER-820. update c unit tests to ensure "zombie" java server - processes don't cause failure (Michi Mutsuzaki via phunt) - - ZOOKEEPER-794. Callbacks are not invoked when the client is closed - (Alexis Midon via phunt) - - ZOOKEEPER-800. zoo_add_auth returns ZOK if zookeeper handle is in - ZOO_CLOSED_STATE (michi mutsuzaki via mahadev konar) - - ZOOKEEPER-904. super digest is not actually acting as a full superuser - (Camille Fournier via mahadev) - - ZOOKEEPER-897. C Client seg faults during close (jared cantwell via mahadev) - - ZOOKEEPER-898. C Client might not cleanup correctly during close - (jared cantwell via mahadev) - - ZOOKEEPER-907. Spurious "KeeperErrorCode = Session moved" messages (vishal k via breed) - - ZOOKEEPER-884. Remove LedgerSequence references from BookKeeper documentation and comments in tests (fpj via breed) - - ZOOKEEPER-916. Problem receiving messages from subscribed channels in c++ client (ivan via breed) - - ZOOKEEPER-930. Hedwig c++ client uses a non thread safe logging library (ivan via breed) - - ZOOKEEPER-900. FLE implementation should be improved to use non-blocking sockets (vishal via fpj) - - ZOOKEEPER-937. test -e not available on solaris /bin/sh (Erik Hetzner via mahadev) - - ZOOKEEPER-905. enhance zkServer.sh for easier zookeeper automation-izing (Nicholas Harteau via mahadev) - - ZOOKEEPER-913. Version parser fails to parse "3.3.2-dev" from build.xml (Anthony Urso and phunt via breed) - - ZOOKEEPER-957. zkCleanup.sh doesn't do anything (Ted Dunning via mahadev) - - ZOOKEEPER-958. Flag to turn off autoconsume in hedwig c++ client (Ivan Kelly - via mahadev) - - ZOOKEEPER-882. Startup loads last transaction from snapshot (j:ared via fpj) - - ZOOKEEPER-962. leader/follower coherence issue when follower is receiving a DIFF - (camille fournier via breed) - - ZOOKEEPER-902. Fix findbug issue in trunk "Malicious code vulnerability" - (flavio and phunt via phunt) - - ZOOKEEPER-985. Test BookieRecoveryTest fails on trunk. (fpj via breed) - - ZOOKEEPER-983. running zkServer.sh start remotely using ssh hangs (phunt) - - ZOOKEEPER-976. ZooKeeper startup script doesn't use JAVA_HOME (phunt) - - ZOOKEEPER-994 "eclipse" target in the build script doesnot include - libraray required for test classes in the classpath (MIS via phunt) - - ZOOKEEPER-1013 zkServer.sh usage message should mention all startup options - (eugene koontz via mahadev) - - ZOOKEEPER-1007. iarchive leak in C client (jeremy stribling via mahadev) - - ZOOKEEPER-993. Code improvements (MIS via fpj) - - ZOOKEEPER-1012. support distinct JVMFLAGS for zookeeper server in zkServer.sh - and zookeeper client in zkCli.sh (Eugene Koontz via breed) - - ZOOKEEPER-880. QuorumCnxManager$SendWorker grows without bounds (vishal via breed) - - ZOOKEEPER-1018. The connection permutation in get_addrs uses a weak and inefficient - shuffle (Stephen Tyree via breed) - - ZOOKEEPER-1028. In python bindings, zookeeper.set2() should return a stat dict but - instead returns None. (Chris Medaglia and Ivan Kelly via mahadev) - - ZOOKEEPER-975. new peer goes in LEADING state even if ensemble is online. (vishal via fpj) - - ZOOKEEPER-1049. Session expire/close flooding renders heartbeats to delay significantly. - (chang song via mahadev) - - ZOOKEEPER-1033. c client should install includes into INCDIR/zookeeper, not INCDIR/c-client-src - (Nicholas Harteau via mahadev) - - ZOOKEEPER-1061. Zookeeper stop fails if start called twice. (Ted Dunning via mahadev) - - ZOOKEEPER-1059. stat command isses on non-existing node causes NPE. (Bhallamudi Kamesh via mahadev) - - ZOOKEEPER-1058. fix typo in opToString for getData. (camille) - - ZOOKEEPER-1046. Creating a new sequential node results in a ZNODEEXISTS error. (Vishal K via camille) - - ZOOKEEPER-1069. Calling shutdown() on a QuorumPeer too quickly can lead to a corrupt log. (Vishal K via camille) - - ZOOKEEPER-1083. Javadoc for WatchedEvent not being generated. (Ivan Kelly via michim) - - ZOOKEEPER-1086. zookeeper test jar has non mavenised dependency. (Ivan Kelly via michim) - - ZOOKEEPER-335. zookeeper servers should commit the new leader txn to their logs. (breed) - - ZOOKEEPER-1081. modify leader/follower code to correctly deal with new leader (breed) - - ZOOKEEPER-1082. modify leader election to correctly take into account current epoch (fpj via breed) - - ZOOKEEPER-1060. QuorumPeer takes a long time to shutdown (Vishal via fpj) - - ZOOKEEPER-1087. ForceSync VM arguement not working when set to "no" (Nate Putnam via breed) - - ZOOKEEPER-1068. Documentation and default config suggest incorrect - location for Zookeeper state (Roman Shaposhnik via phunt) - - ZOOKEEPER-1103. In QuorumTest, use the same "for ( .. try { break } - catch { } )" pattern in testFollowersStartAfterLeaders as in - testSessionMove. (Eugene Koontz via phunt) - ZOOKEEPER-1046. Creating a new sequential node results in a ZNODEEXISTS error. (breed via camille) - - ZOOKEEPER-1097. Quota is not correctly rehydrated on snapshot reload (camille via henryr) - - ZOOKEEPER-1046. Small fix: Creating a new sequential node results in a ZNODEEXISTS error. (Vishal K via camille) - - ZOOKEEPER-782. Incorrect C API documentation for Watches. (mahadev via breed) - - ZOOKEEPER-1063. Dubious synchronization in Zookeeper and ClientCnxnSocketNIO classes (Yanick Dufresne via breed) - - ZOOKEEPER-1124. Multiop submitted to non-leader always fails due to timeout (Marshall McMullen via breed) - - ZOOKEEPER-1111. JMXEnv uses System.err instead of logging - (Ivan Kelly via phunt) - - ZOOKEEPER-1027. chroot not transparent in zoo_create() (Thijs Terlouw via - mahadev) - - ZOOKEEPER-1109. Zookeeper service is down when SyncRequestProcessor meets - any exception. (Laxman via mahadev) - - ZOOKEEPER-1134. ClientCnxnSocket string comparison using == rather than equals. - (phunt via mahadev) - - ZOOKEEPER-1119. zkServer stop command incorrectly reading comment lines in - zoo.cfg (phunt via mahadev) - - ZOOKEEPER-1090. Race condition while taking snapshot can lead to not restoring data tree correctly (Vishal K via breed) - - ZOOKEEPER-1138. release audit failing for a number of new files. (phunt via mahadev) - - ZOOKEEPER-1139. jenkins is reporting two warnings, fix these (phunt via mahadev) - - ZOOKEEPER-1142. incorrect stat output (phunt via mahadev) - - ZOOKEEPER-1144. ZooKeeperServer not starting on leader due to a race condition (Vishal K via camille) - - ZOOKEEPER-839. deleteRecursive does not belong to the other methods. - (mahadev) - - ZOOKEEPER-1146. significant regression in client (c/python) performance. - (phunt via mahadev) - - ZOOKEEPER-1150. fix for this patch to compile on windows. (dheeraj - via mahadev) - - ZOOKEEPER-1055. check for duplicate ACLs in addACL() and create(). - (Eugene Koontz via mahadev) - - ZOOKEEPER-1141. zkpython fails tests under python 2.4. (phunt via mahadev) - - ZOOKEEPER-1025. zkCli is overly sensitive to to spaces. (Laxman via camille) - - ZOOKEEPER-1117. zookeeper 3.3.3 fails to build with gcc >= 4.6.1 on - Debian/Ubuntu (James Page via mahadev) - - ZOOKEEPER-1140. server shutdown is not stopping threads. (laxman via mahadev) - - ZOOKEEPER-1051. SIGPIPE in Zookeeper 0.3.* when send'ing after - cluster disconnection (Stephen Tyree via mahadev) - - ZOOKEEPER-1168. ZooKeeper fails to run with IKVM (Andrew Finnell via phunt) - - ZOOKEEPER-1165. better eclipse support in tests (Warren Turkal via phunt) - - ZOOKEEPER-1154. Data inconsistency when the node(s) with the highest zxid is not present at the time of leader election. (Vishal Kathuria via camille) - - ZOOKEEPER-1156. Log truncation truncating log too much - can cause data loss. (Vishal Kathuria via camille) - - ZOOKEEPER-1160. test timeouts are too small (breed via phunt) - - ZOOKEEPER-731. Zookeeper#delete , #create - async versions miss a verb in the javadoc. (Thomas Koch via camille) - - ZOOKEEPER-1108. Various bugs in zoo_add_auth in C. (Dheeraj Agrawal via mahadev) - - ZOOKEEPER-981. Hang in zookeeper_close() in the multi-threaded C client. - (Jeremy Stribling via mahadev) - - ZOOKEEPER-1125. Intermittent java core test failures. (Vishar Kher via mahadev) - - ZOOKEEPER-961. Watch recovery after disconnection when connection string contains a prefix. - (Matthias Spycher via mahadev) - - ZOOKEEPER-1136. NEW_LEADER should be queued not sent to match the Zab 1.0 protocol - on the twiki (breed via mahadev) - - ZOOKEEPER-1189. For an invalid snapshot file(less than 10bytes size) RandomAccessFile - stream is leaking. (Rakesh R via mahadev) - - ZOOKEEPER-1185. Send AuthFailed event to client if SASL authentication fails. - (Eugene Kuntz via mahadev) - - ZOOKEEPER-1174. FD leak when network unreachable (Ted Dunning via camille) - - ZOOKEEPER-1203. Zookeeper systest is missing Junit Classes - (Prashant Gokhale via phunt) - - ZOOKEEPER-1206. Sequential node creation does not use always use - digits in node name given certain Locales. (Mark Miller via phunt) - - ZOOKEEPER-1212. zkServer.sh stop action is not conformat with LSB - para 20.2 Init Script Actions (Roman Shaposhnik via phunt) - - ZOOKEEPER-1190. ant package is not including many of the bin scripts - in the package (zkServer.sh for example) (Eric Yang via phunt) - - ZOOKEEPER-1181. Fix problems with Kerberos TGT renewal. - (Eugene Koontz via mahadev) - - ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing intermittently. (phunt via camille) - - ZOOKEEPER-1268. problems with read only mode, intermittent test failures - and ERRORs in the log. (phunt via mahadev) - - ZOOKEEPER-1246. Dead code in PrepRequestProcessor catch Exception block. (camille) - - ZOOKEEPER-1271. testEarlyLeaderAbandonment failing on solaris - - clients not retrying connection (mahadev via phunt) - - ZOOKEEPER-1192. Leader.waitForEpochAck() checks waitingForNewEpoch instead - of checking electionFinished (Alexander Shraer via mahadev) - - ZOOKEEPER-1270. testEarlyLeaderAbandonment failing intermittently, - quorum formed, no serving. (Flavio, Camille and Alexander Shraer via mahadev) - - ZOOKEEPER-1264. FollowerResyncConcurrencyTest failing - intermittently. (breed, camille and Alex Shraer via camille) - - ZOOKEEPER-1282. Learner.java not following Zab 1.0 protocol - - setCurrentEpoch should be done upon receipt of NEWLEADER - (before acking it) and not upon receipt of UPTODATE (breed via camille) - - ZOOKEEPER-1291. AcceptedEpoch not updated at leader before it proposes the epoch to followers. (Alex Shraer via camille) - - ZOOKEEPER-1208. Ephemeral node not removed after the client session is long gone. (phunt via camille) - - ZOOKEEPER-1239. add logging/stats to identify fsync stalls. (phunt via camille) - - ZOOKEEPER-1299. Add winconfig.h file to ignore in release audit. (mahadev) - -IMPROVEMENTS: - ZOOKEEPER-724. Improve junit test integration - log harness information - (phunt via mahadev) - - ZOOKEEPER-766. forrest recipes docs don't mention the lock/queue recipe - implementations available in the release (phunt via mahadev) - - ZOOKEEPER-769: Leader can treat observers as quorum members - (Sergey Doroshenko via henryr) - - ZOOKEEPER-788: Add server id to message logs - (Ivan Kelly via flavio) - - ZOOKEEPER-789. Improve FLE log messages (flavio via phunt) - - ZOOKEEPER-798. Fixup loggraph for FLE changes (Ivan Kelly via phunt) - - ZOOKEEPER-797 c client source with AI_ADDRCONFIG cannot be compiled with - early glibc (Qian Ye via phunt) - - ZOOKEEPER-790. Last processed zxid set prematurely while establishing leadership - (fpj via breed) - - ZOOKEEPER-821. Add ZooKeeper version information to zkpython (Rich - Schumacher via mahadev) - - ZOOKEEPER-765. Add python example script (Travis and Andrei via mahadev) - - ZOOKEEPER-809. Improved REST Interface (Andrei Savu via phunt) - - ZOOKEEPER-733. use netty to handle client connections (breed and phunt) - - ZOOKEEPER-853. Make zookeeper.is_unrecoverable return True or False - in zkpython (Andrei Savu via henryr) - - ZOOKEEPER-864. Hedwig C++ client improvements (Ivan Kelly via breed) - - ZOOKEEPER-862. Hedwig created ledgers with hardcoded Bookkeeper ensemble and - quorum size. Make these a server config parameter instead. (Erwin T via breed) - - ZOOKEEPER-926. Fork Hadoop common's test-patch.sh and modify for Zookeeper. - (nigel) - - ZOOKEEPER-909. Extract NIO specific code from ClientCnxn - (Thomas Koch via phunt) - - ZOOKEEPER-908. Remove code duplication and inconsistent naming in - ClientCnxn.Packet creation (Thomas Koch via phunt) - - ZOOKEEPER-836. hostlist as string. (Thomas Koch via breed) - - ZOOKEEPER-921. zkPython incorrectly checks for existence of required - ACL elements (Nicholas Knight via henryr) - - ZOOKEEPER-963. Make Forrest work with JDK6 (Carl Steinbach via henryr) - - ZOOKEEPER-500. Async methods shouldnt throw exceptions (fpj via breed) - - ZOOKEEPER-977. passing null for path_buffer in zoo_create (breed via mahadev) - - ZOOKEEPER-465. Ledger size in bytes. (flavio via mahadev) - - ZOOKEEPER-980. allow configuration parameters for log4j.properties - (phunt via mahadev) - - ZOOKEEPER-1042. Generate zookeeper test jar for maven installation - (ivan kelly via breed) - - ZOOKEEPER-1030: Increase default for maxClientCnxns - (Todd Lipcon via breed/mahadev/phunt) - - ZOOKEEPER-850: Switch from log4j to slf4j (Olaf Krische via michim) - - ZOOKEEPER-874. FileTxnSnapLog.restore does not call listener (diogo via fpj) - - ZOOKEEPER-1052. Findbugs warning in QuorumPeer.ResponderThread.run() (fpj via michim) - - ZOOKEEPER-1094. Small improvements to LeaderElection and Vote classes (henryr via breed) - - ZOOKEEPER-1074. zkServer.sh is missing nohup/sleep, which are necessary - for remote invocation. (phunt via mahadev) - - ZOOKEEPER-965. Need a multi-update command to allow multiple znodes to be updated safely (Marshall McMullen and Ted Dunning via breed) - - ZOOKEEPER-1073. address a documentation issue in ZOOKEEPER-1030. (phunt via mahadev) - - ZOOKEEPER-1095. Simple leader election recipe - (Eric Sammer via henry and phunt) - - ZOOKEEPER-1076. some quorum tests are unnecessarily extending QuorumBase (phunt via mahadev) - - ZOOKEEPER-1143. quorum send & recv workers are missing thread names - (phunt via mahadev) - - ZOOKEEPER-1104. CLONE - In QuorumTest, use the same "for ( .. try { break } - catch { } )" pattern in testFollowersStartAfterLeaders as in testSessionMove. - (Eugene Koontz via mahadev) - - ZOOKEEPER-1034. perl bindings should automatically find the zookeeper - c-client headers (nicholas harteau via mahadev) - - ZOOKEEPER-1166. Please add a few svn:ignore properties (via phunt) - - ZOOKEEPER-1169. Fix compiler (eclipse) warnings in (generated) jute - code (Thomas Koch via phunt) - - ZOOKEEPER-1171. fix build for java 7 (phunt via mahadev) - - ZOOKEEPER-1201. Clean SaslServerCallbackHandler.java. (Thomas Koch - via mahadev) - - ZOOKEEPER-1321. Add number of client connections metric in JMX and srvr. (Neha Narkhede via camille) - -NEW FEATURES: - ZOOKEEPER-729. Java client API to recursively delete a subtree. - (Kay Kay via henry) - - ZOOKEEPER-747. Add C# generation to Jute (Eric Hauser via phunt) - - ZOOKEEPER-464. Need procedure to garbage collect ledgers - (erwin via fpj) - - ZOOKEEPER-773. Log visualisation (Ivan Kelly via phunt) - - ZOOKEEPER-744. Add monitoring four-letter word (Andrei Savu via phunt) - - ZOOKEEPER-712. Bookie recovery. (erwin tam via breed) - - ZOOKEEPER-799. Add tools and recipes for monitoring as a contrib - (Andrei Savu via phunt) - - ZOOKEEPER-808. Web-based Administrative Interface - (Andrei Savu via phunt) - - ZOOKEEPER-775. A large scale pub/sub system (Erwin, Ivan and Ben via - mahadev) - - ZOOKEEPER-1020. Implement function in C client to determine which host you're currently connected to. (stephen tyree via breed) - - ZOOKEEPER-1038. Move bookkeeper and hedwig code in subversion (breed) - - ZOOKEEPER-784. Server-side functionality for read-only mode (Sergey Doroshenko via henryr) - - ZOOKEEPER-992. MT Native Version of Windows C Client (Dheeraj Agrawal via michim) - - ZOOKEEPER-938. Support Kerberos authentication of clients. (Eugene Koontz - via mahadev) - - ZOOKEEPER-1152. Exceptions thrown from handleAuthentication can cause buffer corruption issues in NIOServer. (camille via breed) - - ZOOKEEPER-999. Create an package integration project (Eric Yang via phunt) - - ZOOKEEPER-1107. automating log and snapshot cleaning (Laxman via phunt) - -DEPRECATION: - ZOOKEEPER-1153. Deprecate AuthFLE and LE. (Flavio Junqueira via mahadev) - -Release 3.3.0 - 2010-03-24 - -Non-backward compatible changes: - -BUGFIXES: - -Backward compatible changes: - -BUGFIXES: - ZOOKEEPER-59. Synchronized block in NIOServerCnxn (fpj via breed) - - ZOOKEEPER-524. DBSizeTest is not really testing anything (breed) - - ZOOKEEPER-469. make sure CPPUNIT_CFLAGS isn't overwritten - (chris via mahadev) - - ZOOKEEPER-471. update zkperl for 3.2.x branch (chris via mahadev) - - ZOOKEEPER-470. include unistd.h for sleep() in c tests (chris via mahadev) - - ZOOKEEPR-460. bad testRetry in cppunit tests (hudson failure) - (giri via mahadev) - - ZOOKEEPER-467. Change log level in BookieHandle. (flavio via mahadev) - - ZOOKEEPER-482. ignore sigpipe in testRetry to avoid silent immediate - failure. (chris via mahadev) - - ZOOKEEPER-487. setdata on root (/) crashes the servers (mahadev via phunt) - - ZOOKEEPER-457. Make ZookeeperMain public, support for HBase (and other) - embedded clients (ryan rawson via phunt) - - ZOOKEEPER-481. Add lastMessageSent to QuorumCnxManager. (flavio via mahadev) - - ZOOKEEPER-479. QuorumHierarchical does not count groups correctly - (flavio via mahadev) - - ZOOKEEPER-466. crash on zookeeper_close() when using auth with empty cert - (Chris Darroch via phunt) - - ZOOKEEPER-480. FLE should perform leader check when node is not leading and - add vote of follower (flavio via mahadev) - - ZOOKEEPER-491. Prevent zero-weight servers from being elected. - (flavio via mahadev) - - ZOOKEEPER-447. zkServer.sh doesn't allow different config files to be - specified on the command line (henry robinson via phunt) - - ZOOKEEPER-493. patch for command line setquota (steve bendiola via phunt) - - ZOOKEEPER-311. handle small path lengths in zoo_create() - (chris barroch via breed) - - ZOOKEEPER-484. Clients get SESSION MOVED exception when switching from - follower to a leader. (mahadev) - - ZOOKEEPER-490. the java docs for session creation are misleading/incomplete - (phunt) - - ZOOKEEPER-501. CnxManagerTest failed on hudson. (flavio via mahadev) - - ZOOKEEPER-499. electionAlg should default to FLE (3) - regression - (phunt via mahadev) - - ZOOKEEPER-477. zkCleanup.sh is flaky (fernando via mahadev) - - ZOOKEEPER-498. Unending Leader Elections : WAN configuration - (flavio via mahadev) - - ZOOKEEPER-508. proposals and commits for DIFF and Truncate messages from the - leader to the followers is buggy. (mahadev and ben via mahadev) - - ZOOKEEPER-518. DEBUG message for outstanding proposals in leader should be - moved to trace. (phunt) - - ZOOKEEPER-533. ant error running clean twice (phunt via mahadev) - - ZOOKEEPER-535. ivy task does not enjoy being defined twice - (build error) (phunt via mahadev) - - ZOOKEEPER-420. build/test should not require install in zkpython - (henry robinson via phunt) - - ZOOKEEPER-538. zookeeper.async causes python to segfault - (henry robinson via phunt) - - ZOOKEEPER-542. c-client can spin when server unresponsive (Christian - Wiedmann via mahadev) - - ZOOKEEEPER-510. zkpython lumps all exceptions as IOError, needs specialized - exceptions for KeeperException types (henry & pat via mahadev) - - ZOOKEEPER-541. zkpython limited to 256 handles (henry robinson via phunt) - - ZOOKEEPER-554. zkpython can segfault when statting a deleted node - (henry robinson via phunt) - - ZOOKEEPER-512. FLE election fails to elect leader (flavio via mahadev) - - ZOOKEEPER-563. ant test for recipes is broken. (mahadev via phunt) - - ZOOKEEPER-562. c client can flood server with pings if tcp send queue - filled. (ben reed via mahadev) - - ZOOKEEPER-537. The zookeeper jar includes the java source files - (Thomas Dudziak via phunt) - - ZOOKEEPER-551. unnecessary SetWatches message on new session. - (phunt via flavio) - - ZOOKEEPER-566. "reqs" four letter word (command port) returns no information - (phunt via breed) - - ZOOKEEPER-567. javadoc for getchildren2 needs to mention "new in 3.3.0" - (phunt via breed) - - ZOOKEEPER-547. Sanity check in QuorumCnxn Manager and quorum communication - port. (mahadev via breed) - - ZOOKEEPER-532. java compiler should be target Java 1.5 - (hiram chirino and phunt via breed) - - ZOOKEEPER-519. Followerhandler should close the socket if it gets an exception - on a write. (mahadev via breed) - - ZOOKEEPER-570. AsyncHammerTest is broken, callbacks need to validate rc - parameter (phunt via breed) - - ZOOKEEPER-3. syncLimit has slightly different comments in the class header, - and > inline with the variable. (mahadev via breed) - - ZOOKEEPER-576. docs need to be updated for session moved exception and how - to handle it (breed via phunt) - - ZOOKEEPER-582. ZooKeeper can revert to old data when a snapshot is created - outside of normal processing (ben reed and mahadev via mahadev) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (Patrick Hunt via mahadev) - - ZOOKEEPER-598. LearnerHandler is misspelt in the thread's constructor - (Henry Robinson via fpj) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk (take 2) - (breed) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (take 3) (phunt via mahadev) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (take 4) (breed via mahadev) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (take 5) (mahadev) - - ZOOKEEPER-611. hudson build failiure (mahadev) - - ZOOKEEPER-611. hudson build failure (take 2) (mahadev) - - ZOOKEEPER-615. wrong javadoc for create with a sequence flag - (mahadev via breed) - - ZOOKEEPER-588. remove unnecessary/annoying log of tostring error in - Request.toString() (phunt via breed) - - ZOOKEEPER-587. client should log timeout negotiated with server - (phunt via mahadev) - - ZOOKEEPER-610. cleanup final fields, esp those used for locking - (phunt via henry) - - ZOOKEEPER-614. Improper synchronisation in getClientCnxnCount - (henry via mahadev) - - ZOOKEEPER-609. ObserverTest failure "zk should not be connected expected not - same" (henry robinson via phunt) - - ZOOKEEPER-630. Trunk has duplicate ObserverTest.java files - (henry robinson via phunt) - - ZOOKEEPER-627. zkpython arbitrarily restricts the size of a 'get' to 512 - bytes (henry robinson via mahadev) - - ZOOKEEPER-534. The test target in contib/bookkeeper does not depend on jar - target. (phunt via mahadev) - - ZOOKEEPER-623. ClientBase in bookkeeper.util requires junit (fpj via breed) - - ZOOKEEPER-600. TODO pondering about allocation behavior in zkpython may be - removed (gustavo via mahadev) - - ZOOKEEPER-596. The last logged zxid calculated by zookeeper servers could - cause problems in leader election if data gets corrupted. (mahadev) - - ZOOKEEPER-637. Trunk build is failing (fpj via breed) - - ZOOKEEPER-637. Trunk build is failing - second patch (breed via fpj) - - ZOOKEEPER-644. Nightly build failed on hudson. (pat via mahadev) - - ZOOKEEPER-651: Log exception trace in QuorumCnxManager.SendWorker - (flavio via henry) - - ZOOKEEPER-608. Receipt of ACK from observer should not be logged as ERROR - (henry via mahadev) - - ZOOKEEPER-647. hudson failure in testLeaderShutdown (flavio via mahadev) - - ZOOKEEPER-574. the documentation on snapcount in the admin guide has the - wrong default (phunt via mahadev) - - ZOOKEEPER-656. SledgeHammer test - thread.run() deprecated (kay kay via mahadev) - - ZOOKEEPER-413. two flaws need addressing in the c tests that can cause false - positive failures (phunt via mahadev) - - ZOOKEEPER-495. c client logs an invalid error when zookeeper_init is called - with chroot (phunt via mahadev) - - ZOOKEEPER-589. When create a znode, a NULL ACL parameter cannot be accepted. - (breed via mahadev) - - ZOOKEEPER-673. Fix observer documentation regarding leader election (flavio - via mahadev) - - ZOOKEEPER-672. typo nits across documentation (Kay Kay via mahadev) - - ZOOKEEPER-668. Close method in LedgerInputStream doesn't do anything (flavio - via mahadev) - - ZOOKEEPER-569. Failure of elected leader can lead to never-ending leader - election (henry via flavio) - - ZOOKEEPER-669. watchedevent tostring should clearly output the - state/type/path (phunt via mahadev) - - ZOOKEEPER-683. LogFormatter fails to parse transactional log files (phunt - via mahadev) - - ZOOKEEPER-682. Event is not processed when the watcher is set to watch "/" - if chrooted (Scott Wang via mahadev) - - ZOOKEEPER-687. LENonterminatetest fails on some machines. (mahadev) - - ZOOKEEPER-681. Minor doc issue re unset maxClientCnxns (phunt via mahadev) - - ZOOKEEPER-622. Test for pending watches in send_set_watches should be moved - (ben and steven via mahadev) - - ZOOKEEPER-689. release build broken - ivysettings.xml not copied during - "package" (phunt via mahadev) - - ZOOKEEPER-59. Synchronized block in NIOServerCnxn (flavio via mahadev) - - ZOOKEEPER-691. Interface changed for NIOServer.Factory (breed via mahadev) - - ZOOKEEPER-685. Race in LENonTerminateTest (henry via breed) - - ZOOKEEPER-677. c client doesn't allow ipv6 numeric connect string - (breed & phunt & mahadev via breed) - - ZOOKEEPER-693. TestObserver stuck in tight notification loop in FLE - (flavio via phunt) - - ZOOKEEPER-696. NPE in the hudson logs, seems nioservercnxn closed twice - (phunt via mahadev) - - ZOOKEEPER-511. bad error handling in FollowerHandler.sendPackets - (mahadev via flavio) - - ZOOKEEPER-604. zk needs to prevent export of any symbol not listed in their - api (mahadev) - - ZOOKEEPER-121. SyncRequestProcessor is not closing log stream during - shutdown (mahadev) - - ZOOKEEPER-698. intermittent JMX test failures due to not verifying QuorumPeer - shutdown (phunt) - - ZOOKEEPER-121_2. SyncRequestProcessor is not closing log stream during - shutdown (breed via mahadev) - - ZOOKEEPER-121_3. SyncRequestProcessor is not closing log stream during - shutdown (mahadev via phunt) - - ZOOKEEPER-121_4. SyncRequestProcessor is not closing log stream during - shutdown (mahadev via breed) - - ZOOKEEPER-586. c client does not compile under cygwin (phunt, mahadev, breed via breed) - - ZOOKEEPER-624. The C Client cause core dump when receive error data from - Zookeeper Server (mahadev) - - ZOOKEEPER-591. The C Client cannot exit properly in some situation (mahadev) - - ZOOKEEPER-591_2. The C Client cannot exit properly in some situation - (mahadev via phunt) - - ZOOKEEPER-709. bookkeeper build failing with missing factory - (phunt) - - ZOOKEEPER-708. zkpython failing due to undefined symbol - deallocate_String_vector (mahadev via phunt) - - ZOOKEEPER-436. Bookies should auto register to ZooKeeper (erwin tam & fpj via breed) - - ZOOKEEPER-710. permanent ZSESSIONMOVED error after client app reconnects to zookeeper cluster (phunt via breed) - - ZOOKEEPER-718. the fatjar is missing libraries (ben via mahadev) - - ZOOKEEPER-717. add a preferred list to the instancemanager (breed via - mahadev) - -IMPROVEMENTS: - ZOOKEEPER-473. cleanup junit tests to eliminate false positives due to - "socket reuse" and failure to close client (phunt via mahadev) - - ZOOKEEPER-488. Fix zkServer.sh to add clover.jar in classpath - (Giridharan Kesavan via gkesavan) - - ZOOKEEPER-516. add support for 10 minute test ie "pre-commit" test (phunt) - - ZOOKEEPER-529. Use Ivy to pull dependencies and also generate pom (phunt - via mahadev) - - ZOOKEEPER-530. Memory corruption: Zookeeper c client IPv6 implementation - does not honor struct sockaddr_in6 size (isabel drost via mahadev) - - ZOOKEEPER-549. Refactor Followers and related classes into a Peer->Follower - hierarchy in preparation for Observers (henry robinson via mahadev) - - ZOOKEEPER-472. Making DataNode not instantiate a HashMap when the node is - ephmeral (Erik Holstad via mahadev) - - ZOOKEEPER-425. Add OSGi metadata to zookeeper.jar (david bosschaert via breed) - - ZOOKEEPER-599. Changes to FLE and QuorumCnxManager to support Observers - (fpj via breed) - - ZOOKEEPER-506. QuorumBase should use default leader election (fpj via breed) - - ZOOKEEPER-633. Fetch netty using ivy for bookkeeper (giri via fpj) - - ZOOKEEPER-544. improve client testability - allow test client to access - connected server location (phunt via breed) - - ZOOKEEPER-426. Windows versions of zookeeper scripts - (David Bosschaert via breed) - - ZOOKEEPER-638. upgrade ivy to 2.1.0 final from 2.1.0 release - candidate (phunt via breed) - - ZOOKEEPER-648. Fix releaseaudit warning count to zero (phunt via henry) - - ZOOKEEPER-626. ensure the c/java cli's print xid/sessionid/etc... in hex - (pat via mahadev) - - ZOOKEEPER-655. StringBuffer -> StringBuilder - conversion of references as - necessary (Kay Kay via henry) - - ZOOKEEPER-612. Make Zookeeper C client can be compiled by gcc of early - version (qian via mahadev) - - ZOOKEEPER-456. CREATOR_ALL_ACL has unnecessary PERMS.ADMIN in the - declartion. (phunt via mahadev) - - ZOOKEEPER-593. java client api does not allow client to access negotiated - session timeout (phunt via mahadev) - - ZOOKEEPER-507. BookKeeper client re-write (Utkarsh and ben via mahadev) - - ZOOKEEPER-665. Add BookKeeper streaming documentation (flavio via mahadev) - - ZOOKEEPER-664. BookKeeper API documentation (flavio via mahadev) - - ZOOKEEPER-607. improve bookkeeper overview (flavio via mahadev) - - ZOOKEEPER-485. Need ops documentation that details supervision of ZK server - processes. (phunt via mahadev) - - ZOOKEEPER-658. update forrest docs - AuthFLE no longer supported (flavio via - mahadev) - - ZOOKEEPER-640. make build.xml more configurable to ease packaging for linux - distros (phunt via mahadev) - - ZOOKEEPER-579. zkpython needs more test coverage for ACL code paths (henry - via mahadev) - - ZOOKEEPER-688. explain session expiration better in the docs & faq (phunt - via mahadev) - - ZOOKEEPER-663. hudson failure in ZKDatabaseCorruptionTest (mahadev via henryr) - - ZOOKEEPER-543. Tests for ZooKeeper examples (steven via mahadev) - - ZOOKEEPER-692. upgrade junit to latest version (4.8.1) (phunt via mahadev) - - ZOOKEEPER-601. allow configuration of session timeout min/max bounds (phunt - via mahadev) - -NEW FEATURES: - ZOOKEEPER-539. generate eclipse project via ant target. (phunt via mahadev) - - ZOOKEEPER-555. Add stat information to GetChildrenResponse. (Arni Jonson and - phunt via mahadev) - - ZOOKEEPER-550. Java Queue Recipe. (steven cheng via mahadev) - - ZOOKEEPER-368. Observers: core functionality (henry robinson via mahadev) - - ZOOKEEPER-496. zookeeper-tree utility for export, import and incremental - updates (anirban roy via breed) - - ZOOKEEPER-572. add ability for operator to examine state of watches - currently registered with a server (phunt via mahadev) - - ZOOKEEPER-678. Gui browser application to view and edit the contents of a - zookeeper instance (Colin Goodheart-Smithe via phunt) - - ZOOKEEPER-635. Server supports listening on a specified network address (phunt via breed) - -Release 3.2.0 - 2009-06-30 - -Non-backward compatible changes: - -BUGFIXES: - ZOOKEEPER-444. perms definition for PERMS_ALL differ in C and java (mahadev) - -Backward compatible changes: - -BUGFIXES: - ZOOKEEPER-303. Bin scripts dont work on a Mac. (tom white via mahadev) - - ZOOKEEPER-330. zookeeper standalone server does not startup with just a - port and datadir. (chris darroch and mahadev) - - ZOOKEEPER-319. add locking around auth info in zhandle_t. - (chris darroch via mahadev) - - ZOOKEEPER-320. call auth completion in free_completions(). - (chris darroch via mahadev) - - ZOOKEEPER-334. bookkeeper benchmark (testclient.java) has compiling errors. - (flavio and mahadev) - - ZOOKEEPER-281. autoreconf fails for /zookeeper-3.0.1/src/c/ (phunt) - - ZOOKEEPER-318. remove locking in zk_hashtable.c or add locking in - collect_keys() (chris darroch via mahadev) - - ZOOKEEPER-333. helgrind thread issues identified in mt c client code - (mahadev via phunt) - - ZOOKEEPER-309. core dump using zoo_get_acl() (mahadev via phunt) - - ZOOKEEPER-341. regression in QuorumPeerMain, - tickTime from config is lost, cannot start quorum (phunt via mahadev) - - ZOOKEEPER-360. WeakHashMap in Bookie.java causes NPE (flavio via mahadev) - - ZOOKEEPER-362. Issues with FLENewEpochTest. (fix bug in Fast leader election) - (flavio via mahadev) - - ZOOKEEPER-363. NPE when recovering ledger with no hint. (flavio via mahadev) - - ZOOKEEPER-370. Fix critical problems reported by findbugs. - (flavio via mahadev) - - ZOOKEEPER-347. zkfuse uses non-standard String. (patrick hunt via mahadev) - - ZOOKEEPER-355. make validatePath non public in Zookeeper client api. - (phunt via mahadev) - - ZOOKEEPER-374. Uninitialized struct variable in C causes warning which - is treated as an error (phunt via mahadev) - - ZOOKEEPER-337. improve logging in leader election lookForLeader method when - address resolution fails (phunt via mahadev) - - ZOOKEEPER-367. RecoveryTest failure - "unreasonable length" IOException - (mahadev via phunt) - - ZOOKEEPER-346. remove the kill command fro mthe client port. - (phunt via mahadev) - - ZOOKEEPER-377. running ant cppunit tests, a failure still results in - BUILD SUCCESSFUL (giri via mahadev) - - ZOOKEEPER-382. zookeeper cpp tests fails on 64 bit machines with gcc 4.1.2 - (mahadev via phunt) - - ZOOKEEPER-365. javadoc is wrong for setLast in LedgerHandle - (flavio via phunt) - - ZOOKEEPER-392. Change log4j properties in bookkeeper. (flavio via mahadev) - - ZOOKEEPER-400. Issues with procedure to close ledger. (flavio) - - ZOOKEEPER-405. nullpointer exception in zookeeper java shell. - (mahadev via breed) - - ZOOKEEPER-410. address all findbugs warnings in client/server classes. - (phunt via breed) - - ZOOKEEPER-403. cleanup javac compiler warnings. (flavio via breed) - - ZOOKEEPER-407. address all findbugs warnings in - org.apache.zookeeper.server.quorum.** packages. - (flavio via breed) - - ZOOKEEPER-411. Building zookeeper fails on RHEL 5 64 bit during test-cppunit - (mahadev via phunt) - - ZOOKEEPER-402. zookeeper c library segfaults on data for a node in zookeeper - being null. (mahadev via phunt) - - ZOOKEEPER-415. zookeeper c tests hang. (mahadev via phunt) - - ZOOKEEPER-385. crctest failed on hudson patch test (mahadev via phunt) - - ZOOKEEPER-192. trailing whitespace in config file can cause number format - exceptions (phunt via breed) - - ZOOKEEPER-409. address all findbugs warnings in jute related classes - (phunt via breed) - - ZOOKEEPER-416. bookkeeper jar includes unnnecessary files. - (flavio via mahadev) - - ZOOKEEPER-419. Reference counting bug in Python bindings causes abort errors - (henry robinson via phunt) - - ZOOKEEPER-421. zkpython run_tests.sh is missing #! - (henry robinson via phunt) - - ZOOKEEPER-406. address all findbugs warnings in persistence classes. - (phunt et al via breed) - - ZOOKEEPER-435. allow "super" admin digest based auth to be configurable - (phunt via breed) - - ZOOKEEPER-375. zoo_add_auth only retains most recent auth on re-sync. - (mahadev) - - ZOOKEEPER-433. getacl on root znode (/) fails. (phunt via mahadev) - - ZOOKEEPER-408. address all findbugs warnings in persistence classes. - (phunt, mahadev, flavio via mahadev) - - ZOOKEEPER-427. ZooKeeper server unexpectedly high CPU utilisation - (Sergey Zhuravlev via breed) - - ZOOKEEPER-446. some traces of the host auth scheme left (breed via mahadev) - - ZOOKEEPER-438. addauth fails to register auth on new client that's not yet - connected (breed via mahadev) - - ZOOKEEPER-448. png files do nto work with forrest. (mahadev) - - ZOOKEEPER-417. stray message problem when changing servers - (breed via mahadev) - - ZOOKEEPER-449. sesssionmoved in java code and ZCLOSING in C have the same - value. (mahadev) - - ZOOKEEPER-452. zookeeper performance graph should have percentage of reads - rather than percentage of writes - zkperfRW-3.2.jpg (mahadev) - - ZOOKEEPER-450. emphemeral cleanup not happening with session timeout. - (breed via mahadev) - - ZOOKEEPER-453. Worker is not removed in QuorumCnxManager upon crash. - (flavio via mahadev) - - ZOOKEEPER-454. allow compilation with jdk1.5 (phunt) - - ZOOKEEPER-455. zookeeper c client crashes with chroot specified in the string. - (phunt via mahadev) - - ZOOKEEPER-468. avoid compile warning in send_auth_info(). - -IMPROVEMENTS: - ZOOKEEPER-308. improve the atomic broadcast performance 3x. - (breed via mahadev) - - ZOOKEEPER-326. standalone server ignores tickTime configuration. - (chris darroch via mahadev) - - ZOOKEEPER-279. Allow specialization of quorum config parsing - (e.g. variable expansion in zoo.cfg) (Jean-Daniel Cryans via phunt) - - ZOOKEEPER-351. to run checkstyle (giridharan kesavan via mahadev) - - ZOOKEEPER-350. to run rats for releaseaudit. - (giridharan kesavan via mahadev) - - ZOOKEEPER-352. to add standard ant targets required by test-patch.sh script - (giridharan kesavan via mahadev) - - ZOOKEEPER-353. javadoc warnings needs to be fixed. - (giridharan kesavan via mahadev) - - ZOOKEEPER-354. to fix javadoc warning in the source files. (mahadev) - - ZOOKEEPER-349. to automate patch testing. (giridharan kesavan via mahadev) - - ZOOKEEPER-288. Cleanup and fixes to BookKeeper (flavio via mahadev) - - ZOOKEEPER-305. Replace timers with semaphores in FLENewEpochTest. - (flavio via mahadev) - - ZOOKEEPER-60. Get cppunit tests running as part of Hudson CI. - (girish via mahadev) - - ZOOKEEPER-343. add tests that specifically verify the zkmain and - qpmain classes. (phunt via mahadev) - - ZOOKEEPER-361. integrate cppunit testing as part of hudson patch process. - (giri via mahadev) - - ZOOKEEPER-373. One thread per bookie (flavio via mahadev) - - ZOOKEEPER-384. keeper exceptions missing path (phunt via mahadev) - - ZOOKEEPER-380. bookkeeper should have a streaming api so that its easier to - store checpoints/snapshots in bookkeeper. (mahadev via flavio) - - ZOOKEEPER-389. add help/usage to the c shell cli.c (phunt via mahadev) - - ZOOKEEPER-376. ant test target re-compiles cppunit code every time - (phunt via mahadev) - - ZOOKEEPER-391. bookeeper mainline code should not be calling - printStackTrace. (flavio via mahadev) - - ZOOKEEPER-300. zk jmx code is calling printStackTrace when creating bean - name (should not be) (phunt via mahadev) - - ZOOKEEPER-94. JMX tests are needed to verify that the JMX MBeans work - properly (phunt via mahadev) - - ZOOKEEPER-404. nightly build failed on hudson. - (henry robinson and pat via mahadev) - - ZOOKEEPER-345. the CLIs should allow addAuth to be invoked. - (henry robinson via breed) - - ZOOKEEPER-292. commit configure scripts (autotools) to svn for c projects and - include in release (phunt via breed) - - ZOOKEEPER-383. Asynchronous version of createLedger(). (flavio via mahadev) - - ZOOKEEPER-358. Throw exception when ledger does not exist. (flavio via breed) - - ZOOKEEPER-431. Expose methods to ease ZK integration. (Jean-Daniel via breed) - - ZOOKEEPER-396. race condition in zookeeper client library between - zookeeper_close and zoo_synchronous api. (mahadev) - - ZOOKEEPER-196. doxygen comment for state argument of watcher_fn typedef and - implementation differ ("...one of the *_STATE constants, otherwise -1") - (breed via mahadev) - - ZOOKEEPER-336. single bad client can cause server to stop accepting - connections (henry robinson via breed) - - ZOOKEEPER-434. the java shell should indicate connection status on command - prompt (henry robinson via breed) - - ZOOKEEPER-437. Variety of Documentation Updates (grant via mahadev) - - ZOOKEEPER-443. trace logging in watch notification not wrapped with - istraceneabled - inefficient (pat via mahadev) - - ZOOKEEPER-432. Various improvements to zkpython bindings. - (henry via mahadev) - - ZOOKEEPER-428. logging should be makred as warn rathen than error in - NIOServerCnxn. (phunt via mahadev) - - ZOOKEEPER-422. Java CLI should support ephemeral and sequential node creation - (henry via breed) - - ZOOKEEPER-315. add forrest docs for bookkeeper. (flavio via mahadev) - - ZOOKEEPER-329. document how to integrate 3rd party authentication into ZK - server ACLs. (breed via mahadev) - - ZOOKEEPER-356. Masking bookie failure during writes to a ledger - (flavio via breed) - - ZOOKEEPER-327. document effects (latency) of storing large amounts of data - in znodes. (breed via mahadev) - - ZOOKEEPER-264. docs should include a state transition diagram for client - state (breed via mahadev) - - ZOOKEEPER-440. update the performance documentation in forrest - (breed via phunt) - -NEW FEATURES: - - ZOOKEEPER-371. jdiff documentation included in build/release (giri via phunt) - - ZOOKEEPER-78. added a high level protocol/feature - for easy Leader - Election or exclusive Write Lock creation (mahadev via phunt) - - ZOOKEEPER-29. Flexible quorums (flavio via mahadev) - - ZOOKEEPER-378. perl binding for zookeeper (chris darroch via mahadev) - - ZOOKEEPER-386. improve java cli shell. (henry robinson via mahadev) - - ZOOKEEPER-36. REST access to ZooKeeper (phunt via mahadev) - - ZOOKEEPER-395. Python bindings. (henry robinson via mahadev) - - ZOOKEEPER-237. Add a Chroot request (phunt and mahadev) - - -Release 3.1.0 - 2009-02-06 - -Non-backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-255. zoo_set() api does not return stat datastructure. - (avery ching via mahadev) - - ZOOKEEPER-246. review error code definition in both source and docs. - (pat via mahadev) - -Backward compatible changes: - -BUGFIXES: - ZOOKEEPER-211. Not all Mock tests are working (ben via phunt) - - ZOOKEEPER-223. change default level in root logger to INFO. - (pat via mahadev) - - ZOOKEEPER-212. fix the snapshot to be asynchronous. (mahadev and ben) - - ZOOKEEPER-213. fix programmer guide C api docs to be in sync with latest - zookeeper.h (pat via mahadev) - - ZOOKEEPER-219. fix events.poll timeout in watcher test to be longer. - (pat via mahadev) - - ZOOKEEPER-217. Fix errors in config to be thrown as Exceptions. (mahadev) - - ZOOKEEPER-228. fix apache header missing in DBTest. (mahadev) - - ZOOKEEPER-218. fix the error in the barrier example code. (pat via mahadev) - - ZOOKEEPER-206. documentation tab should contain the version number and - other small site changes. (pat via mahadev) - - ZOOKEEPER-226. fix exists calls that fail on server if node has null data. - (mahadev) - - ZOOKEEPER-204. SetWatches needs to be the first message after auth - messages to the server (ben via mahadev) - - ZOOKEEPER-208. Zookeeper C client uses API that are not thread safe, - causing crashes when multiple instances are active. - (austin shoemaker, chris daroch and ben reed via mahadev) - - ZOOKEEPER-227. gcc warning from recordio.h (chris darroch via mahadev) - - ZOOKEEPER-232. fix apache licence header in TestableZookeeper (mahadev) - - ZOOKEEPER-249. QuorumPeer.getClientPort() always returns -1. - (nitay joffe via mahadev) - - ZOOKEEPER-248. QuorumPeer should use Map interface instead of HashMap - implementation. (nitay joffe via mahadev) - - ZOOKEEPER-241. Build of a distro fails after clean target is run. - (patrick hunt via mahadev) - - ZOOKEEPER-245. update readme/quickstart to be release tar, rather than - source, based (patrick hunt via mahadev) - - ZOOKEEPER-251. NullPointerException stopping and starting Zookeeper servers - (mahadev via phunt) - - ZOOKEEPER-250. isvalidsnapshot should handle the case of 0 snapshot - files better. (mahadev via phunt) - - ZOOKEEPER-265. remove (deprecate) unused NoSyncConnected from KeeperState. - (phunt via mahadev) - - ZOOKEEPER-273. Zookeeper c client build should not depend on CPPUNIT. (pat -and runping via mahadev) - - ZOOKEEPER-268. tostring on jute generated objects can cause NPE. (pat via mahadev) - - ZOOKEEPER-267. java client incorrectly generating syncdisconnected event when in disconnected state. (pat via breed) - - ZOOKEEPER-263. document connection host:port as comma separated list in forrest docs (pat via breed) - - ZOOKEEPER-275. Bug in FastLeaderElection. (flavio via mahadev) - - ZOOKEEPER-272. getchildren can fail for large number of children. (mahadev) - - ZOOKEEPER-16. Need to do path validation. (pat, mahadev) - - ZOOKEEPER-252. PurgeTxnLog is not handling the new dataDir directory - structure (mahadev via phunt) - - ZOOKEEPER-291. regression for legacy code using KeeperException.Code - constants (due to 246). (pat via mahadev) - - ZOOKEEPER-255. zoo_set() api does not return stat datastructure. - (avery ching via mahadev) - - ZOOKEEPER-293. zoo_set needs to be abi compatible (3.1 changed the -signature), fix this by adding zoo_set2 (pat via mahadev) - - ZOOKEEPER-302. Quote values in JMX objectnames. (tom and pat via mahadev) - -IMPROVEMENTS: - - ZOOKEEPER-64. Log system env information when initializing server and - client (pat via mahadev) - - ZOOKEEPER-243. add SEQUENCE flag documentation to the programming guide. - (patrick hunt via mahadev) - - ZOOKEEPER-161. Content needed: "Designing a ZooKeeper Deployment" - (breed via phunt) - - ZOOKEEPER-247. fix formatting of C API in ACL section of programmer guide. - (patrick hunt via mahadev) - - ZOOKEEPER-230. Improvements to FLE. (Flavio via mahadev) - - ZOOKEEPER-225. c client should log an info message in zookeeper_init - detailing connection parameters. (pat via mahadev) - - ZOOKEEPER-222. print C client log message timestamp in human readable - form. (pat via mahadev) - - ZOOKEEPER-256. support use of JMX to manage log4j configuration at runtime. - (pat via mahadev) - - ZOOKEEPER-214. add new "stat reset" command to server admin port. - (pat via mahadev) - - ZOOKEEPER-258. docs incorrectly state max client timeout as 60 seconds - (it's based on server ticktime). (phunt via mahadev) - - ZOOKEEPER-135. Fat jar build target. (phunt and breed via mahadev) - - ZOOKEEPER-234. Eliminate using statics to initialize the sever. Should - allow server to be more embeddable in OSGi enviorments. (phunt) - - ZOOKEEPER-259. cleanup the logging levels used (use the correct level) - and messages generated. (phunt via breed) - - ZOOKEEPER-210. Require Java 6. (phunt via breed) - - ZOOKEEPER-177. needed: docs for JMX (phunt via mahadev) - - ZOOKEEPER-253. documentation of DataWatcher state transition is misleading -regarding auto watch reset on reconnect. (phunt via mahadev) - - ZOOKEEPER-269. connectionloss- add more documentation to detail. (phunt and -flavio via mahadev) - - ZOOKEEPER-260. document the recommended values for server id's - (mahadev via phunt) - - ZOOKEEPER-215. expand system test environment (breed via phunt) - - ZOOKEEPER-229. improve documentation regarding user's responsibility to - cleanup datadir (snaps/logs) (mahadev via phunt) - - ZOOKEEPER-69. ZooKeeper logo - - ZOOKEEPER-286. Make GenerateLoad use InstanceContainers. (breed via mahadev) - - ZOOKEEPER-220. programming guide watches section should clarify - server/clientlib role in data/child watch maint. (breed via phunt) - - ZOOKEEPER-289. add debug messages to nioserver select loop. (mahadev) - -NEW FEATURES: - - ZOOKEEPER-276. Bookkeeper contribution (Flavio and Luca Telloli via mahadev) - - ZOOKEEPER-231. Quotas in ZooKeeper. (mahadev) - -Release 3.0.0 - 2008-10-21 - -Non-backward compatible changes: - - ZOOKEEPER-43. Server side of auto reset watches. (breed via mahadev) - - ZOOKEEPER-132. Create Enum to replace CreateFlag in ZooKepper.create - method (Jakob Homan via phunt) - - ZOOKEEPER-139. Create Enums for WatcherEvent's KeeperState and EventType - (Jakob Homan via phunt) - - ZOOKEEPER-18. keeper state inconsistency (Jakob Homan via phunt) - - ZOOKEEPER-38. headers (version+) in log/snap files (Andrew Kornev and Mahadev - Konar via breed) - - ZOOKEEPER-8. Stat enchaned to include num of children and size - (phunt) - - ZOOKEEPER-6. List of problem identifiers in zookeeper.h - (phunt) - - ZOOKEEPER-7. Use enums rather than ints for types and state - (Jakob Homan via mahadev) - - ZOOKEEPER-27. Unique DB identifiers for servers and clients - (mahadev) - - ZOOKEEPER-32. CRCs for ZooKeeper data - (mahadev) - - ZOOKEEPER-33. Better ACL management - (mahadev) - -Backward compatible changes: - - BUGFIXES: - - ZOOKEEPER-203. fix datadir typo in releasenotes (phunt) - - ZOOKEEPER-145. write detailed release notes for users migrating from 2.x - to 3.0 (phunt) - - ZOOKEEPER-23. Auto reset of watches on reconnect (breed via phunt) - - ZOOKEEPER-191. forrest docs for upgrade. (mahadev via phunt) - - ZOOKEEPER-201. validate magic number when reading snapshot and transaction - logs (mahadev via phunt) - - ZOOKEEPER-200. the magic number for snapshot and log must be different - (currently same) (phunt) - - ZOOKEEPER-199. fix log messages in persistence code (mahadev via phunt) - - ZOOKEEPER-197. create checksums for snapshots (mahadev via phunt) - - ZOOKEEPER-198. apache license header missing from FollowerSyncRequest.java - (phunt) - - ZOOKEEPER-5. Upgrade Feature in Zookeeper server. (mahadev via phunt) - - ZOOKEEPER-194. Fix terminology in zookeeperAdmin.xml - (Flavio Paiva Junqueira) - - ZOOKEEPER-151. Document change to server configuration - (Flavio Paiva Junqueira) - - ZOOKEEPER-193. update java example doc to compile with latest zookeeper - (phunt) - - ZOOKEEPER-187. CreateMode api docs missing (phunt) - - ZOOKEEPER-186. add new "releasenotes.xml" to forrest documentation - (phunt) - - ZOOKEEPER-190. Reorg links to docs and navs to docs into related sections - (robbie via phunt) - - ZOOKEEPER-189. forrest build not validated xml of input documents - (robbie via phunt) - - ZOOKEEPER-188. Check that election port is present for all servers - (Flavio Paiva Junqueira via phunt) - - ZOOKEEPER-185. Improved version of FLETest (Flavio Paiva Junqueira) - - ZOOKEEPER-184. tests: An explicit include derective is needed for the usage - of memcpy(), memset(), strlen(), strdup() and free() functions - (Maxim P. Dementiev via phunt) - - ZOOKEEPER-183. Array subscript is above array bounds in od_completion(), - src/cli.c. (Maxim P. Dementiev via phunt) - - ZOOKEEPER-182. zookeeper_init accepts empty host-port string and returns - valid pointer to zhandle_t. (Maxim P. Dementiev via phunt) - - ZOOKEEPER-17. zookeeper_init doc needs clarification (phunt) - - ZOOKEEPER-181. Some Source Forge Documents did not get moved over: - javaExample, zookeeperTutorial, zookeeperInternals (robbie via phunt) - - ZOOKEEPER-180. Placeholder sections needed in document for new topics that - the umbrella jira discusses (robbie via phunt) - - ZOOKEEPER-179. Programmer's Guide "Basic Operations" section is missing - content (robbie via phunt) - - ZOOKEEPER-178. FLE test. (Flavio Paiva Junqueira) - - ZOOKEEPER-159. Cover two corner cases of leader election - (Flavio Paiva Junqueira via phunt) - - ZOOKEEPER-156. update programmer guide with acl details from old wiki page - (phunt) - - ZOOKEEPER-154. reliability graph diagram in overview doc needs context - (phunt) - - ZOOKEEPER-157. Peer can't find existing leader (Flavio Paiva Junqueira) - - ZOOKEEPER-155. improve "the zookeeper project" section of overview doc - (phunt) - - ZOOKEEPER-140. Deadlock in QuorumCnxManager (Flavio Paiva Junqueira) - - ZOOKEEPER-147. This is version of the documents with most of the [tbd...] - scrubbed out (robbie via phunt) - - ZOOKEEPER-150. zookeeper build broken (mahadev via phunt) - - ZOOKEEPER-136. sync causes hang in all followers of quorum. (breed) - - ZOOKEEPER-134. findbugs cleanup (phunt) - - ZOOKEEPER-133. hudson tests failing intermittently (phunt) - - ZOOKEEPER-144. add tostring support for watcher event, and enums for event - type/state (Jakob Homan via phunt) - - ZOOKEEPER-21. Improve zk ctor/watcher (state transition) docs (phunt) - - ZOOKEEPER-142. Provide Javadoc as to the maximum size of the data byte - array that may be stored within a znode (Jakob Homan via phunt) - - ZOOKEEPER-93. Create Documentation for Zookeeper (phunt) - - ZOOKEEPER-117. threading issues in Leader election (fpj via breed) - - ZOOKEEPER-137. client watcher objects can lose events (phunt via breed) - - ZOOKEEPER-131. Old leader election can elect a dead leader over and over - again (breed via mahadev) - - ZOOKEEPER-130. update build.xml to support apache release process - (phunt via mahadev) - - ZOOKEEPER-118. findbugs flagged switch statement in - followerrequestprocessor.run() (Flavio Paiva Junqueira via phunt) - - ZOOKEEPER-115. Potential NPE in QuorumCnxManager - (Flavio Paiva Junqueira) - - ZOOKEEPER-114. cleanup ugly event messages in zookeeper client - (Jakob Homan) - - ZOOKEEPER-112. src/java/main ZooKeeper.java has test code embedded into it. - (phunt) - - ZOOKEEPER-39. Use Watcher objects rather than boolean on read operations. - (Andrew Kornev) - - ZOOKEEPER-97. supports optional output directory in code generator. (Hiram - Chirino via phunt) - - ZOOKEEPER-101. Integrate ZooKeeper with "violations" feature on hudson - (phunt) - - ZOOKEEPER-105. Catch Zookeeper exceptions and print on the stderr. - (Anthony Urso via Mahadev) - - ZOOKEEPER-42. Change Leader Election to fast tcp. (Flavio Paiva Junqueira - via phunt) - - ZOOKEEPER-48. auth_id now handled correctly when no auth ids present - (Benjamin Reed via phunt) - - ZOOKEEPER-44. Create sequence flag children with prefixes of 0's so that - they can be lexicographically sorted. (Jakob Homan via mahadev) - - ZOOKEEPER-108. Fix sync operation reordering on a Quorum. - (Flavio Paiva Junqueira via Mahadev) - - ZOOKEEPER-25. Fuse module for Zookeeper. (Swee Lim, Bart, Patrick Hunt and - Andrew Kornev via Mahadev) - - ZOOKEEPER-58. Race condition on ClientCnxn.java (breed) - - ZOOKEEPER-56. Add clover support to build.xml. (Patrick Hunt via mahadev) - - ZOOKEEPER-75. register the ZooKeeper mailing lists with nabble.com (phunt) - - ZOOKEEPER-54. remove sleeps in the tests. (phunt) - - ZOOKEEPER-55. build.xml failes to retrieve a release number from SVN and - the ant target "dist" fails (Andrew Kornev) - - ZOOKEEPER-89. invoke WhenOwnerListener.whenNotOwner() when the ZK - connection fails (james strachan) - - ZOOKEEPER-90. invoke WhenOwnerListener.whenNotOwner() when the ZK - session expires and the znode is the leader (james strachan) - - ZOOKEEPER-82. Make the ZooKeeperServer more DI friendly. (Hiram Chirino via - mahadev) - - ZOOKEEPER-110. Build script relies on svnant, which is not compatible - with subversion 1.5 working copies (Jakob Homan) - - ZOOKEEPER-111. Significant cleanup of existing tests. (Patrick Hunt via - mahadev) - - ZOOKEEPER-122. Fix NPE in jute's Utils.toCSVString. (Anthony Urso via - mahadev) - - ZOOKEEPER-123. Fix the wrong class is specified for the logger. (Jakob Homan - via mahadev) - - ZOOKEEPER-2. Fix synchronization issues in QuorumPeer and FastLeader - election. (Flavio Paiva Junqueira via mahadev) - - ZOOKEEPER-125. Remove unwanted class declaration in FastLeaderElection. - (Flavio Paiva Junqueira via mahadev) - - ZOOKEEPER-61. Address (remove) use of sleep(#) in client/server test cases. - (phunt) - - ZOOKEEPER-75. cleanup the library directory (phunt) - - ZOOKEEPER-109. cleanup of NPE and Resource issue nits found by static - analysis (phunt) - - ZOOKEEPER-76. Commit 677109 removed the cobertura library, but not the - build targets. (phunt) - - ZOOKEEPER-63. Race condition in client close() operation. (phunt via breed) - - ZOOKEEPER-70. Add skeleton forrest doc structure for ZooKeeper (phunt) - - ZOOKEEPER-79. Document jacob's leader election on the wiki recipes page - (Flavio Junqueira) - - ZOOKEEPER-73. Move ZK wiki from SourceForge to Apache (phunt) - - ZOOKEEPER-72. Initial creation/setup of ZooKeeper ASF site. (phunt) - - ZOOKEEPER-71. Determine what to do re ZooKeeper Changelog(s) (mahadev) - - ZOOKEEPER-68. parseACLs in ZooKeeper.java fails to parse elements of ACL, - should be lastIndexOf rather than IndexOf (mahadev) - - ZOOKEEPER-130. update build.xml to support apache release process. - (phunt via mahadev) - - ZOOKEEPER-131. Fix Old leader election can elect a dead leader over and over - again. (breed via mahadev) - - ZOOKEEPER-137. client watcher objects can lose events (Patrick Hunt via breed) - - ZOOKEEPER-117. threading issues in Leader election (Flavio Junqueira and - Patrick Hunt via breed) - - ZOOKEEPER-128. test coverage on async client operations needs to be improved - (phunt) - - ZOOKEEPER-127. Use of non-standard election ports in config breaks services - (Mark Harwood and Flavio Junqueira via breed) - - ZOOKEEPER-53. tests failing on solaris. (phunt) - - ZOOKEEPER-172. FLE Test (Flavio Junqueira via breed) - - ZOOKEEPER-41. Sample startup script (mahadev) - - ZOOKEEPER-33. Better ACL management (Mahadev Konar) - - ZOOKEEPER-49. SetACL does not work (breed) - - ZOOKEEPER-20. Child watches are not triggered when the node is deleted - (phunt) - - ZOOKEEPER-15. handle failure better in build.xml:test (phunt) - - ZOOKEEPER-11. ArrayList is used instead of List (phunt) - - ZOOKEEPER-45. Restructure the SVN repository after initial import (phunt) - - ZOOKEEPER-1. Initial ZooKeeper code contribution from Yahoo! (phunt) From 07a12868bbfc16308b7bf192ac3abda77d1f2846 Mon Sep 17 00:00:00 2001 From: Mohammad Arshad Date: Sat, 11 Feb 2017 04:39:22 +0530 Subject: [PATCH 385/444] ZOOKEEPER-2680: Correct DataNode.getChildren() inconsistent behaviour Author: Mohammad Arshad Reviewers: Edward Ribeiro , Abraham Fine , Michael Han , Rakesh Radhakrishnan Closes #162 from arshadmohammad/ZOOKEEPER-2680-br-3.4 --- .../org/apache/zookeeper/server/DataNode.java | 4 +- .../org/apache/zookeeper/server/DataTree.java | 48 ++++---------- .../server/PrepRequestProcessor.java | 3 +- .../zookeeper/server/SnapshotFormatter.java | 6 +- .../apache/zookeeper/server/DataNodeTest.java | 65 +++++++++++++++++++ 5 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/DataNodeTest.java diff --git a/src/java/main/org/apache/zookeeper/server/DataNode.java b/src/java/main/org/apache/zookeeper/server/DataNode.java index 4c79d55439d..8efdaf8bd72 100644 --- a/src/java/main/org/apache/zookeeper/server/DataNode.java +++ b/src/java/main/org/apache/zookeeper/server/DataNode.java @@ -60,6 +60,8 @@ public class DataNode implements Record { */ private Set children = null; + private static final Set EMPTY_SET = Collections.emptySet(); + /** * default constructor for the datanode */ @@ -130,7 +132,7 @@ public synchronized void setChildren(HashSet children) { */ public synchronized Set getChildren() { if (children == null) { - return children; + return EMPTY_SET; } return Collections.unmodifiableSet(children); diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index 25f2975841c..656e4e9d209 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -385,10 +385,8 @@ public String createNode(String path, byte data[], List acl, } synchronized (parent) { Set children = parent.getChildren(); - if (children != null) { - if (children.contains(childName)) { - throw new KeeperException.NodeExistsException(); - } + if (children.contains(childName)) { + throw new KeeperException.NodeExistsException(); } if (parentCVersion == -1) { @@ -598,14 +596,7 @@ public List getChildren(String path, Stat stat, Watcher watcher) if (stat != null) { n.copyStat(stat); } - ArrayList children; - Set childs = n.getChildren(); - if (childs != null) { - children = new ArrayList(childs.size()); - children.addAll(childs); - } else { - children = new ArrayList(0); - } + List children = new ArrayList(n.getChildren()); if (watcher != null) { childWatches.addWatch(path, watcher); @@ -937,17 +928,12 @@ private void getCounts(String path, Counts counts) { int len = 0; synchronized (node) { Set childs = node.getChildren(); - if (childs != null) { - children = childs.toArray(new String[childs.size()]); - } + children = childs.toArray(new String[childs.size()]); len = (node.data == null ? 0 : node.data.length); } // add itself counts.count += 1; counts.bytes += len; - if (children == null || children.length == 0) { - return; - } for (String child : children) { getCounts(path + "/" + child, counts); } @@ -987,11 +973,9 @@ private void traverseNode(String path) { String children[] = null; synchronized (node) { Set childs = node.getChildren(); - if (childs != null) { - children = childs.toArray(new String[childs.size()]); - } + children = childs.toArray(new String[childs.size()]); } - if (children == null || children.length == 0) { + if (children.length == 0) { // this node does not have a child // is the leaf node // check if its the leaf node @@ -1051,23 +1035,19 @@ void serializeNode(OutputArchive oa, StringBuilder path) throws IOException { //are never changed nodeCopy = new DataNode(node.parent, node.data, node.acl, statCopy); Set childs = node.getChildren(); - if (childs != null) { - children = childs.toArray(new String[childs.size()]); - } + children = childs.toArray(new String[childs.size()]); } oa.writeString(pathString, "path"); oa.writeRecord(nodeCopy, "node"); path.append('/'); int off = path.length(); - if (children != null) { - for (String child : children) { - // since this is single buffer being resused - // we need - // to truncate the previous bytes of string. - path.delete(off, Integer.MAX_VALUE); - path.append(child); - serializeNode(oa, path); - } + for (String child : children) { + // since this is single buffer being resused + // we need + // to truncate the previous bytes of string. + path.delete(off, Integer.MAX_VALUE); + path.append(child); + serializeNode(oa, path); } } diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index fe02b8f32b2..1248b08115d 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -154,8 +154,7 @@ ChangeRecord getRecordForPath(String path) throws KeeperException.NoNodeExceptio synchronized(n) { children = n.getChildren(); } - lastChange = new ChangeRecord(-1, path, n.stat, - children != null ? children.size() : 0, + lastChange = new ChangeRecord(-1, path, n.stat, children.size(), zks.getZKDatabase().aclForNode(n)); } } diff --git a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java index f94c54ddffd..bc434020f0d 100644 --- a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java +++ b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java @@ -94,10 +94,8 @@ private void printZnode(DataTree dataTree, String name) { } children = n.getChildren(); } - if (children != null) { - for (String child : children) { - printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child); - } + for (String child : children) { + printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child); } } diff --git a/src/java/test/org/apache/zookeeper/server/DataNodeTest.java b/src/java/test/org/apache/zookeeper/server/DataNodeTest.java new file mode 100644 index 00000000000..628976604a3 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/DataNodeTest.java @@ -0,0 +1,65 @@ +/** + * 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.server; + +import static org.junit.Assert.*; + +import java.util.Set; + +import org.junit.Test; + +public class DataNodeTest { + + @Test + public void testGetChildrenShouldReturnEmptySetWhenThereAreNoChidren() { + // create DataNode and call getChildren + DataNode dataNode = new DataNode(); + Set children = dataNode.getChildren(); + assertNotNull(children); + assertEquals(0, children.size()); + + // add child,remove child and then call getChildren + String child = "child"; + dataNode.addChild(child); + dataNode.removeChild(child); + children = dataNode.getChildren(); + assertNotNull(children); + assertEquals(0, children.size()); + + // Returned empty set must not be modifiable + children = dataNode.getChildren(); + try { + children.add("new child"); + fail("UnsupportedOperationException is expected"); + } catch (UnsupportedOperationException e) { + // do nothing + } + } + + @Test + public void testGetChildrenReturnsImmutableEmptySet() { + DataNode dataNode = new DataNode(); + Set children = dataNode.getChildren(); + try { + children.add("new child"); + fail("UnsupportedOperationException is expected"); + } catch (UnsupportedOperationException e) { + // do nothing + } + } +} From e51dbeb47fbb7131ca96c58caa479f53c0b8633c Mon Sep 17 00:00:00 2001 From: tony mancill Date: Mon, 13 Feb 2017 21:25:40 +0530 Subject: [PATCH 386/444] ZOOKEEPER-2617: correct a few spelling typos Hi - this PR contains corrections for some spelling typos. Most of them appear in comments, but a few appear in documentation and program output. Thank you for considering it. Author: tony mancill Reviewers: Flavio Junqueira , Edward Ribeiro , Rakesh Radhakrishnan Closes #87 from tmancill/tmancill/spelling-typos --- src/c/include/zookeeper.h | 14 +++++++------- src/c/src/load_gen.c | 2 +- src/contrib/zkpython/src/c/pyzk_docstrings.h | 6 +++--- src/contrib/zkpython/src/c/zookeeper.c | 4 ++-- src/contrib/zktreeutil/src/ZkAdaptor.cc | 2 +- src/contrib/zktreeutil/src/ZkAdaptor.h | 2 +- src/contrib/zktreeutil/src/ZkTreeUtil.cc | 2 +- .../inspector/manager/ZooInspectorManagerImpl.java | 4 ++-- src/docs/src/documentation/conf/cli.xconf | 2 +- .../content/xdocs/zookeeperProgrammers.xml | 2 +- .../zookeeper/server/DatadirCleanupManager.java | 2 +- .../apache/zookeeper/server/ZooKeeperThread.java | 2 +- .../org/apache/zookeeper/server/PurgeTxnTest.java | 6 +++--- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/c/include/zookeeper.h b/src/c/include/zookeeper.h index 7d1066a9313..005f1f066ba 100644 --- a/src/c/include/zookeeper.h +++ b/src/c/include/zookeeper.h @@ -468,8 +468,8 @@ ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn, * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZOPERATIONTIMEOUT - failed to flush the buffers within the specified timeout. - * ZCONNECTIONLOSS - a network error occured while attempting to send request to server - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details + * ZCONNECTIONLOSS - a network error occurred while attempting to send request to server + * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details */ ZOOAPI int zookeeper_close(zhandle_t *zh); @@ -523,12 +523,12 @@ ZOOAPI struct sockaddr* zookeeper_get_connected_host(zhandle_t *zh, * ZOK - success * ZBADARGUMENTS - invalid input parameters * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZCONNECTIONLOSS - a network error occured while attempting to establish + * ZCONNECTIONLOSS - a network error occurred while attempting to establish * a connection to the server * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZOPERATIONTIMEOUT - hasn't received anything from the server for 2/3 of the * timeout value specified in zookeeper_init() - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details + * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details */ #ifdef WIN32 ZOOAPI int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest, @@ -547,11 +547,11 @@ ZOOAPI int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, * ZOK - success * ZBADARGUMENTS - invalid input parameters * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZCONNECTIONLOSS - a network error occured while attempting to send request to server + * ZCONNECTIONLOSS - a network error occurred while attempting to send request to server * ZSESSIONEXPIRED - connection attempt failed -- the session's expired * ZAUTHFAILED - authentication request failed, e.i. invalid credentials * ZRUNTIMEINCONSISTENCY - a server response came out of order - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details + * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details * ZNOTHING -- not an error; simply indicates that there no more data from the server * to be processed (when called with ZOOKEEPER_READ flag). */ @@ -1166,7 +1166,7 @@ ZOOAPI const char* zerror(int c); * ZBADARGUMENTS - invalid input parameters * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZSYSTEMERROR - a system error occured + * ZSYSTEMERROR - a system error occurred */ ZOOAPI int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert, int certLen, void_completion_t completion, const void *data); diff --git a/src/c/src/load_gen.c b/src/c/src/load_gen.c index 72b5950765c..0e172e83bf1 100644 --- a/src/c/src/load_gen.c +++ b/src/c/src/load_gen.c @@ -256,7 +256,7 @@ int main(int argc, char **argv) { deletedCounter=0; rc=recursiveDelete(argv[2]); if(rc==ZOK){ - LOG_INFO(("Succesfully deleted a subtree starting at %s (%d nodes)", + LOG_INFO(("Successfully deleted a subtree starting at %s (%d nodes)", argv[2],deletedCounter)); exit(0); } diff --git a/src/contrib/zkpython/src/c/pyzk_docstrings.h b/src/contrib/zkpython/src/c/pyzk_docstrings.h index d2c4d60f6a4..1f38d53fc74 100644 --- a/src/contrib/zkpython/src/c/pyzk_docstrings.h +++ b/src/contrib/zkpython/src/c/pyzk_docstrings.h @@ -276,7 +276,7 @@ const char pyzk_add_auth_doc[] = "BADARGUMENTS - invalid input parameters\n" "INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n" - "SYSTEMERROR - a system error occured\n"; + "SYSTEMERROR - a system error occurred\n"; const char pyzk_is_unrecoverable_doc[] = " checks if the current zookeeper connection state can't be recovered.\n" @@ -513,8 +513,8 @@ static const char pyzk_close_doc[] = "BADARGUMENTS - invalid input parameters\n" "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n" "OPERATIONTIMEOUT - failed to flush the buffers within the specified timeout.\n" -"CONNECTIONLOSS - a network error occured while attempting to send request to server\n" - "SYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details\n"; +"CONNECTIONLOSS - a network error occurred while attempting to send request to server\n" + "SYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details\n"; static const char pyzk_set2_doc[] = "\n" diff --git a/src/contrib/zkpython/src/c/zookeeper.c b/src/contrib/zkpython/src/c/zookeeper.c index 0bf6c59c28e..4474661860a 100644 --- a/src/contrib/zkpython/src/c/zookeeper.c +++ b/src/contrib/zkpython/src/c/zookeeper.c @@ -721,7 +721,7 @@ PyObject *pyzoo_adelete(PyObject *self, PyObject *args) return Py_BuildValue("i", err); } -/* Asynchronous node existance check, returns integer error code */ +/* Asynchronous node existence check, returns integer error code */ PyObject *pyzoo_aexists(PyObject *self, PyObject *args) { int zkhid; char *path; @@ -1059,7 +1059,7 @@ static PyObject *pyzoo_delete(PyObject *self, PyObject *args) return Py_BuildValue("i", err); } -/* Synchronous node existance check, returns stat if exists, None if +/* Synchronous node existence check, returns stat if exists, None if absent */ static PyObject *pyzoo_exists(PyObject *self, PyObject *args) { diff --git a/src/contrib/zktreeutil/src/ZkAdaptor.cc b/src/contrib/zktreeutil/src/ZkAdaptor.cc index baec8f9b0fa..1df175a9bac 100644 --- a/src/contrib/zktreeutil/src/ZkAdaptor.cc +++ b/src/contrib/zktreeutil/src/ZkAdaptor.cc @@ -445,7 +445,7 @@ namespace zktreeutil if (rc == ZNONODE) return false; // Some error - std::cerr << "[zktreeutil] Error in checking existance of " << path << std::endl; + std::cerr << "[zktreeutil] Error in checking existence of " << path << std::endl; throw ZooKeeperException( string("Unable to check existence of node ") + path, rc ); } else { return true; diff --git a/src/contrib/zktreeutil/src/ZkAdaptor.h b/src/contrib/zktreeutil/src/ZkAdaptor.h index d94b033c51b..4b68e28db1f 100644 --- a/src/contrib/zktreeutil/src/ZkAdaptor.h +++ b/src/contrib/zktreeutil/src/ZkAdaptor.h @@ -255,7 +255,7 @@ namespace zktreeutil vector getNodeChildren( const string &path) throw(ZooKeeperException); /** - * \brief Check the existance of path to a znode. + * \brief Check the existence of path to a znode. * * @param path the absolute path name of the znode * @return TRUE if the znode exists; FALSE otherwise diff --git a/src/contrib/zktreeutil/src/ZkTreeUtil.cc b/src/contrib/zktreeutil/src/ZkTreeUtil.cc index 83f0cbf3f94..270bf3105c7 100644 --- a/src/contrib/zktreeutil/src/ZkTreeUtil.cc +++ b/src/contrib/zktreeutil/src/ZkTreeUtil.cc @@ -347,7 +347,7 @@ namespace zktreeutil std::cerr << "[zktreeutil] connected to ZK serverfor reading" << std::endl; - // Check the existance of the path to znode + // Check the existence of the path to znode if (!zkHandle->nodeExists (path)) { string errMsg = string("[zktreeutil] path does not exists : ") + path; diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java index 63d82c1fb6f..d8194d5e4a5 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java @@ -625,7 +625,7 @@ public void addWatchers(Collection selectedNodes, zooKeeper)); } catch (Exception e) { LoggerFactory.getLogger().error( - "Error occured adding node watcher for node: " + "Error occurred adding node watcher for node: " + node, e); } } @@ -700,7 +700,7 @@ public void process(WatchedEvent event) { } } catch (Exception e) { LoggerFactory.getLogger().error( - "Error occured re-adding node watcherfor node " + "Error occurred re-adding node watcherfor node " + nodePath, e); } nodeListener.processEvent(event.getPath(), event.getType() diff --git a/src/docs/src/documentation/conf/cli.xconf b/src/docs/src/documentation/conf/cli.xconf index 99eedf583c1..c6713408245 100644 --- a/src/docs/src/documentation/conf/cli.xconf +++ b/src/docs/src/documentation/conf/cli.xconf @@ -98,7 +98,7 @@ | | | Two attributes to this node specify whether a page should - | be generated when an error has occured. 'generate' specifies + | be generated when an error has occurred. 'generate' specifies | whether a page should be generated (default: true) and | extension specifies an extension that should be appended | to the generated page's filename (default: none) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml index f6d50b64594..f0c05b34981 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml @@ -1506,7 +1506,7 @@ authProvider.2=com.f.MyAuth2 If you are using watches, you must look for the connected watch event. When a ZooKeeper client disconnects from a server, you will not receive notification of changes until reconnected. If you are - watching for a znode to come into existance, you will miss the event + watching for a znode to come into existence, you will miss the event if the znode is created and deleted while you are disconnected. diff --git a/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java b/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java index 8c4797166f1..e8dbeb79af1 100644 --- a/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java +++ b/src/java/main/org/apache/zookeeper/server/DatadirCleanupManager.java @@ -139,7 +139,7 @@ public void run() { try { PurgeTxnLog.purge(new File(logsDir), new File(snapsDir), snapRetainCount); } catch (Exception e) { - LOG.error("Error occured while purging.", e); + LOG.error("Error occurred while purging.", e); } LOG.info("Purge task completed."); } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java index 2830624f41e..cf6cecf2744 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperThread.java @@ -57,6 +57,6 @@ public ZooKeeperThread(String threadName) { * - exception object */ protected void handleException(String thName, Throwable e) { - LOG.warn("Exception occured from thread {}", thName, e); + LOG.warn("Exception occurred from thread {}", thName, e); } } diff --git a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java index fe321c8af7b..1685a5bba82 100644 --- a/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java +++ b/src/java/test/org/apache/zookeeper/server/PurgeTxnTest.java @@ -572,7 +572,7 @@ public void run() { zk.create(mynode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (Exception e) { - LOG.error("Unexpected exception occured!", e); + LOG.error("Unexpected exception occurred!", e); } if (i == 200) { doPurge.countDown(); @@ -591,8 +591,8 @@ public void run() { Assert.assertTrue("ZkClient ops is not finished!", finished.await(OP_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS)); } catch (InterruptedException ie) { - LOG.error("Unexpected exception occured!", ie); - Assert.fail("Unexpected exception occured!"); + LOG.error("Unexpected exception occurred!", ie); + Assert.fail("Unexpected exception occurred!"); } return znodes; } From e8247eec1103e387e02bbb1e8859b4d468688f48 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Mon, 13 Feb 2017 23:41:31 +0530 Subject: [PATCH 387/444] ZOOKEEPER-2689: Fix Kerberos Authentication related test cases Presently 'MiniKdc.java' uses Apache Kerby which has a build requirement of jdk1.7+, http://directory.apache.org/kerby/developer-guide.html]. Since branch-3.4.x support Java 1.6 or higher, Apache Kerby binding is causing trouble. I've tried an attempt to rewrite MiniKdc.java test using old way of Kerberos implementation provided by Apache Directory Server project, org.apache.directory.* packages. Please refer MiniKdc implementation of Hadoop, trunk branch git hash revision 42e3a805117ff7cb054c2442f7b0e0cc54be63ad Author: Rakesh Radhakrishnan Reviewers: Mohammad Arshad Closes #170 from rakeshadr/ZK-2689 --- ivy.xml | 56 +-- .../zookeeper/server/quorum/auth/MiniKdc.java | 344 +++++++++++++----- .../server/quorum/auth/MiniKdcTest.java | 18 +- 3 files changed, 291 insertions(+), 127 deletions(-) diff --git a/ivy.xml b/ivy.xml index 437b86be04d..4c113846f38 100644 --- a/ivy.xml +++ b/ivy.xml @@ -78,30 +78,38 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java index 4afef41eadc..8e3cb1bed04 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java @@ -17,26 +17,67 @@ */ package org.apache.zookeeper.server.quorum.auth; + import org.apache.commons.io.Charsets; -import org.apache.kerby.kerberos.kerb.KrbException; -import org.apache.kerby.kerberos.kerb.server.KdcConfigKey; -import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; -import org.apache.kerby.util.IOUtil; -import org.apache.kerby.util.NetworkUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.text.StrSubstitutor; +import org.apache.directory.api.ldap.model.schema.SchemaManager; +import org.apache.directory.api.ldap.schemaextractor.SchemaLdifExtractor; +import org.apache.directory.api.ldap.schemaextractor.impl.DefaultSchemaLdifExtractor; +import org.apache.directory.api.ldap.schemaloader.LdifSchemaLoader; +import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager; +import org.apache.directory.server.constants.ServerDNConstants; +import org.apache.directory.server.core.DefaultDirectoryService; +import org.apache.directory.server.core.api.CacheService; +import org.apache.directory.server.core.api.DirectoryService; +import org.apache.directory.server.core.api.InstanceLayout; +import org.apache.directory.server.core.api.schema.SchemaPartition; +import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; +import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex; +import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition; +import org.apache.directory.server.core.partition.ldif.LdifPartition; +import org.apache.directory.server.kerberos.KerberosConfig; +import org.apache.directory.server.kerberos.kdc.KdcServer; +import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory; +import org.apache.directory.server.kerberos.shared.keytab.Keytab; +import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry; +import org.apache.directory.server.protocol.shared.transport.TcpTransport; +import org.apache.directory.server.protocol.shared.transport.UdpTransport; +import org.apache.directory.server.xdbm.Index; +import org.apache.directory.shared.kerberos.KerberosTime; +import org.apache.directory.shared.kerberos.codec.types.EncryptionType; +import org.apache.directory.shared.kerberos.components.EncryptionKey; +import org.apache.directory.api.ldap.model.entry.DefaultEntry; +import org.apache.directory.api.ldap.model.entry.Entry; +import org.apache.directory.api.ldap.model.ldif.LdifEntry; +import org.apache.directory.api.ldap.model.ldif.LdifReader; +import org.apache.directory.api.ldap.model.name.Dn; +import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.UUID; /** * Mini KDC based on Apache Directory Server that can be embedded in testcases @@ -44,10 +85,11 @@ *

            * From within testcases: *

            - * MiniKdc sets one System property when started and un-set when stopped: + * MiniKdc sets 2 System properties when started and un-sets them when stopped: *

              - *
            • sun.security.krb5.debug: set to the debug value provided in the - * configuration
            • + *
            • java.security.krb5.conf: set to the MiniKDC real/host/port
            • + *
            • sun.security.krb5.debug: set to the debug value provided in the + * configuration
            • *
            * Because of this, multiple MiniKdc instances cannot be started in parallel. * For example, running testcases in parallel that start a KDC each. To @@ -73,7 +115,7 @@ * in case of bug fixing, history, etc. * * Branch : trunk - * Github Revision: 916140604ffef59466ba30832478311d3e6249bd + * Github Revision: 42e3a805117ff7cb054c2442f7b0e0cc54be63ad */ public class MiniKdc { @@ -81,6 +123,8 @@ public class MiniKdc { "java.security.krb5.conf"; public static final String SUN_SECURITY_KRB5_DEBUG = "sun.security.krb5.debug"; + private static final File testData = new File( + System.getProperty("test.data.dir", "build/test/data")); public static void main(String[] args) throws Exception { if (args.length < 4) { @@ -195,17 +239,13 @@ public static Properties createConf() { } private Properties conf; - private SimpleKdcServer simpleKdc; + private DirectoryService ds; + private KdcServer kdc; private int port; private String realm; private File workDir; private File krb5conf; - private String transport; - private boolean krb5Debug; - public void setTransport(String transport) { - this.transport = transport; - } /** * Creates a MiniKdc. * @@ -235,7 +275,12 @@ public MiniKdc(Properties conf, File workDir) throws Exception { LOG.info("---------------------------------------------------------------"); this.conf = conf; port = Integer.parseInt(conf.getProperty(KDC_PORT)); - String orgName= conf.getProperty(ORG_NAME); + if (port == 0) { + ServerSocket ss = new ServerSocket(0, 1, InetAddress.getByName(conf.getProperty(KDC_BIND_ADDRESS))); + port = ss.getLocalPort(); + ss.close(); + } + String orgName = conf.getProperty(ORG_NAME); String orgDomain = conf.getProperty(ORG_DOMAIN); realm = orgName.toUpperCase(Locale.ENGLISH) + "." + orgDomain.toUpperCase(Locale.ENGLISH); @@ -269,7 +314,6 @@ public String getRealm() { } public File getKrb5conf() { - krb5conf = new File(System.getProperty(JAVA_SECURITY_KRB5_CONF)); return krb5conf; } @@ -279,81 +323,186 @@ public File getKrb5conf() { * @throws Exception thrown if the MiniKdc could not be started. */ public synchronized void start() throws Exception { - if (simpleKdc != null) { + if (kdc != null) { throw new RuntimeException("Already started"); } - simpleKdc = new SimpleKdcServer(); - prepareKdcServer(); - simpleKdc.init(); - resetDefaultRealm(); - simpleKdc.start(); - LOG.info("MiniKdc stated."); + initDirectoryService(); + initKDCServer(); } - private void resetDefaultRealm() throws IOException { - InputStream templateResource = new FileInputStream( - getKrb5conf().getAbsolutePath()); - String content = IOUtil.readInput(templateResource); - content = content.replaceAll("default_realm = .*\n", - "default_realm = " + getRealm() + "\n"); - IOUtil.writeFile(content, getKrb5conf()); + private void initDirectoryService() throws Exception { + ds = new DefaultDirectoryService(); + ds.setInstanceLayout(new InstanceLayout(workDir)); + + CacheService cacheService = new CacheService(); + ds.setCacheService(cacheService); + + // first load the schema + InstanceLayout instanceLayout = ds.getInstanceLayout(); + File schemaPartitionDirectory = new File(instanceLayout.getPartitionsDirectory(), "schema"); + SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(instanceLayout.getPartitionsDirectory()); + extractor.extractOrCopy(); + + SchemaLoader loader = new LdifSchemaLoader(schemaPartitionDirectory); + SchemaManager schemaManager = new DefaultSchemaManager(loader); + schemaManager.loadAllEnabled(); + ds.setSchemaManager(schemaManager); + // Init the LdifPartition with schema + LdifPartition schemaLdifPartition = new LdifPartition(schemaManager); + schemaLdifPartition.setPartitionPath(schemaPartitionDirectory.toURI()); + + // The schema partition + SchemaPartition schemaPartition = new SchemaPartition(schemaManager); + schemaPartition.setWrappedPartition(schemaLdifPartition); + ds.setSchemaPartition(schemaPartition); + + JdbmPartition systemPartition = new JdbmPartition(ds.getSchemaManager()); + systemPartition.setId("system"); + systemPartition.setPartitionPath( + new File(ds.getInstanceLayout().getPartitionsDirectory(), systemPartition.getId()).toURI()); + systemPartition.setSuffixDn(new Dn(ServerDNConstants.SYSTEM_DN)); + systemPartition.setSchemaManager(ds.getSchemaManager()); + ds.setSystemPartition(systemPartition); + + ds.getChangeLog().setEnabled(false); + ds.setDenormalizeOpAttrsEnabled(true); + ds.addLast(new KeyDerivationInterceptor()); + + // create one partition + String orgName = conf.getProperty(ORG_NAME).toLowerCase(Locale.ENGLISH); + String orgDomain = conf.getProperty(ORG_DOMAIN).toLowerCase(Locale.ENGLISH); + + JdbmPartition partition = new JdbmPartition(ds.getSchemaManager()); + partition.setId(orgName); + partition.setPartitionPath(new File(ds.getInstanceLayout().getPartitionsDirectory(), orgName).toURI()); + partition.setSuffixDn(new Dn("dc=" + orgName + ",dc=" + orgDomain)); + ds.addPartition(partition); + // indexes + Set> indexedAttributes = new HashSet>(); + indexedAttributes.add(new JdbmIndex("objectClass", false)); + indexedAttributes.add(new JdbmIndex("dc", false)); + indexedAttributes.add(new JdbmIndex("ou", false)); + partition.setIndexedAttributes(indexedAttributes); + + // And start the ds + ds.setInstanceId(conf.getProperty(INSTANCE)); + ds.startup(); + // context entry, after ds.startup() + Dn dn = new Dn("dc=" + orgName + ",dc=" + orgDomain); + Entry entry = ds.newEntry(dn); + entry.add("objectClass", "top", "domain"); + entry.add("dc", orgName); + ds.getAdminSession().add(entry); } - private void prepareKdcServer() throws Exception { - // transport - simpleKdc.setWorkDir(workDir); - simpleKdc.setKdcHost(getHost()); - simpleKdc.setKdcRealm(realm); - if (transport == null) { - transport = conf.getProperty(TRANSPORT); + private void initKDCServer() throws Exception { + String orgName = conf.getProperty(ORG_NAME); + String orgDomain = conf.getProperty(ORG_DOMAIN); + String bindAddress = conf.getProperty(KDC_BIND_ADDRESS); + final Map map = new HashMap(); + map.put("0", orgName.toLowerCase(Locale.ENGLISH)); + map.put("1", orgDomain.toLowerCase(Locale.ENGLISH)); + map.put("2", orgName.toUpperCase(Locale.ENGLISH)); + map.put("3", orgDomain.toUpperCase(Locale.ENGLISH)); + map.put("4", bindAddress); + + InputStream is1 = getMinikdcResourceAsStream("minikdc.ldiff"); + + SchemaManager schemaManager = ds.getSchemaManager(); + LdifReader reader = null; + + try { + final String content = StrSubstitutor.replace(IOUtils.toString(is1), map); + reader = new LdifReader(new StringReader(content)); + + for (LdifEntry ldifEntry : reader) { + ds.getAdminSession().add(new DefaultEntry(schemaManager, ldifEntry.getEntry())); + } + } finally { + IOUtils.closeQuietly(reader); + IOUtils.closeQuietly(is1); } - if (port == 0) { - port = NetworkUtil.getServerPort(); + + KerberosConfig kerberosConfig = new KerberosConfig(); + kerberosConfig.setMaximumRenewableLifetime(Long.parseLong(conf.getProperty(MAX_RENEWABLE_LIFETIME))); + kerberosConfig.setMaximumTicketLifetime(Long.parseLong(conf.getProperty(MAX_TICKET_LIFETIME))); + kerberosConfig.setSearchBaseDn(String.format("dc=%s,dc=%s", orgName, orgDomain)); + kerberosConfig.setPaEncTimestampRequired(false); + kdc = new KdcServer(kerberosConfig); + kdc.setDirectoryService(ds); + + // transport + String transport = conf.getProperty(TRANSPORT); + if (transport.trim().equals("TCP")) { + kdc.addTransports(new TcpTransport(bindAddress, port, 3, 50)); + } else if (transport.trim().equals("UDP")) { + kdc.addTransports(new UdpTransport(port)); + } else { + throw new IllegalArgumentException("Invalid transport: " + transport); } - if (transport != null) { - if (transport.trim().equals("TCP")) { - simpleKdc.setKdcTcpPort(port); - simpleKdc.setAllowUdp(false); - } else if (transport.trim().equals("UDP")) { - simpleKdc.setKdcUdpPort(port); - simpleKdc.setAllowTcp(false); - } else { - throw new IllegalArgumentException("Invalid transport: " + transport); + kdc.setServiceName(conf.getProperty(INSTANCE)); + kdc.start(); + + StringBuilder sb = new StringBuilder(); + InputStream is2 = getMinikdcResourceAsStream("minikdc-krb5.conf"); + + BufferedReader r = null; + + try { + r = new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8)); + String line = r.readLine(); + + while (line != null) { + sb.append(line).append("{3}"); + line = r.readLine(); } - } else { - throw new IllegalArgumentException("Need to set transport!"); + } finally { + IOUtils.closeQuietly(r); + IOUtils.closeQuietly(is2); } - simpleKdc.getKdcConfig().setString(KdcConfigKey.KDC_SERVICE_NAME, - conf.getProperty(INSTANCE)); - if (conf.getProperty(DEBUG) != null) { - krb5Debug = getAndSet(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG)); + + krb5conf = new File(workDir, "krb5.conf").getAbsoluteFile(); + FileUtils.writeStringToFile(krb5conf, MessageFormat.format(sb.toString(), getRealm(), getHost(), + Integer.toString(getPort()), System.getProperty("line.separator"))); + System.setProperty(JAVA_SECURITY_KRB5_CONF, krb5conf.getAbsolutePath()); + + System.setProperty(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG, "false")); + + // refresh the config + Class classRef; + if (System.getProperty("java.vendor").contains("IBM")) { + classRef = Class.forName("com.ibm.security.krb5.internal.Config"); + } else { + classRef = Class.forName("sun.security.krb5.Config"); } + Method refreshMethod = classRef.getMethod("refresh", new Class[0]); + refreshMethod.invoke(classRef, new Object[0]); + + LOG.info("MiniKdc listening at port: {}", getPort()); + LOG.info("MiniKdc setting JVM krb5.conf to: {}", krb5conf.getAbsolutePath()); + } + + private InputStream getMinikdcResourceAsStream(String resourceName) + throws FileNotFoundException { + File kdcResourceFile = new File(testData, "/kerberos/" + resourceName); + return new FileInputStream(kdcResourceFile); } /** * Stops the MiniKdc */ public synchronized void stop() { - if (simpleKdc != null) { + if (kdc != null) { + System.getProperties().remove(JAVA_SECURITY_KRB5_CONF); + System.getProperties().remove(SUN_SECURITY_KRB5_DEBUG); + kdc.stop(); try { - simpleKdc.stop(); - } catch (KrbException e) { - e.printStackTrace(); - } finally { - if(conf.getProperty(DEBUG) != null) { - System.setProperty(SUN_SECURITY_KRB5_DEBUG, - Boolean.toString(krb5Debug)); - } + ds.shutdown(); + } catch (Exception ex) { + LOG.error("Could not shutdown ApacheDS properly: {}", ex.toString(), ex); } } delete(workDir); - try { - // Will be fixed in next Kerby version. - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - LOG.info("MiniKdc stopped."); } private void delete(File f) { @@ -378,9 +527,20 @@ private void delete(File f) { * @param password password. * @throws Exception thrown if the principal could not be created. */ - public synchronized void createPrincipal(String principal, String password) - throws Exception { - simpleKdc.createPrincipal(principal, password); + public synchronized void createPrincipal(String principal, String password) throws Exception { + String orgName = conf.getProperty(ORG_NAME); + String orgDomain = conf.getProperty(ORG_DOMAIN); + String baseDn = "ou=users,dc=" + orgName.toLowerCase(Locale.ENGLISH) + ",dc=" + + orgDomain.toLowerCase(Locale.ENGLISH); + String content = "dn: uid=" + principal + "," + baseDn + "\n" + "objectClass: top\n" + "objectClass: person\n" + + "objectClass: inetOrgPerson\n" + "objectClass: krb5principal\n" + "objectClass: krb5kdcentry\n" + + "cn: " + principal + "\n" + "sn: " + principal + "\n" + "uid: " + principal + "\n" + "userPassword: " + + password + "\n" + "krb5PrincipalName: " + principal + "@" + getRealm() + "\n" + + "krb5KeyVersionNumber: 0"; + + for (LdifEntry ldifEntry : new LdifReader(new StringReader(content))) { + ds.getAdminSession().add(new DefaultEntry(ds.getSchemaManager(), ldifEntry.getEntry())); + } } /** @@ -394,25 +554,21 @@ public synchronized void createPrincipal(String principal, String password) public synchronized void createPrincipal(File keytabFile, String ... principals) throws Exception { - simpleKdc.createPrincipals(principals); - if (keytabFile.exists() && !keytabFile.delete()) { - LOG.error("Failed to delete keytab file: " + keytabFile); - } + String generatedPassword = UUID.randomUUID().toString(); + Keytab keytab = new Keytab(); + List entries = new ArrayList(); for (String principal : principals) { - simpleKdc.getKadmin().exportKeytab(keytabFile, principal); + createPrincipal(principal, generatedPassword); + principal = principal + "@" + getRealm(); + KerberosTime timestamp = new KerberosTime(); + for (Map.Entry entry : KerberosKeyFactory + .getKerberosKeys(principal, generatedPassword).entrySet()) { + EncryptionKey ekey = entry.getValue(); + byte keyVersion = (byte) ekey.getKeyVersion(); + entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey)); + } } + keytab.setEntries(entries); + keytab.write(keytabFile); } - - /** - * Set the System property; return the old value for caching. - * - * @param sysprop property - * @param debug true or false - * @return the previous value - */ - private boolean getAndSet(String sysprop, String debug) { - boolean old = Boolean.getBoolean(sysprop); - System.setProperty(sysprop, debug); - return old; - } -} \ No newline at end of file +} diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java index a7bbf7feb93..196d8bec776 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java @@ -18,8 +18,8 @@ package org.apache.zookeeper.server.quorum.auth; -import org.apache.kerby.kerberos.kerb.keytab.Keytab; -import org.apache.kerby.kerberos.kerb.type.base.PrincipalName; +import org.apache.directory.server.kerberos.shared.keytab.Keytab; +import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry; import org.junit.Assert; import org.junit.Test; @@ -30,7 +30,6 @@ import javax.security.auth.login.LoginContext; import java.io.File; import java.security.Principal; -import java.util.List; import java.util.Set; import java.util.Map; import java.util.HashSet; @@ -60,16 +59,17 @@ public void testKeytabGen() throws Exception { File workDir = getWorkDir(); kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo"); - List principalNameList = - Keytab.loadKeytab(new File(workDir, "keytab")).getPrincipals(); + Keytab kt = Keytab.read(new File(workDir, "keytab")); Set principals = new HashSet(); - for (PrincipalName principalName : principalNameList) { - principals.add(principalName.getName()); + for (KeytabEntry entry : kt.getEntries()) { + principals.add(entry.getPrincipalName()); } - + //here principals use \ instead of / + //because org.apache.directory.server.kerberos.shared.keytab.KeytabDecoder + // .getPrincipalName(IoBuffer buffer) use \\ when generates principal Assert.assertEquals(new HashSet(Arrays.asList( - "foo/bar@" + kdc.getRealm(), "bar/foo@" + kdc.getRealm())), + "foo\\bar@" + kdc.getRealm(), "bar\\foo@" + kdc.getRealm())), principals); } From 2de93fe450b909ff76ac8bd8fa44296a515845a5 Mon Sep 17 00:00:00 2001 From: "Robert (Bobby) Evans" Date: Tue, 14 Feb 2017 10:05:52 -0800 Subject: [PATCH 388/444] ZOOKEEPER-2678: Discovery and Sync can take a very long time on large DB (3.4) This patch addresses recovery time when a leader is lost on a large DB. It does this by not clearing the DB before leader election begins, and by avoiding taking a snapshot as part of the SYNC phase, specifically for a DIFF sync. It does this by buffering the proposals and commits just like the code currently does for proposals/commits sent after the NEWLEADER and before the UPTODATE messages. If a SNAP is sent we cannot avoid writing out the full snapshot because there is no other way to make sure the disk DB is in sync with what is in memory. So any edits to the edit log before a background snapshot happened could possibly be applied on top of an incorrect snapshot. This same optimization should work for TRUNC too, but I opted not to do it for TRUNC because TRUNC is rare and TRUNC by its very nature already forces the DB to be reread after the edit logs are modified. So it would still not be fast. In practice this makes it so instead of taking 5+ mins for the cluster to recover from losing a leader it now takes about 3 seconds. I am happy to port this to 3.5. if it looks good. Author: Robert (Bobby) Evans Reviewers: Flavio Junqueira , Edward Ribeiro , Abraham Fine , Michael Han Closes #157 from revans2/ZOOKEEPER-2678 and squashes the following commits: d079617 [Robert (Bobby) Evans] ZOOKEEPER-2678: Improved test to verify snapshot times dcbf325 [Robert (Bobby) Evans] Addressed review comments f57c384 [Robert (Bobby) Evans] ZOOKEEPER-2678: Fixed another race f705293 [Robert (Bobby) Evans] ZOOKEEPER-2678: Addressed review comments 5aa2562 [Robert (Bobby) Evans] ZOOKEEPER-2678: Discovery and Sync can take a very long time on large DBs --- .../zookeeper/server/ZooKeeperServer.java | 18 ++++++++-- .../zookeeper/server/ZooKeeperServerMain.java | 2 +- .../zookeeper/server/quorum/Learner.java | 34 ++++++++++++------- .../zookeeper/server/quorum/Zab1_0Test.java | 29 +++++++++++++++- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index 62ac466bb1a..b0a834102aa 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -489,7 +489,15 @@ public boolean isRunning() { return state == State.RUNNING; } - public synchronized void shutdown() { + public void shutdown() { + shutdown(false); + } + + /** + * Shut down the server instance + * @param fullyShutDown true if another server using the same database will not replace this one in the same process + */ + public synchronized void shutdown(boolean fullyShutDown) { if (!canShutdown()) { LOG.debug("ZooKeeper server is not running, so not proceeding to shutdown!"); return; @@ -507,9 +515,15 @@ public synchronized void shutdown() { if (firstProcessor != null) { firstProcessor.shutdown(); } - if (zkDb != null) { + + if (fullyShutDown && zkDb != null) { zkDb.clear(); } + // else there is no need to clear the database + // * When a new quorum is established we can still apply the diff + // on top of the same zkDb data + // * If we fetch a new snapshot from leader, the zkDb will be + // cleared anyway before loading the snapshot unregisterJMX(); } diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java index 612d2276b84..1b0f59fe383 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java @@ -124,7 +124,7 @@ public void runFromConfig(ServerConfig config) throws IOException { cnxnFactory.join(); if (zkServer.canShutdown()) { - zkServer.shutdown(); + zkServer.shutdown(true); } } catch (InterruptedException e) { // warn, but generally this is ok diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index 749b2741728..c54f6e6b797 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -321,13 +321,16 @@ protected void syncWithLeader(long newLeaderZxid) throws IOException, Interrupte QuorumPacket ack = new QuorumPacket(Leader.ACK, 0, null, null); QuorumPacket qp = new QuorumPacket(); long newEpoch = ZxidUtils.getEpochFromZxid(newLeaderZxid); - - readPacket(qp); + // In the DIFF case we don't need to do a snapshot because the transactions will sync on top of any existing snapshot + // For SNAP and TRUNC the snapshot is needed to save that history + boolean snapshotNeeded = true; + readPacket(qp); LinkedList packetsCommitted = new LinkedList(); LinkedList packetsNotCommitted = new LinkedList(); synchronized (zk) { if (qp.getType() == Leader.DIFF) { - LOG.info("Getting a diff from the leader 0x" + Long.toHexString(qp.getZxid())); + LOG.info("Getting a diff from the leader 0x{}", Long.toHexString(qp.getZxid())); + snapshotNeeded = false; } else if (qp.getType() == Leader.SNAP) { LOG.info("Getting a snapshot from leader"); @@ -364,10 +367,13 @@ else if (qp.getType() == Leader.SNAP) { long lastQueued = 0; - // in V1.0 we take a snapshot when we get the NEWLEADER message, but in pre V1.0 - // we take the snapshot at the UPDATE, since V1.0 also gets the UPDATE (after the NEWLEADER) + // in Zab V1.0 (ZK 3.4+) we might take a snapshot when we get the NEWLEADER message, but in pre V1.0 + // we take the snapshot on the UPDATE message, since Zab V1.0 also gets the UPDATE (after the NEWLEADER) // we need to make sure that we don't take the snapshot twice. - boolean snapshotTaken = false; + boolean isPreZAB1_0 = true; + //If we are not going to take the snapshot be sure the transactions are not applied in memory + // but written out to the transaction log + boolean writeToTxnLog = !snapshotNeeded; // we are now going to start getting transactions to apply followed by an UPTODATE outerLoop: while (self.isRunning()) { @@ -387,7 +393,7 @@ else if (qp.getType() == Leader.SNAP) { packetsNotCommitted.add(pif); break; case Leader.COMMIT: - if (!snapshotTaken) { + if (!writeToTxnLog) { pif = packetsNotCommitted.peekFirst(); if (pif.hdr.getZxid() != qp.getZxid()) { LOG.warn("Committing " + qp.getZxid() + ", but next proposal is " + pif.hdr.getZxid()); @@ -415,7 +421,7 @@ else if (qp.getType() == Leader.SNAP) { + Long.toHexString(lastQueued + 1)); } lastQueued = packet.hdr.getZxid(); - if (!snapshotTaken) { + if (!writeToTxnLog) { // Apply to db directly if we haven't taken the snapshot zk.processTxn(packet.hdr, packet.rec); } else { @@ -424,13 +430,14 @@ else if (qp.getType() == Leader.SNAP) { } break; case Leader.UPTODATE: - if (!snapshotTaken) { // true for the pre v1.0 case + if (isPreZAB1_0) { zk.takeSnapshot(); self.setCurrentEpoch(newEpoch); } self.cnxnFactory.setZooKeeperServer(zk); break outerLoop; - case Leader.NEWLEADER: // it will be NEWLEADER in v1.0 + case Leader.NEWLEADER: // Getting NEWLEADER here instead of in discovery + // means this is Zab 1.0 // Create updatingEpoch file and remove it after current // epoch is set. QuorumPeer.loadDataBase() uses this file to // detect the case where the server was terminated after @@ -441,13 +448,16 @@ else if (qp.getType() == Leader.SNAP) { throw new IOException("Failed to create " + updating.toString()); } - zk.takeSnapshot(); + if (snapshotNeeded) { + zk.takeSnapshot(); + } self.setCurrentEpoch(newEpoch); if (!updating.delete()) { throw new IOException("Failed to delete " + updating.toString()); } - snapshotTaken = true; + writeToTxnLog = true; //Anything after this needs to go to the transaction log, not applied directly in memory + isPreZAB1_0 = false; writePacket(new QuorumPacket(Leader.ACK, newLeaderZxid, null, null), true); break; } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java index 52e7d279c7e..3ed6097b31f 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/Zab1_0Test.java @@ -19,6 +19,9 @@ package org.apache.zookeeper.server.quorum; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -645,6 +648,8 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile(); File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); + //Spy on ZK so we can check if a snapshot happened or not. + f.zk = spy(f.zk); try { Assert.assertEquals(0, f.self.getAcceptedEpoch()); Assert.assertEquals(0, f.self.getCurrentEpoch()); @@ -687,6 +692,10 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, oa.writeRecord(qp, null); zkDb.serializeSnapshot(oa); oa.writeString("BenWasHere", null); + Thread.sleep(10); //Give it some time to process the snap + //No Snapshot taken yet, the SNAP was applied in memory + verify(f.zk, never()).takeSnapshot(); + qp.setType(Leader.NEWLEADER); qp.setZxid(ZxidUtils.makeZxid(1, 0)); oa.writeRecord(qp, null); @@ -697,7 +706,8 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); Assert.assertEquals(1, f.self.getAcceptedEpoch()); Assert.assertEquals(1, f.self.getCurrentEpoch()); - + //Make sure that we did take the snapshot now + verify(f.zk).takeSnapshot(); Assert.assertEquals(firstZxid, f.fzk.getLastProcessedZxid()); // Make sure the data was recorded in the filesystem ok @@ -773,6 +783,8 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile(); File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); + //Spy on ZK so we can check if a snapshot happened or not. + f.zk = spy(f.zk); try { Assert.assertEquals(0, f.self.getAcceptedEpoch()); Assert.assertEquals(0, f.self.getCurrentEpoch()); @@ -839,13 +851,28 @@ public void converseWithFollower(InputArchive ia, OutputArchive oa, Assert.assertEquals(1, f.self.getAcceptedEpoch()); Assert.assertEquals(1, f.self.getCurrentEpoch()); + //Wait for the transactions to be written out. The thread that writes them out + // does not send anything back when it is done. + long start = System.currentTimeMillis(); + while (createSessionZxid != f.fzk.getLastProcessedZxid() && (System.currentTimeMillis() - start) < 50) { + Thread.sleep(1); + } + Assert.assertEquals(createSessionZxid, f.fzk.getLastProcessedZxid()); // Make sure the data was recorded in the filesystem ok ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); + start = System.currentTimeMillis(); zkDb2.loadDataBase(); + while (zkDb2.getSessionWithTimeOuts().isEmpty() && (System.currentTimeMillis() - start) < 50) { + Thread.sleep(1); + zkDb2.loadDataBase(); + } LOG.info("zkdb2 sessions:" + zkDb2.getSessions()); + LOG.info("zkdb2 with timeouts:" + zkDb2.getSessionWithTimeOuts()); Assert.assertNotNull(zkDb2.getSessionWithTimeOuts().get(4L)); + //Snapshot was never taken during very simple sync + verify(f.zk, never()).takeSnapshot(); } finally { recursiveDelete(tmpDir); } From 373b82bab843cc84c22b99f6511f8fea974fd2b4 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Wed, 15 Feb 2017 16:18:15 -0800 Subject: [PATCH 389/444] ZOOKEEPER-2692: Fix race condition in testWatchAutoResetWithPending Author: Abraham Fine Reviewers: Michael Han Closes #177 from afine/ZOOKEEPER-2692_34 --- .../org/apache/zookeeper/test/WatcherTest.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/test/WatcherTest.java b/src/java/test/org/apache/zookeeper/test/WatcherTest.java index 893f33581ef..1c06690cd51 100644 --- a/src/java/test/org/apache/zookeeper/test/WatcherTest.java +++ b/src/java/test/org/apache/zookeeper/test/WatcherTest.java @@ -45,6 +45,8 @@ public class WatcherTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(WatcherTest.class); + private long timeOfLastWatcherInvocation; + private final class MyStatCallback implements StatCallback { int rc; public void processResult(int rc, String path, Object ctx, Stat stat) { @@ -60,6 +62,7 @@ private class MyWatcher extends CountdownWatcher { public void process(WatchedEvent event) { super.process(event); if (event.getType() != Event.EventType.None) { + timeOfLastWatcherInvocation = System.currentTimeMillis(); try { events.put(event); } catch (InterruptedException e) { @@ -173,7 +176,6 @@ public void testWatcherCount() } final static int COUNT = 100; - boolean hasSeenDelete = true; /** * This test checks that watches for pending requests do not get triggered, * but watches set by previous requests do. @@ -207,7 +209,7 @@ public void testWatchAutoResetWithPending() throws Exception { startServer(); watches[COUNT/2-1].waitForConnected(60000); Assert.assertEquals(null, zk.exists("/test", false)); - Thread.sleep(10); + waitForAllWatchers(); for(int i = 0; i < COUNT/2; i++) { Assert.assertEquals("For " + i, 1, watches[i].events.size()); } @@ -221,6 +223,18 @@ public void testWatchAutoResetWithPending() throws Exception { Assert.assertEquals(COUNT, count[0]); zk.close(); } + + /** + * Wait until no watcher has been fired in the last second to ensure that all watches + * that are waiting to be fired have been fired + * @throws Exception + */ + private void waitForAllWatchers() throws Exception { + timeOfLastWatcherInvocation = System.currentTimeMillis(); + while (System.currentTimeMillis() - timeOfLastWatcherInvocation < 1000) { + Thread.sleep(1000); + } + } final int TIMEOUT = 5000; From b26eaf3492a43821f648d8a8f87d8ac3488f1cef Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Mon, 27 Feb 2017 17:23:36 +0530 Subject: [PATCH 390/444] ZOOKEEPER-2696: Eclipse ant ask no longer determines correct classpath for tests after ZOOKEEPER-2689 After pulling branch-3.4 i noticed that I could no longer compile and run the tests within my IDE. The .classpath file generated by the eclipse ant task was missing necessary dependencies. These missing dependencies all appear to be transitive from dependencies brought in by ZOOKEEPER-2689 (which is why ant is able to run the tests). There were two possible ways to fix this, this patch explicitly lists the new transitive dependencies that are required by our test code. Another solution would be to remove `transitive="false"` from `` in the eclipse target of our `build.xml`. This solution would introduce even more transitive dependencies to the .classpath. Author: Abraham Fine Reviewers: Rakesh Radhakrishnan Closes #178 from afine/ZOOKEEPER-2696 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 9c134744ed1..423eb5ed86e 100644 --- a/build.xml +++ b/build.xml @@ -1797,7 +1797,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> description="Create eclipse project files"> - + From 835377f0e1cd215e791ed29c0bcff95e625f299c Mon Sep 17 00:00:00 2001 From: Michael Han Date: Tue, 7 Mar 2017 17:34:34 +0530 Subject: [PATCH 391/444] ZOOKEEPER-2693: DOS attack on wchp/wchc four letter words (4lw) Similar as pull request 179, this PR introduces new property zookeeper.4lw.commands.whitelist to branch-3.4. Unlike branch-3.5 where all 4lw (with few exceptions) is disabled by default, for branch-3.4 only "wchp" and "wchc" are disabled by default - since 4lw is widely used and there is no alternatives in branch-3.4 so we just disable the exploitable ones. Author: Michael Han Reviewers: Rakesh Radhakrishnan Closes #183 from hanm/ZOOKEEPER-2693-br-3.4 and squashes the following commits: d060ddc [Michael Han] update doc. 2ce4ebd [Michael Han] ZOOKEEPER-2693: DOS attack on wchp/wchc four letter words (4lw). Initial commit for branch-3.4. --- .../content/xdocs/zookeeperAdmin.xml | 44 +++ .../zookeeper/server/NIOServerCnxn.java | 33 ++- .../zookeeper/server/NettyServerCnxn.java | 32 ++- .../apache/zookeeper/server/ServerCnxn.java | 94 ++++++- .../test/org/apache/zookeeper/ZKTestCase.java | 4 + .../test/FourLetterWordsWhiteListTest.java | 252 ++++++++++++++++++ 6 files changed, 449 insertions(+), 10 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 5aefa9a1168..fb00fae2494 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1042,6 +1042,40 @@ server.3=zoo3:2888:3888 + + + 4lw.commands.whitelist + + + (Java system property: zookeeper.4lw.commands.whitelist) + + New in 3.4.10: + This property contains a list of comma separated + Four Letter Words commands. It is introduced + to provide fine grained control over the set of commands ZooKeeper can execute, + so users can turn off certain commands if necessary. + By default it contains all supported four letter word commands except "wchp" and "wchc", + if the property is not specified. If the property is specified, then only commands listed + in the whitelist are enabled. + + + Here's an example of the configuration that enables stat, ruok, conf, and isro + command while disabling the rest of Four Letter Words command: + + 4lw.commands.whitelist=stat, ruok, conf, isro + + + Users can also use asterisk option so they don't have to include every command one by one in the list. + As an example, this will enable all four letter word commands: + + + 4lw.commands.whitelist=* + + + + + @@ -1667,6 +1701,16 @@ imok usage limit that would cause the system to swap. + + + Publicly accessible deployment + + + A ZooKeeper ensemble is expected to operate in a trusted computing environment. + It is thus recommended to deploy ZooKeeper behind a firewall. + + + diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index 4ea7fb2736d..456d4c2f14b 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -825,18 +825,30 @@ public void commandRun() { } } + private class NopCommand extends CommandThread { + private String msg; + + public NopCommand(PrintWriter pw, String msg) { + super(pw); + this.msg = msg; + } + + @Override + public void commandRun() { + pw.println(msg); + } + } + /** Return if four letter word found and responded to, otw false **/ private boolean checkFourLetterWord(final SelectionKey k, final int len) throws IOException { // We take advantage of the limited size of the length to look // for cmds. They are all 4-bytes which fits inside of an int - String cmd = cmd2String.get(len); - if (cmd == null) { + if (!ServerCnxn.isKnown(len)) { return false; } - LOG.info("Processing " + cmd + " command from " - + sock.socket().getRemoteSocketAddress()); + packetReceived(); /** cancel the selection key to remove the socket handling @@ -858,6 +870,19 @@ private boolean checkFourLetterWord(final SelectionKey k, final int len) final PrintWriter pwriter = new PrintWriter( new BufferedWriter(new SendBufferWriter())); + + String cmd = ServerCnxn.getCommandString(len); + // ZOOKEEPER-2693: don't execute 4lw if it's not enabled. + if (!ServerCnxn.isEnabled(cmd)) { + LOG.debug("Command {} is not executed because it is not in the whitelist.", cmd); + NopCommand nopCmd = new NopCommand(pwriter, cmd + " is not executed because it is not in the whitelist."); + nopCmd.start(); + return true; + } + + LOG.info("Processing " + cmd + " command from " + + sock.socket().getRemoteSocketAddress()); + if (len == ruokCmd) { RuokCommand ruok = new RuokCommand(pwriter); ruok.start(); diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index 32fc371e6de..203f0e60b86 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -618,23 +618,47 @@ public void commandRun() { } } + private class NopCommand extends CommandThread { + private String msg; + + public NopCommand(PrintWriter pw, String msg) { + super(pw); + this.msg = msg; + } + + @Override + public void commandRun() { + pw.println(msg); + } + } + /** Return if four letter word found and responded to, otw false **/ private boolean checkFourLetterWord(final Channel channel, ChannelBuffer message, final int len) throws IOException { // We take advantage of the limited size of the length to look // for cmds. They are all 4-bytes which fits inside of an int - String cmd = cmd2String.get(len); - if (cmd == null) { + if (!ServerCnxn.isKnown(len)) { return false; } + channel.setInterestOps(0).awaitUninterruptibly(); - LOG.info("Processing " + cmd + " command from " - + channel.getRemoteAddress()); packetReceived(); final PrintWriter pwriter = new PrintWriter( new BufferedWriter(new SendBufferWriter())); + + String cmd = ServerCnxn.getCommandString(len); + // ZOOKEEPER-2693: don't execute 4lw if it's not enabled. + if (!ServerCnxn.isEnabled(cmd)) { + LOG.debug("Command {} is not executed because it is not in the whitelist.", cmd); + NopCommand nopCmd = new NopCommand(pwriter, cmd + " is not executed because it is not in the whitelist."); + nopCmd.start(); + return true; + } + + LOG.info("Processing " + cmd + " command from " + channel.getRemoteAddress()); + if (len == ruokCmd) { RuokCommand ruok = new RuokCommand(pwriter); ruok.start(); diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java index 6dd509b167d..6b93e93f140 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java @@ -26,10 +26,17 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.Map; import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.apache.jute.Record; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; @@ -227,8 +234,91 @@ public String toString() { protected final static int isroCmd = ByteBuffer.wrap("isro".getBytes()) .getInt(); - protected final static HashMap cmd2String = - new HashMap(); + protected final static Map cmd2String = new HashMap(); + + private static final String ZOOKEEPER_4LW_COMMANDS_WHITELIST = "zookeeper.4lw.commands.whitelist"; + + private static final Logger LOG = LoggerFactory.getLogger(ServerCnxn.class); + + private static final Set whiteListedCommands = new HashSet(); + + private static boolean whiteListInitialized = false; + + // @VisibleForTesting + public static void resetWhiteList() { + whiteListInitialized = false; + whiteListedCommands.clear(); + } + + /** + * Return the string representation of the specified command code. + */ + public static String getCommandString(int command) { + return cmd2String.get(command); + } + + /** + * Check if the specified command code is from a known command. + * + * @param command The integer code of command. + * @return true if the specified command is known, false otherwise. + */ + public static boolean isKnown(int command) { + return cmd2String.containsKey(command); + } + + /** + * Check if the specified command is enabled. + * + * In ZOOKEEPER-2693 we introduce a configuration option to only + * allow a specific set of white listed commands to execute. + * A command will only be executed if it is also configured + * in the white list. + * + * @param command The command string. + * @return true if the specified command is enabled. + */ + public static boolean isEnabled(String command) { + if (whiteListInitialized) { + return whiteListedCommands.contains(command); + } + + String commands = System.getProperty(ZOOKEEPER_4LW_COMMANDS_WHITELIST); + if (commands != null) { + String[] list = commands.split(","); + for (String cmd : list) { + if (cmd.trim().equals("*")) { + for (Map.Entry entry : cmd2String.entrySet()) { + whiteListedCommands.add(entry.getValue()); + } + break; + } + if (!cmd.trim().isEmpty()) { + whiteListedCommands.add(cmd.trim()); + } + } + } else { + for (Map.Entry entry : cmd2String.entrySet()) { + String cmd = entry.getValue(); + if (cmd.equals("wchc") || cmd.equals("wchp")) { + // ZOOKEEPER-2693 / disable these exploitable commands by default. + continue; + } + whiteListedCommands.add(cmd); + } + } + + // Readonly mode depends on "isro". + if (System.getProperty("readonlymode.enabled", "false").equals("true")) { + whiteListedCommands.add("isro"); + } + // zkServer.sh depends on "srvr". + whiteListedCommands.add("srvr"); + whiteListInitialized = true; + LOG.info("The list of known four letter word commands is : {}", Arrays.asList(cmd2String)); + LOG.info("The list of enabled four letter word commands is : {}", Arrays.asList(whiteListedCommands)); + return whiteListedCommands.contains(command); + } // specify all of the commands that are available static { diff --git a/src/java/test/org/apache/zookeeper/ZKTestCase.java b/src/java/test/org/apache/zookeeper/ZKTestCase.java index 97e2db6ee21..9098fc47ebf 100644 --- a/src/java/test/org/apache/zookeeper/ZKTestCase.java +++ b/src/java/test/org/apache/zookeeper/ZKTestCase.java @@ -52,6 +52,10 @@ protected String getTestName() { @Override public void starting(FrameworkMethod method) { testName = method.getName(); + // ZOOKEEPER-2693 disables all 4lw by default. + // Here we enable the 4lw which ZooKeeper tests depends. + System.setProperty("zookeeper.4lw.commands.whitelist", "*"); + LOG.info("STARTING " + testName); } diff --git a/src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java b/src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java new file mode 100644 index 00000000000..613346f2ece --- /dev/null +++ b/src/java/test/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java @@ -0,0 +1,252 @@ +/** + * 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.test; + +import java.io.IOException; + +import org.apache.zookeeper.TestableZooKeeper; +import org.apache.zookeeper.server.ServerCnxn; +import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FourLetterWordsWhiteListTest extends ClientBase { + protected static final Logger LOG = + LoggerFactory.getLogger(FourLetterWordsTest.class); + + @Rule + public Timeout timeout = new Timeout(30000); + + /* + * ZOOKEEPER-2693: test white list of four letter words. + * For 3.5.x default white list is empty. Verify that is + * the case (except 'stat' command which is enabled in ClientBase + * which other tests depend on.). + */ + @Test(timeout=30000) + public void testFourLetterWordsAllDisabledByDefault() throws Exception { + stopServer(); + ServerCnxn.resetWhiteList(); + System.setProperty("zookeeper.4lw.commands.whitelist", "stat"); + startServer(); + + // Default white list for 3.5.x is empty, so all command should fail. + verifyAllCommandsFail(); + + TestableZooKeeper zk = createClient(); + + verifyAllCommandsFail(); + + zk.getData("/", true, null); + + verifyAllCommandsFail(); + + zk.close(); + + verifyFuzzyMatch("stat", "Outstanding"); + verifyAllCommandsFail(); + } + + @Test(timeout=30000) + public void testFourLetterWordsEnableSomeCommands() throws Exception { + stopServer(); + ServerCnxn.resetWhiteList(); + System.setProperty("zookeeper.4lw.commands.whitelist", "stat, ruok, isro"); + startServer(); + // stat, ruok and isro are white listed. + verifyFuzzyMatch("stat", "Outstanding"); + verifyExactMatch("ruok", "imok"); + verifyExactMatch("isro", "rw"); + + // Rest of commands fail. + verifyExactMatch("conf", generateExpectedMessage("conf")); + verifyExactMatch("cons", generateExpectedMessage("cons")); + verifyExactMatch("crst", generateExpectedMessage("crst")); + verifyExactMatch("dump", generateExpectedMessage("dump")); + verifyExactMatch("envi", generateExpectedMessage("envi")); + verifyExactMatch("gtmk", generateExpectedMessage("gtmk")); + verifyExactMatch("stmk", generateExpectedMessage("stmk")); + verifyExactMatch("srst", generateExpectedMessage("srst")); + verifyExactMatch("wchc", generateExpectedMessage("wchc")); + verifyExactMatch("wchp", generateExpectedMessage("wchp")); + verifyExactMatch("wchs", generateExpectedMessage("wchs")); + verifyExactMatch("mntr", generateExpectedMessage("mntr")); + } + + @Test(timeout=30000) + public void testISROEnabledWhenReadOnlyModeEnabled() throws Exception { + stopServer(); + ServerCnxn.resetWhiteList(); + System.setProperty("zookeeper.4lw.commands.whitelist", "stat"); + System.setProperty("readonlymode.enabled", "true"); + startServer(); + verifyExactMatch("isro", "rw"); + System.clearProperty("readonlymode.enabled"); + } + + @Test(timeout=30000) + public void testFourLetterWordsInvalidConfiguration() throws Exception { + stopServer(); + ServerCnxn.resetWhiteList(); + System.setProperty("zookeeper.4lw.commands.whitelist", "foo bar" + + " foo,,, " + + "bar :.,@#$%^&*() , , , , bar, bar, stat, "); + startServer(); + + // Just make sure we are good when admin made some mistakes in config file. + verifyAllCommandsFail(); + // But still, what's valid in white list will get through. + verifyFuzzyMatch("stat", "Outstanding"); + } + + @Test(timeout=30000) + public void testFourLetterWordsEnableAllCommandsThroughAsterisk() throws Exception { + stopServer(); + ServerCnxn.resetWhiteList(); + System.setProperty("zookeeper.4lw.commands.whitelist", "*"); + startServer(); + verifyAllCommandsSuccess(); + } + + @Test(timeout=30000) + public void testFourLetterWordsEnableAllCommandsThroughExplicitList() throws Exception { + stopServer(); + ServerCnxn.resetWhiteList(); + System.setProperty("zookeeper.4lw.commands.whitelist", + "ruok, envi, conf, stat, srvr, cons, dump," + + "wchs, wchp, wchc, srst, crst, " + + "mntr, gtmk, isro, stmk"); + startServer(); + verifyAllCommandsSuccess(); + } + + private void verifyAllCommandsSuccess() throws Exception { + verifyExactMatch("ruok", "imok"); + verifyFuzzyMatch("envi", "java.version"); + verifyFuzzyMatch("conf", "clientPort"); + verifyFuzzyMatch("stat", "Outstanding"); + verifyFuzzyMatch("srvr", "Outstanding"); + verifyFuzzyMatch("cons", "queued"); + verifyFuzzyMatch("dump", "Session"); + verifyFuzzyMatch("wchs", "watches"); + verifyFuzzyMatch("wchp", ""); + verifyFuzzyMatch("wchc", ""); + + verifyFuzzyMatch("srst", "reset"); + verifyFuzzyMatch("crst", "reset"); + + verifyFuzzyMatch("stat", "Outstanding"); + verifyFuzzyMatch("srvr", "Outstanding"); + verifyFuzzyMatch("cons", "queued"); + verifyFuzzyMatch("gtmk", "306"); + verifyFuzzyMatch("isro", "rw"); + + TestableZooKeeper zk = createClient(); + String sid = getHexSessionId(zk.getSessionId()); + + verifyFuzzyMatch("stat", "queued"); + verifyFuzzyMatch("srvr", "Outstanding"); + verifyFuzzyMatch("cons", sid); + verifyFuzzyMatch("dump", sid); + + zk.getData("/", true, null); + + verifyFuzzyMatch("stat", "queued"); + verifyFuzzyMatch("srvr", "Outstanding"); + verifyFuzzyMatch("cons", sid); + verifyFuzzyMatch("dump", sid); + + verifyFuzzyMatch("wchs", "watching 1"); + verifyFuzzyMatch("wchp", sid); + verifyFuzzyMatch("wchc", sid); + zk.close(); + + verifyExactMatch("ruok", "imok"); + verifyFuzzyMatch("envi", "java.version"); + verifyFuzzyMatch("conf", "clientPort"); + verifyFuzzyMatch("stat", "Outstanding"); + verifyFuzzyMatch("srvr", "Outstanding"); + verifyFuzzyMatch("cons", "queued"); + verifyFuzzyMatch("dump", "Session"); + verifyFuzzyMatch("wchs", "watch"); + verifyFuzzyMatch("wchp", ""); + verifyFuzzyMatch("wchc", ""); + + verifyFuzzyMatch("srst", "reset"); + verifyFuzzyMatch("crst", "reset"); + + verifyFuzzyMatch("stat", "Outstanding"); + verifyFuzzyMatch("srvr", "Outstanding"); + verifyFuzzyMatch("cons", "queued"); + verifyFuzzyMatch("mntr", "zk_server_state\tstandalone"); + verifyFuzzyMatch("mntr", "num_alive_connections"); + verifyFuzzyMatch("stat", "Connections"); + verifyFuzzyMatch("srvr", "Connections"); + } + + private void verifyAllCommandsFail() throws Exception { + verifyExactMatch("ruok", generateExpectedMessage("ruok")); + verifyExactMatch("conf", generateExpectedMessage("conf")); + verifyExactMatch("cons", generateExpectedMessage("cons")); + verifyExactMatch("crst", generateExpectedMessage("crst")); + verifyExactMatch("dump", generateExpectedMessage("dump")); + verifyExactMatch("envi", generateExpectedMessage("envi")); + verifyExactMatch("gtmk", generateExpectedMessage("gtmk")); + verifyExactMatch("stmk", generateExpectedMessage("stmk")); + verifyExactMatch("srst", generateExpectedMessage("srst")); + verifyExactMatch("wchc", generateExpectedMessage("wchc")); + verifyExactMatch("wchp", generateExpectedMessage("wchp")); + verifyExactMatch("wchs", generateExpectedMessage("wchs")); + verifyExactMatch("mntr", generateExpectedMessage("mntr")); + verifyExactMatch("isro", generateExpectedMessage("isro")); + + // srvr is enabled by default due to the sad fact zkServer.sh uses it. + verifyFuzzyMatch("srvr", "Outstanding"); + } + + private void verifyFuzzyMatch(String cmd, String expected) throws IOException { + String resp = sendRequest(cmd); + LOG.info("cmd " + cmd + " expected " + expected + " got " + resp); + Assert.assertTrue(resp.contains(expected)); + } + + private String generateExpectedMessage(String command) { + return command + " is not executed because it is not in the whitelist."; + } + + private void verifyExactMatch(String cmd, String expected) throws IOException { + String resp = sendRequest(cmd); + LOG.info("cmd " + cmd + " expected an exact match of " + expected + "; got " + resp); + Assert.assertTrue(resp.trim().equals(expected)); + } + + private String sendRequest(String cmd) throws IOException { + HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); + return send4LetterWord(hpobj.host, hpobj.port, cmd); + } + + private String sendRequest(String cmd, int timeout) throws IOException { + HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); + return send4LetterWord(hpobj.host, hpobj.port, cmd, timeout); + } +} From 09555983be93e5278b1ab8a71e3ca9f54ea6c374 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Tue, 7 Mar 2017 22:56:26 +0530 Subject: [PATCH 392/444] ZOOKEEPER-2710: Regenerate documentation for branch-3.4 release --- docs/bookkeeperConfig.html | 2 +- docs/bookkeeperConfig.pdf | Bin 13824 -> 13797 bytes docs/bookkeeperOverview.html | 2 +- docs/bookkeeperOverview.pdf | Bin 147596 -> 147558 bytes docs/bookkeeperProgrammer.html | 2 +- docs/bookkeeperProgrammer.pdf | Bin 24991 -> 24949 bytes docs/bookkeeperStarted.html | 2 +- docs/bookkeeperStarted.pdf | Bin 17130 -> 17107 bytes docs/bookkeeperStream.html | 2 +- docs/bookkeeperStream.pdf | Bin 13215 -> 13192 bytes docs/index.html | 2 +- docs/index.pdf | Bin 13525 -> 13515 bytes docs/javaExample.html | 2 +- docs/javaExample.pdf | Bin 33852 -> 33767 bytes docs/linkmap.html | 2 +- docs/linkmap.pdf | Bin 12472 -> 12459 bytes docs/recipes.html | 2 +- docs/recipes.pdf | Bin 31072 -> 31021 bytes docs/zookeeperAdmin.html | 74 ++++++++++++------ docs/zookeeperAdmin.pdf | Bin 78642 -> 80536 bytes docs/zookeeperHierarchicalQuorums.html | 2 +- docs/zookeeperHierarchicalQuorums.pdf | Bin 6660 -> 6654 bytes docs/zookeeperInternals.html | 2 +- docs/zookeeperInternals.pdf | Bin 48872 -> 48810 bytes docs/zookeeperJMX.html | 2 +- docs/zookeeperJMX.pdf | Bin 16498 -> 16475 bytes docs/zookeeperObservers.html | 2 +- docs/zookeeperObservers.pdf | Bin 12888 -> 12871 bytes docs/zookeeperOver.html | 2 +- docs/zookeeperOver.pdf | Bin 302521 -> 302472 bytes docs/zookeeperProgrammers.html | 4 +- docs/zookeeperProgrammers.pdf | Bin 134255 -> 134099 bytes docs/zookeeperQuotas.html | 2 +- docs/zookeeperQuotas.pdf | Bin 11195 -> 11184 bytes docs/zookeeperStarted.html | 2 +- docs/zookeeperStarted.pdf | Bin 28118 -> 28073 bytes docs/zookeeperTutorial.html | 2 +- docs/zookeeperTutorial.pdf | Bin 30557 -> 30478 bytes .../content/xdocs/zookeeperAdmin.xml | 2 +- 39 files changed, 71 insertions(+), 43 deletions(-) diff --git a/docs/bookkeeperConfig.html b/docs/bookkeeperConfig.html index d3a35af51c4..ff8d4e8fdad 100644 --- a/docs/bookkeeperConfig.html +++ b/docs/bookkeeperConfig.html @@ -374,7 +374,7 @@

            ZooKeeper Metadata

            @@ -1709,6 +1710,27 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant">
            + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java/test/bin/test-github-pr.sh b/src/java/test/bin/test-github-pr.sh new file mode 100755 index 00000000000..e155769f268 --- /dev/null +++ b/src/java/test/bin/test-github-pr.sh @@ -0,0 +1,616 @@ +#!/usr/bin/env bash +# 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. + + +#set -x + +### Setup some variables. +### GIT_COMMIT and BUILD_URL are set by Hudson if it is run by patch process +### Read variables from properties file +. `dirname $0`/test-patch.properties + +############################################################################### +parseArgs() { + case "$1" in + QABUILD) + ### Set QABUILD to true to indicate that this script is being run by Hudson + QABUILD=true + if [[ $# != 14 ]] ; then + echo "ERROR: usage $0 QABUILD " + cleanupAndExit 0 + fi + PATCH_DIR=$2 + PS=$3 + WGET=$4 + JIRACLI=$5 + GIT=$6 + GREP=$7 + PATCH=$8 + FINDBUGS_HOME=$9 + FORREST_HOME=${10} + BASEDIR=${11} + JIRA_PASSWD=${12} + JAVA5_HOME=${13} + CURL=${14} + if [ ! -e "$PATCH_DIR" ] ; then + mkdir -p $PATCH_DIR + fi + + ## Obtain PR number and title + PULLREQUEST_ID=${GIT_PR_NUMBER} + PULLREQUEST_TITLE="${GIT_PR_TITLE}" + + ## Extract jira number from PR title + local prefix=${PULLREQUEST_TITLE%ZOOKEEPER\-[0-9]*} + local noprefix=${PULLREQUEST_TITLE#$prefix} + local regex='\(ZOOKEEPER-.[0-9]*\)' + defect=$(expr "$noprefix" : ${regex}) + + echo "Pull request id: ${PULLREQUEST_ID}" + echo "Pull request title: ${PULLREQUEST_TITLE}" + echo "Defect number: ${defect}" + + JIRA_COMMENT="GitHub Pull Request ${PULLREQUEST_NUMBER} Build + " + ;; + DEVELOPER) + ### Set QABUILD to false to indicate that this script is being run by a developer + QABUILD=false + if [[ $# != 10 ]] ; then + echo "ERROR: usage $0 DEVELOPER " + cleanupAndExit 0 + fi + PATCH_DIR=$3 + PATCH_FILE=${PATCH_DIR}/patch + curl -L $2.diff > ${PATCH_FILE} + ### PATCH_FILE contains the location of the patchfile + if [[ ! -e "$PATCH_FILE" ]] ; then + echo "Unable to locate the patch file $PATCH_FILE" + cleanupAndExit 0 + fi + ### Check if $PATCH_DIR exists. If it does not exist, create a new directory + if [[ ! -e "$PATCH_DIR" ]] ; then + mkdir "$PATCH_DIR" + if [[ $? == 0 ]] ; then + echo "$PATCH_DIR has been created" + else + echo "Unable to create $PATCH_DIR" + cleanupAndExit 0 + fi + fi + GIT=$4 + GREP=$5 + PATCH=$6 + FINDBUGS_HOME=$7 + FORREST_HOME=$8 + BASEDIR=$9 + JAVA5_HOME=${10} + ### Obtain the patch filename to append it to the version number + local subject=`grep "Subject:" ${PATCH_FILE}` + local length=`expr match ${subject} ZOOKEEPER-[0-9]*` + local position=`expr index ${subject} ZOOKEEPER-` + defect=${${subject:$position:$length}#ZOOKEEPER-} + ;; + *) + echo "ERROR: usage $0 QABUILD [args] | DEVELOPER [args]" + cleanupAndExit 0 + ;; + esac +} + +############################################################################### +checkout () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Testing patch for pull request ${PULLREQUEST_ID}." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + ### When run by a developer, if the workspace contains modifications, do not continue + # Ref http://stackoverflow.com/a/2659808 for details on checking dirty status + ${GIT} diff-index --quiet HEAD + if [[ $? -ne 0 ]] ; then + uncommitted=`${GIT} diff --name-only HEAD` + uncommitted="You have the following files with uncommitted changes:${NEWLINE}${uncommitted}" + fi + untracked="$(${GIT} ls-files --exclude-standard --others)" && test -z "${untracked}" + if [[ $? -ne 0 ]] ; then + untracked="You have untracked and unignored files:${NEWLINE}${untracked}" + fi + + if [[ $QABUILD == "false" ]] ; then + if [[ $uncommitted || $untracked ]] ; then + echo "ERROR: can't run in a workspace that contains the following modifications" + echo "" + echo "${uncommitted}" + echo "" + echo "${untracked}" + cleanupAndExit 1 + fi + else + # I don't believe we need to do anything here - the jenkins job will + # cleanup the environment for us ("cleanup before checkout" action) + # on the precommit jenkins job + echo + fi + return $? +} + +############################################################################### +setup () { + ### exit if warnings are NOT defined in the properties file + if [ -z "$OK_FINDBUGS_WARNINGS" ] || [[ -z "$OK_JAVADOC_WARNINGS" ]] || [[ -z $OK_RELEASEAUDIT_WARNINGS ]]; then + echo "Please define the following properties in test-patch.properties file" + echo "OK_FINDBUGS_WARNINGS" + echo "OK_RELEASEAUDIT_WARNINGS" + echo "OK_JAVADOC_WARNINGS" + cleanupAndExit 1 + fi + ### get pull request diff + ${CURL} -L ${GIT_PR_URL}.diff > $PATCH_DIR/patch + + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Pre-build trunk to verify trunk stability and javac warnings" + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1" + $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1 + if [[ $? != 0 ]] ; then + echo "Trunk compilation is broken?" + cleanupAndExit 1 + fi +} + +############################################################################### +### Check for @author tags in the patch +checkAuthor () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Checking there are no @author tags in the patch." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + authorTags=`$GREP -c -i '@author' $PATCH_DIR/patch` + echo "There appear to be $authorTags @author tags in the patch." + if [[ $authorTags != 0 ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 @author. The patch appears to contain $authorTags @author tags which the Zookeeper community has agreed to not allow in code contributions." + return 1 + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 @author. The patch does not contain any @author tags." + return 0 +} + +############################################################################### +### Check for tests in the patch +checkTests () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Checking there are new or changed tests in the patch." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + testReferences=`$GREP -c -i '/test' $PATCH_DIR/patch` + echo "There appear to be $testReferences test files referenced in the patch." + if [[ $testReferences == 0 ]] ; then + if [[ $QABUILD == "true" ]] ; then + patchIsDoc=`$GREP -c -i 'title="documentation' $PATCH_DIR/jira` + if [[ $patchIsDoc != 0 ]] ; then + echo "The patch appears to be a documentation patch that doesn't require tests." + JIRA_COMMENT="$JIRA_COMMENT + + +0 tests included. The patch appears to be a documentation patch that doesn't require tests." + return 0 + fi + fi + JIRA_COMMENT="$JIRA_COMMENT + + -1 tests included. The patch doesn't appear to include any new or modified tests. + Please justify why no new tests are needed for this patch. + Also please list what manual steps were performed to verify this patch." + return 1 + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 tests included. The patch appears to include $testReferences new or modified tests." + return 0 +} + +############################################################################### +### Check there are no javadoc warnings +checkJavadocWarnings () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Determining number of patched javadoc warnings." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt" + $ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt + javadocWarnings=`$GREP -o '\[javadoc\] [0-9]* warning' $PATCH_DIR/patchJavadocWarnings.txt | awk '{total += $2} END {print total}'` + echo "" + echo "" + echo "There appear to be $javadocWarnings javadoc warnings generated by the patched build." + + ### if current warnings greater than OK_JAVADOC_WARNINGS + if [[ $javadocWarnings > $OK_JAVADOC_WARNINGS ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 javadoc. The javadoc tool appears to have generated `expr $(($javadocWarnings-$OK_JAVADOC_WARNINGS))` warning messages." + return 1 + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 javadoc. The javadoc tool did not generate any warning messages." + return 0 +} + +############################################################################### +### Check there are no changes in the number of Javac warnings +checkJavacWarnings () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Determining number of patched javac warnings." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1" + $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1 + if [[ $? != 0 ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 javac. The patch appears to cause tar ant target to fail." + return 1 + fi + ### Compare trunk and patch javac warning numbers + if [[ -f $PATCH_DIR/patchJavacWarnings.txt ]] ; then + trunkJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/trunkJavacWarnings.txt | awk '{total += $2} END {print total}'` + patchJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/patchJavacWarnings.txt | awk '{total += $2} END {print total}'` + echo "There appear to be $trunkJavacWarnings javac compiler warnings before the patch and $patchJavacWarnings javac compiler warnings after applying the patch." + if [[ $patchJavacWarnings != "" && $trunkJavacWarnings != "" ]] ; then + if [[ $patchJavacWarnings -gt $trunkJavacWarnings ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 javac. The applied patch generated $patchJavacWarnings javac compiler warnings (more than the trunk's current $trunkJavacWarnings warnings)." + return 1 + fi + fi + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 javac. The applied patch does not increase the total number of javac compiler warnings." + return 0 +} + +############################################################################### +### Check there are no changes in the number of release audit (RAT) warnings +checkReleaseAuditWarnings () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Determining number of patched release audit warnings." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + echo "$ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1" + $ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1 + + ### Compare trunk and patch release audit warning numbers + if [[ -f $PATCH_DIR/patchReleaseAuditWarnings.txt ]] ; then + patchReleaseAuditWarnings=`$GREP -c '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt` + echo "" + echo "" + echo "There appear to be $OK_RELEASEAUDIT_WARNINGS release audit warnings before the patch and $patchReleaseAuditWarnings release audit warnings after applying the patch." + if [[ $patchReleaseAuditWarnings != "" && $OK_RELEASEAUDIT_WARNINGS != "" ]] ; then + if [[ $patchReleaseAuditWarnings -gt $OK_RELEASEAUDIT_WARNINGS ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 release audit. The applied patch generated $patchReleaseAuditWarnings release audit warnings (more than the trunk's current $OK_RELEASEAUDIT_WARNINGS warnings)." + $GREP '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt > $PATCH_DIR/patchReleaseAuditProblems.txt + echo "Lines that start with ????? in the release audit report indicate files that do not have an Apache license header." >> $PATCH_DIR/patchReleaseAuditProblems.txt + JIRA_COMMENT_FOOTER="Release audit warnings: $BUILD_URL/artifact/trunk/patchprocess/patchReleaseAuditProblems.txt +$JIRA_COMMENT_FOOTER" + return 1 + fi + fi + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 release audit. The applied patch does not increase the total number of release audit warnings." + return 0 +} + +############################################################################### +### Check there are no changes in the number of Checkstyle warnings +checkStyle () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Determining number of patched checkstyle warnings." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + echo "THIS IS NOT IMPLEMENTED YET" + echo "" + echo "" + echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle" + $ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle + JIRA_COMMENT_FOOTER="Checkstyle results: $BUILD_URL/artifact/trunk/build/test/checkstyle-errors.html +$JIRA_COMMENT_FOOTER" + ### TODO: calculate actual patchStyleErrors +# patchStyleErrors=0 +# if [[ $patchStyleErrors != 0 ]] ; then +# JIRA_COMMENT="$JIRA_COMMENT +# +# -1 checkstyle. The patch generated $patchStyleErrors code style errors." +# return 1 +# fi +# JIRA_COMMENT="$JIRA_COMMENT +# +# +1 checkstyle. The patch generated 0 code style errors." + return 0 +} + +############################################################################### +### Check there are no changes in the number of Findbugs warnings +checkFindbugsWarnings () { + findbugs_version=`${FINDBUGS_HOME}/bin/findbugs -version` + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Determining number of patched Findbugs warnings." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + echo "$ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= findbugs" + $ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -Dforrest.home=${FORREST_HOME} -DZookeeperPatchProcess= findbugs + if [ $? != 0 ] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 findbugs. The patch appears to cause Findbugs (version ${findbugs_version}) to fail." + return 1 + fi +JIRA_COMMENT_FOOTER="Findbugs warnings: $BUILD_URL/artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html +$JIRA_COMMENT_FOOTER" + cp $BASEDIR/build/test/findbugs/*.xml $PATCH_DIR/patchFindbugsWarnings.xml + $FINDBUGS_HOME/bin/setBugDatabaseInfo -timestamp "01/01/2000" \ + $PATCH_DIR/patchFindbugsWarnings.xml \ + $PATCH_DIR/patchFindbugsWarnings.xml + findbugsWarnings=`$FINDBUGS_HOME/bin/filterBugs -first "01/01/2000" $PATCH_DIR/patchFindbugsWarnings.xml \ + $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml | /usr/bin/awk '{print $1}'` + $FINDBUGS_HOME/bin/convertXmlToText -html \ + $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml \ + $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html + cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html $PATCH_DIR/newPatchFindbugsWarnings.html + cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml $PATCH_DIR/newPatchFindbugsWarnings.xml + + ### if current warnings greater than OK_FINDBUGS_WARNINGS + if [[ $findbugsWarnings > $OK_FINDBUGS_WARNINGS ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 findbugs. The patch appears to introduce `expr $(($findbugsWarnings-$OK_FINDBUGS_WARNINGS))` new Findbugs (version ${findbugs_version}) warnings." + return 1 + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 findbugs. The patch does not introduce any new Findbugs (version ${findbugs_version}) warnings." + return 0 +} + +############################################################################### +### Run the test-core target +runCoreTests () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Running core tests." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + + ### Kill any rogue build processes from the last attempt + $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null + + echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes -Dtest.junit.threads=8 -Dcompile.c++=yes -Dforrest.home=$FORREST_HOME -Djava5.home=$JAVA5_HOME test-core" + $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes -Dtest.junit.threads=8 -Dcompile.c++=yes -Dforrest.home=$FORREST_HOME -Djava5.home=$JAVA5_HOME test-core + if [[ $? != 0 ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 core tests. The patch failed core unit tests." + return 1 + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 core tests. The patch passed core unit tests." + return 0 +} + +############################################################################### +### Run the test-contrib target +runContribTests () { + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Running contrib tests." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + + ### Kill any rogue build processes from the last attempt + $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null + + echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes test-contrib" + $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes test-contrib + if [[ $? != 0 ]] ; then + JIRA_COMMENT="$JIRA_COMMENT + + -1 contrib tests. The patch failed contrib unit tests." + return 1 + fi + JIRA_COMMENT="$JIRA_COMMENT + + +1 contrib tests. The patch passed contrib unit tests." + return 0 +} + +############################################################################### +### Submit a comment to the defect's Jira +submitJiraComment () { + local result=$1 + ### Do not output the value of JIRA_COMMENT_FOOTER when run by a developer + if [[ $QABUILD == "false" ]] ; then + JIRA_COMMENT_FOOTER="" + fi + if [[ $result == 0 ]] ; then + comment="+1 overall. $JIRA_COMMENT + +$JIRA_COMMENT_FOOTER" + else + comment="-1 overall. $JIRA_COMMENT + +$JIRA_COMMENT_FOOTER" + fi + ### Output the test result to the console + echo " + + + +$comment" + + if [[ $QABUILD == "true" ]] ; then + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Adding comment to Jira." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + ### Update Jira with a comment + export USER=jenkins + $JIRACLI -s https://issues.apache.org/jira -a addcomment -u hadoopqa -p $JIRA_PASSWD --comment "$comment" --issue $defect + $JIRACLI -s https://issues.apache.org/jira -a logout -u hadoopqa -p $JIRA_PASSWD + fi +} + +############################################################################### +### Cleanup files +cleanupAndExit () { + local result=$1 + if [[ $QABUILD == "true" ]] ; then + if [ -e "$PATCH_DIR" ] ; then + mv $PATCH_DIR $BASEDIR + fi + fi + echo "" + echo "" + echo "======================================================================" + echo "======================================================================" + echo " Finished build." + echo "======================================================================" + echo "======================================================================" + echo "" + echo "" + exit $result +} + +############################################################################### +############################################################################### +############################################################################### + +JIRA_COMMENT="" +JIRA_COMMENT_FOOTER="Console output: $BUILD_URL/console + +This message is automatically generated." + +### Check if arguments to the script have been specified properly or not +echo "----- Going to parser args -----" +parseArgs $@ +cd $BASEDIR + +echo "----- Parsed args, going to checkout -----" +checkout +RESULT=$? +if [[ $QABUILD == "true" ]] ; then + if [[ $RESULT != 0 ]] ; then + exit 100 + fi +fi +setup +checkAuthor +(( RESULT = RESULT + $? )) + +checkTests +checkTestsResult=$? +(( RESULT = RESULT + $checkTestsResult )) +if [[ $checkTestsResult != 0 ]] ; then + submitJiraComment 1 + cleanupAndExit 1 +fi +checkJavadocWarnings +(( RESULT = RESULT + $? )) +checkJavacWarnings +(( RESULT = RESULT + $? )) +### Checkstyle not implemented yet +#checkStyle +#(( RESULT = RESULT + $? )) +checkFindbugsWarnings +(( RESULT = RESULT + $? )) +checkReleaseAuditWarnings +(( RESULT = RESULT + $? )) +### Do not call these when run by a developer +if [[ $QABUILD == "true" ]] ; then + runCoreTests + (( RESULT = RESULT + $? )) + runContribTests + (( RESULT = RESULT + $? )) +fi +JIRA_COMMENT_FOOTER="Test results: $BUILD_URL/testReport/ +$JIRA_COMMENT_FOOTER" + +submitJiraComment $RESULT +cleanupAndExit $RESULT From 1b59e3cde6457f81951f06c31548cc2064ed69c6 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Wed, 8 Mar 2017 12:21:40 +0530 Subject: [PATCH 394/444] Preparing for release 3.4.10 --- NOTICE.txt | 2 +- build.xml | 2 +- docs/releasenotes.html | 105 ++++++++++++++++++++++++++++++ src/NOTICE.txt | 2 +- src/c/NOTICE.txt | 2 +- src/c/configure.ac | 2 +- src/c/include/winconfig.h | 6 +- src/c/include/zookeeper_version.h | 2 +- 8 files changed, 114 insertions(+), 9 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 5f43319d785..877a3f50637 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2016 The Apache Software Foundation +Copyright 2009-2017 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/build.xml b/build.xml index 35f37da9cea..7e84be9a6ed 100644 --- a/build.xml +++ b/build.xml @@ -30,7 +30,7 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - + diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 337a7ea9c7f..e4529bc24f1 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -203,6 +203,111 @@ PDF + Release Notes - ZooKeeper - Version 3.4.10 + +

            Sub-task +

            +
              +
            • [ZOOKEEPER-2692] - Fix race condition in testWatchAutoResetWithPending +
            • +
            + +

            Bug +

            +
              +
            • [ZOOKEEPER-2044] - CancelledKeyException in zookeeper branch-3.4 +
            • +
            • [ZOOKEEPER-2383] - Startup race in ZooKeeperServer +
            • +
            • [ZOOKEEPER-2465] - Documentation copyright notice is out of date. +
            • +
            • [ZOOKEEPER-2467] - NullPointerException when redo Command is passed negative value +
            • +
            • [ZOOKEEPER-2470] - ServerConfig#parse(String[]) ignores tickTime +
            • +
            • [ZOOKEEPER-2542] - Update NOTICE file with Netty notice in 3.4 +
            • +
            • [ZOOKEEPER-2552] - Revisit release note doc and remove the items which are not related to the released version +
            • +
            • [ZOOKEEPER-2558] - Potential memory leak in recordio.c +
            • +
            • [ZOOKEEPER-2573] - Modify Info.REVISION to adapt git repo +
            • +
            • [ZOOKEEPER-2574] - PurgeTxnLog can inadvertently delete required txn log files +
            • +
            • [ZOOKEEPER-2579] - ZooKeeper server should verify that dataDir and snapDir are writeable before starting +
            • +
            • [ZOOKEEPER-2606] - SaslServerCallbackHandler#handleAuthorizeCallback() should log the exception +
            • +
            • [ZOOKEEPER-2617] - correct a few spelling typos +
            • +
            • [ZOOKEEPER-2622] - ZooTrace.logQuorumPacket does nothing +
            • +
            • [ZOOKEEPER-2633] - Build failure in contrib/zkfuse with gcc 6.x +
            • +
            • [ZOOKEEPER-2646] - Java target in branch 3.4 doesn't match documentation +
            • +
            • [ZOOKEEPER-2651] - Missing src/pom.template in release +
            • +
            • [ZOOKEEPER-2652] - Fix HierarchicalQuorumTest.java +
            • +
            • [ZOOKEEPER-2671] - Fix compilation error in branch-3.4 +
            • +
            • [ZOOKEEPER-2678] - Large databases take a long time to regain a quorum +
            • +
            • [ZOOKEEPER-2680] - Correct DataNode.getChildren() inconsistent behaviour. +
            • +
            • [ZOOKEEPER-2689] - Fix Kerberos Authentication related test cases +
            • +
            • [ZOOKEEPER-2693] - DOS attack on wchp/wchc four letter words (4lw) +
            • +
            • [ZOOKEEPER-2696] - Eclipse ant task no longer determines correct classpath for tests after ZOOKEEPER-2689 +
            • +
            • [ZOOKEEPER-2710] - Regenerate documentation for branch-3.4 release +
            • +
            + +

            Improvement +

            +
              +
            • [ZOOKEEPER-2479] - Add 'electionTimeTaken' value in LeaderMXBean and FollowerMXBean +
            • +
            • [ZOOKEEPER-2507] - C unit test improvement: line break between 'ZooKeeper server started' and 'Running' +
            • +
            • [ZOOKEEPER-2557] - Update gitignore to account for other file extensions +
            • +
            • [ZOOKEEPER-2594] - Use TLS for downloading artifacts during build +
            • +
            • [ZOOKEEPER-2620] - Add comments to testReadOnlySnapshotDir and testReadOnlyTxnLogDir indicating that the tests will fail when run as root +
            • +
            • [ZOOKEEPER-2672] - Remove CHANGE.txt +
            • +
            • [ZOOKEEPER-2682] - Make it optional to fail build on test failure +
            • +
            + +

            New Feature +

            +
              +
            • [ZOOKEEPER-1045] - Support Quorum Peer mutual authentication via SASL +
            • +
            + +

            Test +

            +
              +
            • [ZOOKEEPER-2502] - Flaky Test: org.apache.zookeeper.server.quorum.CnxManagerTest.testCnxFromFutureVersion +
            • +
            • [ZOOKEEPER-2650] - Test Improvement by adding more QuorumPeer Auth related test cases +
            • +
            • [ZOOKEEPER-2656] - Fix ServerConfigTest#testValidArguments test case failures +
            • +
            • [ZOOKEEPER-2664] - ClientPortBindTest#testBindByAddress may fail due to "No such device" exception +
            • +
            • [ZOOKEEPER-2665] - Port QA github pull request build to branch 3.4 and 3.5 +
            • +
            + Release Notes - ZooKeeper - Version 3.4.9

            Sub-task diff --git a/src/NOTICE.txt b/src/NOTICE.txt index a902c6ab1e8..80d018867ba 100644 --- a/src/NOTICE.txt +++ b/src/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2016 The Apache Software Foundation +Copyright 2009-2017 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/src/c/NOTICE.txt b/src/c/NOTICE.txt index 07ef1ad516c..440b74d5aa2 100644 --- a/src/c/NOTICE.txt +++ b/src/c/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2011 The Apache Software Foundation +Copyright 2009-2017 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/src/c/configure.ac b/src/c/configure.ac index be00af90626..21d752c9b36 100644 --- a/src/c/configure.ac +++ b/src/c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.4.9,[user@zookeeper.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.4.10,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index ce42b26abe0..9d936ec99e2 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -117,7 +117,7 @@ #define PACKAGE_NAME "zookeeper C client" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "zookeeper C client 3.4.9 win32" +#define PACKAGE_STRING "zookeeper C client 3.4.10 win32" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "c-client-src" @@ -126,7 +126,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.4.9" +#define PACKAGE_VERSION "3.4.10" /* poll() second argument type */ #define POLL_NFDS_TYPE @@ -138,7 +138,7 @@ #define TIME_WITH_SYS_TIME /* Version number of package */ -#define VERSION "3.4.9" +#define VERSION "3.4.10" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h index 57790c49aee..186dc74d6d1 100644 --- a/src/c/include/zookeeper_version.h +++ b/src/c/include/zookeeper_version.h @@ -24,7 +24,7 @@ extern "C" { #define ZOO_MAJOR_VERSION 3 #define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 9 +#define ZOO_PATCH_VERSION 10 #ifdef __cplusplus } From 3e5000f5f5b30b838db709fa95228e556612eacf Mon Sep 17 00:00:00 2001 From: Michael Han Date: Thu, 16 Mar 2017 21:43:45 +0530 Subject: [PATCH 395/444] ZOOKEEPER-2716: Flaky Test: org.apache.zookeeper.server.SessionTrackerTest.testAddSessionAfterSessionExpiry We can't let the gate open until we increase the closed session count. Otherwise depends on timing, the test thread might see old session close count value between gate open and the session count actually gets increased. Author: Michael Han Reviewers: Abraham Fine , Rakesh Radhakrishnan Closes #187 from hanm/ZOOKEEPER-2716 (cherry picked from commit 86438f9c6e65b2a95d709bf575cdcc2abc1c2e8b) Signed-off-by: Rakesh Radhakrishnan --- .../test/org/apache/zookeeper/server/SessionTrackerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java b/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java index 61072e677d9..f42710292b8 100644 --- a/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java +++ b/src/java/test/org/apache/zookeeper/server/SessionTrackerTest.java @@ -143,8 +143,8 @@ public FirstProcessor(ZooKeeperServer zks, public void processRequest(Request request) { // check session close request if (request.type == OpCode.closeSession) { - latch.countDown(); countOfCloseSessionReq++; + latch.countDown(); } } From 902bee6410a565aef59ab6b9834fa9e1ead3a742 Mon Sep 17 00:00:00 2001 From: Kyle Nusbaum Date: Thu, 16 Mar 2017 12:58:29 -0700 Subject: [PATCH 396/444] ZOOKEEPER-2726 ZOOKEEPER-2693: Patch for introduces potential race condition #196 Author: Kyle Nusbaum Reviewers: Govind Menon, Kishor Patil , Michael Han Closes #197 from knusbaum/ZOOKEEPER-2726-3.4 --- src/java/main/org/apache/zookeeper/server/ServerCnxn.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java index 6b93e93f140..b554df96f2f 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java @@ -245,7 +245,7 @@ public String toString() { private static boolean whiteListInitialized = false; // @VisibleForTesting - public static void resetWhiteList() { + public synchronized static void resetWhiteList() { whiteListInitialized = false; whiteListedCommands.clear(); } @@ -278,7 +278,7 @@ public static boolean isKnown(int command) { * @param command The command string. * @return true if the specified command is enabled. */ - public static boolean isEnabled(String command) { + public synchronized static boolean isEnabled(String command) { if (whiteListInitialized) { return whiteListedCommands.contains(command); } From 5897852dba9bbaf78f73bc34ed9cbea5f7c9029a Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Wed, 22 Mar 2017 13:02:16 +0530 Subject: [PATCH 397/444] ZOOKEEPER-2712: MiniKdc test case intermittently failing due to principal not found in Kerberos database After long long analysis, I have observed concurrency issues at `ApacheDs `(used for unit test) causing the trouble and failing minikdc test cases. Introduced delay between the ZK server's startup to avoid simultaneous login context init across servers. Also, modified few test code for better debugging. Author: Rakesh Radhakrishnan Reviewers: Michael Han Closes #200 from rakeshadr/ZK-2712 --- .../zookeeper/server/quorum/QuorumPeer.java | 11 ++++ .../zookeeper/server/quorum/auth/MiniKdc.java | 2 +- .../quorum/auth/QuorumAuthTestBase.java | 60 +++++++++++++++---- .../quorum/auth/QuorumAuthUpgradeTest.java | 10 ++-- .../quorum/auth/QuorumDigestAuthTest.java | 10 ++-- .../quorum/auth/QuorumKerberosAuthTest.java | 11 +++- .../auth/QuorumKerberosHostBasedAuthTest.java | 20 +++++-- 7 files changed, 92 insertions(+), 32 deletions(-) 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 889ee62a851..9eeeb5d3ee1 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -98,6 +98,10 @@ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider QuorumCnxManager qcm; QuorumAuthServer authServer; QuorumAuthLearner authLearner; + // VisibleForTesting. This flag is used to know whether qLearner's and + // qServer's login context has been initialized as ApacheDS has concurrency + // issues. Refer https://issues.apache.org/jira/browse/ZOOKEEPER-2712 + private boolean authInitialized = false; /* ZKDatabase is a top level member of quorumpeer * which will be used in all the zookeeperservers @@ -571,6 +575,7 @@ public void initialize() throws SaslException { quorumServerLoginContext, authzHosts); authLearner = new SaslQuorumAuthLearner(isQuorumLearnerSaslAuthRequired(), quorumServicePrincipal, quorumLearnerLoginContext); + authInitialized = true; } else { authServer = new NullQuorumAuthServer(); authLearner = new NullQuorumAuthLearner(); @@ -1455,6 +1460,12 @@ private boolean isQuorumLearnerSaslAuthRequired() { return quorumLearnerSaslAuthRequired; } + // VisibleForTesting. Returns true if both the quorumlearner and + // quorumserver login has been finished. Otherwse, false. + public boolean hasAuthInitialized(){ + return authInitialized; + } + public QuorumCnxManager createCnxnManager() { return new QuorumCnxManager(this.getId(), this.getView(), diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java index 8e3cb1bed04..ebe541db554 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/MiniKdc.java @@ -224,7 +224,7 @@ public void run() { DEFAULT_CONFIG.setProperty(TRANSPORT, "TCP"); DEFAULT_CONFIG.setProperty(MAX_TICKET_LIFETIME, "86400000"); DEFAULT_CONFIG.setProperty(MAX_RENEWABLE_LIFETIME, "604800000"); - DEFAULT_CONFIG.setProperty(DEBUG, "false"); + DEFAULT_CONFIG.setProperty(DEBUG, "true"); } /** diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java index 4d4b071b304..219d5bc6f85 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java @@ -66,10 +66,11 @@ public static void cleanupJaasConfig() { } protected String startQuorum(final int serverCount, - Map authConfigs, int authServerCount) throws IOException { + Map authConfigs, int authServerCount, + boolean delayedServerStartup) throws IOException { StringBuilder connectStr = new StringBuilder(); final int[] clientPorts = startQuorum(serverCount, 0, connectStr, - authConfigs, authServerCount); + authConfigs, authServerCount, delayedServerStartup); for (int i = 0; i < serverCount; i++) { Assert.assertTrue("waiting for server " + i + " being up", ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], @@ -98,7 +99,7 @@ protected String startQuorum(final int serverCount, int observerCount, throws IOException { StringBuilder connectStr = new StringBuilder(); final int[] clientPorts = startQuorum(serverCount, observerCount, - connectStr, authConfigs, authServerCount); + connectStr, authConfigs, authServerCount, false); for (int i = 0; i < serverCount; i++) { Assert.assertTrue("waiting for server " + i + " being up", ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], @@ -122,12 +123,15 @@ protected String startQuorum(final int serverCount, int observerCount, * configuration parameters for authentication * @param authServerCount * number of auth enabled servers + * @param delayedServerStartup + * true flag value to add delay between server's startup, false otherwise. * @return client port for the respective servers * @throws IOException */ protected int[] startQuorum(final int serverCount, int observerCount, StringBuilder connectStr, Map authConfigs, - int authServerCount) throws IOException { + int authServerCount, boolean delayedServerStartup) + throws IOException { final int clientPorts[] = new int[serverCount]; StringBuilder sb = new StringBuilder(); @@ -159,7 +163,7 @@ protected int[] startQuorum(final int serverCount, int observerCount, String obsCfgSection = quorumCfg + "\npeerType=observer"; quorumCfg = obsCfgSection; } - startServer(authConfigs, clientPorts, quorumCfg, i); + startServer(authConfigs, clientPorts[i], quorumCfg, i, delayedServerStartup); } // servers without any authentication configured for (int j = 0; j < serverCount - authServerCount; j++, i++) { @@ -167,20 +171,52 @@ protected int[] startQuorum(final int serverCount, int observerCount, String obsCfgSection = quorumCfg + "\npeerType=observer"; quorumCfg = obsCfgSection; } - MainThread mthread = new MainThread(i, clientPorts[i], quorumCfg); - mt.add(mthread); - mthread.start(); + startServer(null, clientPorts[i], quorumCfg, i, delayedServerStartup); } return clientPorts; } private void startServer(Map authConfigs, - final int[] clientPorts, String quorumCfg, int i) - throws IOException { - MainThread mthread = new MainThread(i, clientPorts[i], quorumCfg, - authConfigs); + final int clientPort, String quorumCfg, int i, + boolean delayedServerStartup) throws IOException { + MainThread mthread; + if (authConfigs != null) { + mthread = new MainThread(i, clientPort, quorumCfg, authConfigs); + } else { + mthread = new MainThread(i, clientPort, quorumCfg); + } mt.add(mthread); mthread.start(); + + if (delayedServerStartup) { + addDelayBeforeStartingNextServer(mthread); + } + } + + private void addDelayBeforeStartingNextServer(MainThread mThread) { + // Refer https://issues.apache.org/jira/browse/ZOOKEEPER-2712 + LOG.info("Waiting to finish login context init(Krb login), " + + "as there are potential concurrency issues in ApacheDS " + + "if multiple servers starts together!"); + int retries = 60; // 15secs delay + while (retries > 0) { + if (mThread.getQuorumPeer() != null + && mThread.getQuorumPeer().hasAuthInitialized()) { + try { + Thread.sleep(1000); // adding 1sec grace period. + } catch (InterruptedException e) { + LOG.info("Ignore InterruptedException"); + } + break; + } + // moving to next retry cycle + retries--; + try { + Thread.sleep(250); + } catch (InterruptedException e) { + LOG.info("Ignore InterruptedException"); + } + } } protected void startServer(MainThread restartPeer, diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java index 359324549e2..4eeccf34a90 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java @@ -83,7 +83,7 @@ public void testNullAuthLearnerServer() throws Exception { Map authConfigs = new HashMap(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); - String connectStr = startQuorum(2, authConfigs, 0); + String connectStr = startQuorum(2, authConfigs, 0, false); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); @@ -103,7 +103,7 @@ public void testAuthLearnerAgainstNullAuthServer() throws Exception { Map authConfigs = new HashMap(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); - String connectStr = startQuorum(2, authConfigs, 1); + String connectStr = startQuorum(2, authConfigs, 1, false); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); @@ -123,7 +123,7 @@ public void testAuthLearnerAgainstNoAuthRequiredServer() throws Exception { Map authConfigs = new HashMap(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); - String connectStr = startQuorum(2, authConfigs, 2); + String connectStr = startQuorum(2, authConfigs, 2, false); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); @@ -145,7 +145,7 @@ public void testAuthLearnerServer() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); - String connectStr = startQuorum(2, authConfigs, 2); + String connectStr = startQuorum(2, authConfigs, 2, false); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); @@ -178,7 +178,7 @@ public void testRollingUpgrade() throws Exception { Map authConfigs = new HashMap(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); - String connectStr = startQuorum(3, authConfigs, 0); + String connectStr = startQuorum(3, authConfigs, 0, false); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java index 18d1b92f5cf..c2f4cc319b9 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java @@ -87,7 +87,7 @@ public void testValidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); - String connectStr = startQuorum(3, authConfigs, 3); + String connectStr = startQuorum(3, authConfigs, 3, false); CountdownWatcher watcher = new CountdownWatcher(); zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); @@ -108,7 +108,7 @@ public void testSaslNotRequiredWithInvalidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid"); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); - String connectStr = startQuorum(3, authConfigs, 3); + String connectStr = startQuorum(3, authConfigs, 3, false); CountdownWatcher watcher = new CountdownWatcher(); zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); @@ -132,7 +132,7 @@ public void testSaslRequiredInvalidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); int serverCount = 2; final int[] clientPorts = startQuorum(serverCount, 0, - new StringBuilder(), authConfigs, serverCount); + new StringBuilder(), authConfigs, serverCount, false); for (int i = 0; i < serverCount; i++) { boolean waitForServerUp = ClientBase.waitForServerUp( "127.0.0.1:" + clientPorts[i], QuorumPeerTestBase.TIMEOUT); @@ -262,7 +262,7 @@ public void testNonAuthEnabledObserverJoiningAuthEnabledQuorum() // Starting auth enabled 3-node cluster. int totalServerCount = 3; String connectStr = startQuorum(totalServerCount, authConfigs, - totalServerCount); + totalServerCount, false); CountdownWatcher watcher = new CountdownWatcher(); zk = new ZooKeeper(connectStr.toString(), ClientBase.CONNECTION_TIMEOUT, @@ -310,7 +310,7 @@ public void testRelectionWithValidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); - String connectStr = startQuorum(3, authConfigs, 3); + String connectStr = startQuorum(3, authConfigs, 3, false); CountdownWatcher watcher = new CountdownWatcher(); zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java index 2cc56a76794..e3eddf7df21 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java @@ -46,7 +46,9 @@ public class QuorumKerberosAuthTest extends KerberosSecurityTestcase { + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" - + " debug=false\n" + + " debug=true\n" + + " doNotPrompt=true\n" + + " refreshKrb5Config=true\n" + " principal=\"" + KerberosTestUtils.getServerPrincipal() + "\";\n" + "};\n" + "QuorumLearner {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" @@ -54,7 +56,10 @@ public class QuorumKerberosAuthTest extends KerberosSecurityTestcase { + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" - + " debug=false\n" + + " debug=true\n" + + " doNotPrompt=true\n" + + " refreshKrb5Config=true\n" + + " isInitiator=true\n" + " principal=\"" + KerberosTestUtils.getLearnerPrincipal() + "\";\n" + "};\n"); setupJaasConfig(jaasEntries); } @@ -98,7 +103,7 @@ public void testValidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); - String connectStr = startQuorum(3, authConfigs, 3); + String connectStr = startQuorum(3, authConfigs, 3, true); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java index fcb76919f1b..55deefb8326 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java @@ -43,7 +43,7 @@ public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase { private static File keytabFile; private static String hostServerPrincipal = KerberosTestUtils.getHostServerPrincipal(); private static String hostLearnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal(); - private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myHost"); + private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myhost"); static { setupJaasConfigEntries(hostServerPrincipal, hostLearnerPrincipal, hostNamedLearnerPrincipal); } @@ -58,7 +58,9 @@ private static void setupJaasConfigEntries(String hostServerPrincipal, + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" - + " debug=false\n" + + " debug=true\n" + + " doNotPrompt=true\n" + + " refreshKrb5Config=true\n" + " principal=\"" + KerberosTestUtils.replaceHostPattern(hostServerPrincipal) + "\";\n" + "};\n" + "QuorumLearner {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" @@ -66,7 +68,10 @@ private static void setupJaasConfigEntries(String hostServerPrincipal, + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" - + " debug=false\n" + + " debug=true\n" + + " doNotPrompt=true\n" + + " refreshKrb5Config=true\n" + + " isInitiator=true\n" + " principal=\"" + KerberosTestUtils.replaceHostPattern(hostLearnerPrincipal) + "\";\n" + "};\n" + "QuorumLearnerMyHost {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" @@ -74,7 +79,10 @@ private static void setupJaasConfigEntries(String hostServerPrincipal, + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" - + " debug=false\n" + + " debug=true\n" + + " doNotPrompt=true\n" + + " refreshKrb5Config=true\n" + + " isInitiator=true\n" + " principal=\"" + hostNamedLearnerPrincipal + "\";\n" + "};\n"); setupJaasConfig(jaasEntries); } @@ -122,7 +130,7 @@ public void testValidCredentials() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); - String connectStr = startQuorum(3, authConfigs, 3); + String connectStr = startQuorum(3, authConfigs, 3, true); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); @@ -143,7 +151,7 @@ public void testConnectBadServer() throws Exception { authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); - String connectStr = startQuorum(3, authConfigs, 3); + String connectStr = startQuorum(3, authConfigs, 3, true); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); From 39d3a4f269333c922ed3db283be479f9deacaa0f Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 23 Mar 2017 13:24:44 +0530 Subject: [PATCH 398/444] ZOOKEEPER-2706: checkstyle broken on branch-3.4 This is essentially a backport of ZOOKEEPER-412. Author: Abraham Fine Reviewers: Michael Han , Rakesh Radhakrishnan Closes #181 from afine/ZOOKEEPER-2706 and squashes the following commits: d606225 [Abraham Fine] switch to checkstyle 6.1.1, the last version compatable with java 6 12966a6 [Abraham Fine] ZOOKEEPER-2706: checkstyle broken on branch-3.4 --- build.xml | 41 +++++++++++++----------------------- ivy.xml | 2 +- src/java/test/checkstyle.xml | 29 +++++++++++++++++++------ 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/build.xml b/build.xml index 7e84be9a6ed..e5842949b74 100644 --- a/build.xml +++ b/build.xml @@ -20,7 +20,8 @@ +xmlns:maven="antlib:org.apache.maven.artifact.ant" +xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> @@ -1587,32 +1588,20 @@ xmlns:maven="antlib:org.apache.maven.artifact.ant"> - - - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - diff --git a/ivy.xml b/ivy.xml index 4c113846f38..1a9af7612ab 100644 --- a/ivy.xml +++ b/ivy.xml @@ -58,7 +58,7 @@ - diff --git a/src/java/test/checkstyle.xml b/src/java/test/checkstyle.xml index 541573132b2..a5d51821aa8 100644 --- a/src/java/test/checkstyle.xml +++ b/src/java/test/checkstyle.xml @@ -4,6 +4,23 @@ "http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> + - + @@ -45,6 +62,8 @@ + + @@ -96,8 +115,9 @@ - - + + + @@ -110,7 +130,6 @@ - @@ -134,7 +153,6 @@ - @@ -143,7 +161,6 @@ - From b0aaa7de5b28375cd34ead8c8fbf14e3ebddd30f Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Thu, 23 Mar 2017 15:46:04 +0530 Subject: [PATCH 399/444] Preparing for release 3.4.10 --- docs/releasenotes.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index e4529bc24f1..1131220bb52 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -263,8 +263,14 @@

            Bug

          • [ZOOKEEPER-2696] - Eclipse ant task no longer determines correct classpath for tests after ZOOKEEPER-2689
          • +
          • [ZOOKEEPER-2706] - checkstyle broken on branch-3.4 +
          • [ZOOKEEPER-2710] - Regenerate documentation for branch-3.4 release
          • +
          • [ZOOKEEPER-2712] - MiniKdc test case intermittently failing due to principal not found in Kerberos database +
          • +
          • [ZOOKEEPER-2726] - Patch for ZOOKEEPER-2693 introduces potential race condition +
          • Improvement @@ -292,7 +298,7 @@

            New Feature
          • [ZOOKEEPER-1045] - Support Quorum Peer mutual authentication via SASL
          • - +

            Test

              @@ -306,6 +312,8 @@

              Test
            • [ZOOKEEPER-2665] - Port QA github pull request build to branch 3.4 and 3.5
            • +
            • [ZOOKEEPER-2716] - Flaky Test: org.apache.zookeeper.server.SessionTrackerTest.testAddSessionAfterSessionExpiry +
            Release Notes - ZooKeeper - Version 3.4.9 From 0f83f51644c17aee216031f3848866e182cabb67 Mon Sep 17 00:00:00 2001 From: Rakesh Radhakrishnan Date: Thu, 30 Mar 2017 10:13:51 +0530 Subject: [PATCH 400/444] Update the version to 3.4.11-SNAPSHOT --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index e5842949b74..d34ca808fbf 100644 --- a/build.xml +++ b/build.xml @@ -31,7 +31,7 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> - + From 8a0c6f4215a9c526a5a49282e35c84756f34e681 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Sat, 8 Apr 2017 15:13:21 -0700 Subject: [PATCH 401/444] ZOOKEEPER-2744: Typos in the comments of ZooKeeper class Author: Abraham Fine Reviewers: Michael Han Closes #219 from afine/ZOOKEEPER-2744_3.4 --- src/java/main/org/apache/zookeeper/ZooKeeper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index cabc4d53235..ef5cd7dcefa 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -76,11 +76,11 @@ * A client needs an object of a class implementing Watcher interface for * processing the events delivered to the client. * - * When a client drops current connection and re-connects to a server, all the + * When a client drops the current connection and re-connects to a server, all the * existing watches are considered as being triggered but the undelivered events * are lost. To emulate this, the client will generate a special event to tell - * the event handler a connection has been dropped. This special event has type - * EventNone and state sKeeperStateDisconnected. + * the event handler a connection has been dropped. This special event has + * EventType None and KeeperState Disconnected. * */ public class ZooKeeper { From a1b39d4a152f932077bb678a7d4816df4b6fe591 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Sat, 8 Apr 2017 15:16:43 -0700 Subject: [PATCH 402/444] ZOOKEEPER-2638: ZooKeeper should log which serverCnxnFactory is used during startup Author: Abraham Fine Reviewers: Michael Han Closes #218 from afine/ZOOKEEPER-2638_3.4 --- .../org/apache/zookeeper/server/ServerCnxnFactory.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java index cf417f8b297..2a67ec56ee4 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java @@ -46,7 +46,7 @@ public interface PacketProcessor { public void processPacket(ByteBuffer packet, ServerCnxn src); } - Logger LOG = LoggerFactory.getLogger(ServerCnxnFactory.class); + private static final Logger LOG = LoggerFactory.getLogger(ServerCnxnFactory.class); /** * The buffer will cause the connection to be close when we do a send. @@ -107,8 +107,9 @@ static public ServerCnxnFactory createFactory() throws IOException { serverCnxnFactoryName = NIOServerCnxnFactory.class.getName(); } try { - return (ServerCnxnFactory) Class.forName(serverCnxnFactoryName) - .newInstance(); + ServerCnxnFactory serverCnxnFactory = (ServerCnxnFactory) Class.forName(serverCnxnFactoryName).newInstance(); + LOG.info("Using {} as server connection factory", serverCnxnFactoryName); + return serverCnxnFactory; } catch (Exception e) { IOException ioe = new IOException("Couldn't instantiate " + serverCnxnFactoryName); From fa718b93f22ce17a7f041b68bbfb2a74ac6d3dbe Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Sat, 8 Apr 2017 15:31:41 -0700 Subject: [PATCH 403/444] ZOOKEEPER-2729: Cleanup findbug warnings in branch-3.4: Correctness Warnings Author: Abraham Fine Reviewers: Michael Han Closes #220 from afine/ZOOKEEPER-2729 --- .../zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java | 4 +--- .../zookeeper/server/quorum/auth/SaslQuorumAuthServer.java | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java index 2643808202e..9a38bb88d8b 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java @@ -173,9 +173,7 @@ private void send(DataOutputStream dout, byte[] response) BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); BinaryOutputArchive boa = BinaryOutputArchive .getArchive(bufferedOutput); - if (response != null && response.length < 0) { - throw new IOException("Response length < 0"); - } else if (response == null) { + if (response == null) { authPacket = QuorumAuth.createPacket( QuorumAuth.Status.IN_PROGRESS, response); } else { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java index 0a67b79a8ea..a64513a9a9c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java @@ -167,9 +167,7 @@ private void send(DataOutputStream dout, byte[] challenge, BinaryOutputArchive boa = BinaryOutputArchive .getArchive(bufferedOutput); QuorumAuthPacket authPacket; - if (challenge != null && challenge.length < 0) { - throw new IOException("Response length < 0"); - } else if (challenge == null && s != QuorumAuth.Status.SUCCESS) { + if (challenge == null && s != QuorumAuth.Status.SUCCESS) { authPacket = QuorumAuth.createPacket( QuorumAuth.Status.IN_PROGRESS, challenge); } else { From 13ca3f3b80e3db3f4a614eed37bfe9ed0453e306 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Sat, 8 Apr 2017 15:57:00 -0700 Subject: [PATCH 404/444] ZOOKEEPER-2749: Cleanup findbug warnings in branch-3.4: Experimental Warnings There were a bunch of findbugs warnings related to not closing FileWriters in findbugs. We should conform to best practices and close them. We should also cherrypick this to 3.5 Author: Abraham Fine Reviewers: Michael Han Closes #221 from afine/ZOOKEEPER-2749 --- .../org/apache/jute/compiler/CGenerator.java | 126 ++-- .../apache/jute/compiler/CppGenerator.java | 119 ++-- .../org/apache/jute/compiler/JRecord.java | 621 +++++++++--------- .../apache/zookeeper/version/util/VerGen.java | 10 +- 4 files changed, 456 insertions(+), 420 deletions(-) diff --git a/src/java/main/org/apache/jute/compiler/CGenerator.java b/src/java/main/org/apache/jute/compiler/CGenerator.java index 4bfdcadc9ae..689ee2120a4 100644 --- a/src/java/main/org/apache/jute/compiler/CGenerator.java +++ b/src/java/main/org/apache/jute/compiler/CGenerator.java @@ -61,70 +61,82 @@ void genCode() throws IOException { + outputDirectory); } } - FileWriter c = new FileWriter(new File(outputDirectory, mName+".c")); - FileWriter h = new FileWriter(new File(outputDirectory, mName+".h")); - h.write("/**\n"); - h.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); - h.write("* or more contributor license agreements. See the NOTICE file\n"); - h.write("* distributed with this work for additional information\n"); - h.write("* regarding copyright ownership. The ASF licenses this file\n"); - h.write("* to you under the Apache License, Version 2.0 (the\n"); - h.write("* \"License\"); you may not use this file except in compliance\n"); - h.write("* with the License. You may obtain a copy of the License at\n"); - h.write("*\n"); - h.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); - h.write("*\n"); - h.write("* Unless required by applicable law or agreed to in writing, software\n"); - h.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); - h.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); - h.write("* See the License for the specific language governing permissions and\n"); - h.write("* limitations under the License.\n"); - h.write("*/\n"); - h.write("\n"); + FileWriter c = null; + FileWriter h = null; + try { + c = new FileWriter(new File(outputDirectory, mName+".c")); + h = new FileWriter(new File(outputDirectory, mName+".h")); - c.write("/**\n"); - c.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); - c.write("* or more contributor license agreements. See the NOTICE file\n"); - c.write("* distributed with this work for additional information\n"); - c.write("* regarding copyright ownership. The ASF licenses this file\n"); - c.write("* to you under the Apache License, Version 2.0 (the\n"); - c.write("* \"License\"); you may not use this file except in compliance\n"); - c.write("* with the License. You may obtain a copy of the License at\n"); - c.write("*\n"); - c.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); - c.write("*\n"); - c.write("* Unless required by applicable law or agreed to in writing, software\n"); - c.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); - c.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); - c.write("* See the License for the specific language governing permissions and\n"); - c.write("* limitations under the License.\n"); - c.write("*/\n"); - c.write("\n"); + h.write("/**\n"); + h.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + h.write("* or more contributor license agreements. See the NOTICE file\n"); + h.write("* distributed with this work for additional information\n"); + h.write("* regarding copyright ownership. The ASF licenses this file\n"); + h.write("* to you under the Apache License, Version 2.0 (the\n"); + h.write("* \"License\"); you may not use this file except in compliance\n"); + h.write("* with the License. You may obtain a copy of the License at\n"); + h.write("*\n"); + h.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + h.write("*\n"); + h.write("* Unless required by applicable law or agreed to in writing, software\n"); + h.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + h.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + h.write("* See the License for the specific language governing permissions and\n"); + h.write("* limitations under the License.\n"); + h.write("*/\n"); + h.write("\n"); - h.write("#ifndef __"+mName.toUpperCase().replace('.','_')+"__\n"); - h.write("#define __"+mName.toUpperCase().replace('.','_')+"__\n"); + c.write("/**\n"); + c.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + c.write("* or more contributor license agreements. See the NOTICE file\n"); + c.write("* distributed with this work for additional information\n"); + c.write("* regarding copyright ownership. The ASF licenses this file\n"); + c.write("* to you under the Apache License, Version 2.0 (the\n"); + c.write("* \"License\"); you may not use this file except in compliance\n"); + c.write("* with the License. You may obtain a copy of the License at\n"); + c.write("*\n"); + c.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + c.write("*\n"); + c.write("* Unless required by applicable law or agreed to in writing, software\n"); + c.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + c.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + c.write("* See the License for the specific language governing permissions and\n"); + c.write("* limitations under the License.\n"); + c.write("*/\n"); + c.write("\n"); - h.write("#include \"recordio.h\"\n"); - for (Iterator i = mInclFiles.iterator(); i.hasNext();) { - JFile f = i.next(); - h.write("#include \""+f.getName()+".h\"\n"); - } - // required for compilation from C++ - h.write("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"); + h.write("#ifndef __"+mName.toUpperCase().replace('.','_')+"__\n"); + h.write("#define __"+mName.toUpperCase().replace('.','_')+"__\n"); - c.write("#include \n"); // need it for calloc() & free() - c.write("#include \""+mName+".h\"\n\n"); + h.write("#include \"recordio.h\"\n"); + for (Iterator i = mInclFiles.iterator(); i.hasNext();) { + JFile f = i.next(); + h.write("#include \""+f.getName()+".h\"\n"); + } + // required for compilation from C++ + h.write("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"); - for (Iterator i = mRecList.iterator(); i.hasNext();) { - JRecord jr = i.next(); - jr.genCCode(h, c); - } + c.write("#include \n"); // need it for calloc() & free() + c.write("#include \""+mName+".h\"\n\n"); - h.write("\n#ifdef __cplusplus\n}\n#endif\n\n"); - h.write("#endif //"+mName.toUpperCase().replace('.','_')+"__\n"); + for (Iterator i = mRecList.iterator(); i.hasNext();) { + JRecord jr = i.next(); + jr.genCCode(h, c); + } - h.close(); - c.close(); + h.write("\n#ifdef __cplusplus\n}\n#endif\n\n"); + h.write("#endif //"+mName.toUpperCase().replace('.','_')+"__\n"); + } finally { + try { + if (h != null) { + h.close(); + } + } finally { + if (c != null) { + c.close(); + } + } + } } } diff --git a/src/java/main/org/apache/jute/compiler/CppGenerator.java b/src/java/main/org/apache/jute/compiler/CppGenerator.java index 16d44ce0e6c..98892a303c6 100644 --- a/src/java/main/org/apache/jute/compiler/CppGenerator.java +++ b/src/java/main/org/apache/jute/compiler/CppGenerator.java @@ -61,65 +61,76 @@ void genCode() throws IOException { + outputDirectory); } } - FileWriter cc = new FileWriter(new File(outputDirectory, mName+".cc")); - FileWriter hh = new FileWriter(new File(outputDirectory, mName+".hh")); + FileWriter cc = null; + FileWriter hh = null; - hh.write("/**\n"); - hh.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); - hh.write("* or more contributor license agreements. See the NOTICE file\n"); - hh.write("* distributed with this work for additional information\n"); - hh.write("* regarding copyright ownership. The ASF licenses this file\n"); - hh.write("* to you under the Apache License, Version 2.0 (the\n"); - hh.write("* \"License\"); you may not use this file except in compliance\n"); - hh.write("* with the License. You may obtain a copy of the License at\n"); - hh.write("*\n"); - hh.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); - hh.write("*\n"); - hh.write("* Unless required by applicable law or agreed to in writing, software\n"); - hh.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); - hh.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); - hh.write("* See the License for the specific language governing permissions and\n"); - hh.write("* limitations under the License.\n"); - hh.write("*/\n"); - hh.write("\n"); + try { + cc = new FileWriter(new File(outputDirectory, mName+".cc")); + hh = new FileWriter(new File(outputDirectory, mName+".hh")); + hh.write("/**\n"); + hh.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + hh.write("* or more contributor license agreements. See the NOTICE file\n"); + hh.write("* distributed with this work for additional information\n"); + hh.write("* regarding copyright ownership. The ASF licenses this file\n"); + hh.write("* to you under the Apache License, Version 2.0 (the\n"); + hh.write("* \"License\"); you may not use this file except in compliance\n"); + hh.write("* with the License. You may obtain a copy of the License at\n"); + hh.write("*\n"); + hh.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + hh.write("*\n"); + hh.write("* Unless required by applicable law or agreed to in writing, software\n"); + hh.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + hh.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + hh.write("* See the License for the specific language governing permissions and\n"); + hh.write("* limitations under the License.\n"); + hh.write("*/\n"); + hh.write("\n"); - cc.write("/**\n"); - cc.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); - cc.write("* or more contributor license agreements. See the NOTICE file\n"); - cc.write("* distributed with this work for additional information\n"); - cc.write("* regarding copyright ownership. The ASF licenses this file\n"); - cc.write("* to you under the Apache License, Version 2.0 (the\n"); - cc.write("* \"License\"); you may not use this file except in compliance\n"); - cc.write("* with the License. You may obtain a copy of the License at\n"); - cc.write("*\n"); - cc.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); - cc.write("*\n"); - cc.write("* Unless required by applicable law or agreed to in writing, software\n"); - cc.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); - cc.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); - cc.write("* See the License for the specific language governing permissions and\n"); - cc.write("* limitations under the License.\n"); - cc.write("*/\n"); - cc.write("\n"); + cc.write("/**\n"); + cc.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + cc.write("* or more contributor license agreements. See the NOTICE file\n"); + cc.write("* distributed with this work for additional information\n"); + cc.write("* regarding copyright ownership. The ASF licenses this file\n"); + cc.write("* to you under the Apache License, Version 2.0 (the\n"); + cc.write("* \"License\"); you may not use this file except in compliance\n"); + cc.write("* with the License. You may obtain a copy of the License at\n"); + cc.write("*\n"); + cc.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + cc.write("*\n"); + cc.write("* Unless required by applicable law or agreed to in writing, software\n"); + cc.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + cc.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + cc.write("* See the License for the specific language governing permissions and\n"); + cc.write("* limitations under the License.\n"); + cc.write("*/\n"); + cc.write("\n"); - hh.write("#ifndef __"+mName.toUpperCase().replace('.','_')+"__\n"); - hh.write("#define __"+mName.toUpperCase().replace('.','_')+"__\n"); + hh.write("#ifndef __"+mName.toUpperCase().replace('.','_')+"__\n"); + hh.write("#define __"+mName.toUpperCase().replace('.','_')+"__\n"); - hh.write("#include \"recordio.hh\"\n"); - for (Iterator i = mInclFiles.iterator(); i.hasNext();) { - JFile f = i.next(); - hh.write("#include \""+f.getName()+".hh\"\n"); - } - cc.write("#include \""+mName+".hh\"\n"); - - for (Iterator i = mRecList.iterator(); i.hasNext();) { - JRecord jr = i.next(); - jr.genCppCode(hh, cc); - } + hh.write("#include \"recordio.hh\"\n"); + for (Iterator i = mInclFiles.iterator(); i.hasNext();) { + JFile f = i.next(); + hh.write("#include \""+f.getName()+".hh\"\n"); + } + cc.write("#include \""+mName+".hh\"\n"); - hh.write("#endif //"+mName.toUpperCase().replace('.','_')+"__\n"); + for (Iterator i = mRecList.iterator(); i.hasNext();) { + JRecord jr = i.next(); + jr.genCppCode(hh, cc); + } - hh.close(); - cc.close(); + hh.write("#endif //"+mName.toUpperCase().replace('.','_')+"__\n"); + } finally { + try { + if (hh != null) { + hh.close(); + } + } finally { + if (cc != null) { + cc.close(); + } + } + } } } diff --git a/src/java/main/org/apache/jute/compiler/JRecord.java b/src/java/main/org/apache/jute/compiler/JRecord.java index 01ba2989afb..13965f7e815 100644 --- a/src/java/main/org/apache/jute/compiler/JRecord.java +++ b/src/java/main/org/apache/jute/compiler/JRecord.java @@ -404,167 +404,173 @@ public void genJavaCode(File outputDirectory) throws IOException { throw new IOException(pkgpath + " is not a directory."); } File jfile = new File(pkgdir, getName()+".java"); - FileWriter jj = new FileWriter(jfile); - jj.write("// File generated by hadoop record compiler. Do not edit.\n"); - jj.write("/**\n"); - jj.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); - jj.write("* or more contributor license agreements. See the NOTICE file\n"); - jj.write("* distributed with this work for additional information\n"); - jj.write("* regarding copyright ownership. The ASF licenses this file\n"); - jj.write("* to you under the Apache License, Version 2.0 (the\n"); - jj.write("* \"License\"); you may not use this file except in compliance\n"); - jj.write("* with the License. You may obtain a copy of the License at\n"); - jj.write("*\n"); - jj.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); - jj.write("*\n"); - jj.write("* Unless required by applicable law or agreed to in writing, software\n"); - jj.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); - jj.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); - jj.write("* See the License for the specific language governing permissions and\n"); - jj.write("* limitations under the License.\n"); - jj.write("*/\n"); - jj.write("\n"); - jj.write("package "+getJavaPackage()+";\n\n"); - jj.write("import org.apache.jute.*;\n"); - jj.write("public class "+getName()+" implements Record {\n"); - for (Iterator i = mFields.iterator(); i.hasNext();) { - JField jf = i.next(); - jj.write(jf.genJavaDecl()); - } - jj.write(" public "+getName()+"() {\n"); - jj.write(" }\n"); + FileWriter jj = null; + try{ + jj = new FileWriter(jfile); + jj.write("// File generated by hadoop record compiler. Do not edit.\n"); + jj.write("/**\n"); + jj.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + jj.write("* or more contributor license agreements. See the NOTICE file\n"); + jj.write("* distributed with this work for additional information\n"); + jj.write("* regarding copyright ownership. The ASF licenses this file\n"); + jj.write("* to you under the Apache License, Version 2.0 (the\n"); + jj.write("* \"License\"); you may not use this file except in compliance\n"); + jj.write("* with the License. You may obtain a copy of the License at\n"); + jj.write("*\n"); + jj.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + jj.write("*\n"); + jj.write("* Unless required by applicable law or agreed to in writing, software\n"); + jj.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + jj.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + jj.write("* See the License for the specific language governing permissions and\n"); + jj.write("* limitations under the License.\n"); + jj.write("*/\n"); + jj.write("\n"); + jj.write("package "+getJavaPackage()+";\n\n"); + jj.write("import org.apache.jute.*;\n"); + jj.write("public class "+getName()+" implements Record {\n"); + for (Iterator i = mFields.iterator(); i.hasNext();) { + JField jf = i.next(); + jj.write(jf.genJavaDecl()); + } + jj.write(" public "+getName()+"() {\n"); + jj.write(" }\n"); - jj.write(" public "+getName()+"(\n"); - int fIdx = 0; - int fLen = mFields.size(); - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaConstructorParam(jf.getName())); - jj.write((fLen-1 == fIdx)?"":",\n"); - } - jj.write(") {\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaConstructorSet(jf.getName())); - } - jj.write(" }\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaGetSet(fIdx)); - } - jj.write(" public void serialize(OutputArchive a_, String tag) throws java.io.IOException {\n"); - jj.write(" a_.startRecord(this,tag);\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaWriteMethodName()); - } - jj.write(" a_.endRecord(this,tag);\n"); - jj.write(" }\n"); + jj.write(" public "+getName()+"(\n"); + int fIdx = 0; + int fLen = mFields.size(); + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaConstructorParam(jf.getName())); + jj.write((fLen-1 == fIdx)?"":",\n"); + } + jj.write(") {\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaConstructorSet(jf.getName())); + } + jj.write(" }\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaGetSet(fIdx)); + } + jj.write(" public void serialize(OutputArchive a_, String tag) throws java.io.IOException {\n"); + jj.write(" a_.startRecord(this,tag);\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaWriteMethodName()); + } + jj.write(" a_.endRecord(this,tag);\n"); + jj.write(" }\n"); - jj.write(" public void deserialize(InputArchive a_, String tag) throws java.io.IOException {\n"); - jj.write(" a_.startRecord(tag);\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaReadMethodName()); - } - jj.write(" a_.endRecord(tag);\n"); - jj.write("}\n"); - - jj.write(" public String toString() {\n"); - jj.write(" try {\n"); - jj.write(" java.io.ByteArrayOutputStream s =\n"); - jj.write(" new java.io.ByteArrayOutputStream();\n"); - jj.write(" CsvOutputArchive a_ = \n"); - jj.write(" new CsvOutputArchive(s);\n"); - jj.write(" a_.startRecord(this,\"\");\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaWriteMethodName()); - } - jj.write(" a_.endRecord(this,\"\");\n"); - jj.write(" return new String(s.toByteArray(), \"UTF-8\");\n"); - jj.write(" } catch (Throwable ex) {\n"); - jj.write(" ex.printStackTrace();\n"); - jj.write(" }\n"); - jj.write(" return \"ERROR\";\n"); - jj.write(" }\n"); - - jj.write(" public void write(java.io.DataOutput out) throws java.io.IOException {\n"); - jj.write(" BinaryOutputArchive archive = new BinaryOutputArchive(out);\n"); - jj.write(" serialize(archive, \"\");\n"); - jj.write(" }\n"); - - jj.write(" public void readFields(java.io.DataInput in) throws java.io.IOException {\n"); - jj.write(" BinaryInputArchive archive = new BinaryInputArchive(in);\n"); - jj.write(" deserialize(archive, \"\");\n"); - jj.write(" }\n"); - - jj.write(" public int compareTo (Object peer_) throws ClassCastException {\n"); - boolean unimplemented = false; - for (JField f : mFields) { - if ((f.getType() instanceof JMap) - || (f.getType() instanceof JVector)) - { - unimplemented = true; + jj.write(" public void deserialize(InputArchive a_, String tag) throws java.io.IOException {\n"); + jj.write(" a_.startRecord(tag);\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaReadMethodName()); } - } - if (unimplemented) { - jj.write(" throw new UnsupportedOperationException(\"comparing " - + getName() + " is unimplemented\");\n"); - } else { + jj.write(" a_.endRecord(tag);\n"); + jj.write("}\n"); + + jj.write(" public String toString() {\n"); + jj.write(" try {\n"); + jj.write(" java.io.ByteArrayOutputStream s =\n"); + jj.write(" new java.io.ByteArrayOutputStream();\n"); + jj.write(" CsvOutputArchive a_ = \n"); + jj.write(" new CsvOutputArchive(s);\n"); + jj.write(" a_.startRecord(this,\"\");\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaWriteMethodName()); + } + jj.write(" a_.endRecord(this,\"\");\n"); + jj.write(" return new String(s.toByteArray(), \"UTF-8\");\n"); + jj.write(" } catch (Throwable ex) {\n"); + jj.write(" ex.printStackTrace();\n"); + jj.write(" }\n"); + jj.write(" return \"ERROR\";\n"); + jj.write(" }\n"); + + jj.write(" public void write(java.io.DataOutput out) throws java.io.IOException {\n"); + jj.write(" BinaryOutputArchive archive = new BinaryOutputArchive(out);\n"); + jj.write(" serialize(archive, \"\");\n"); + jj.write(" }\n"); + + jj.write(" public void readFields(java.io.DataInput in) throws java.io.IOException {\n"); + jj.write(" BinaryInputArchive archive = new BinaryInputArchive(in);\n"); + jj.write(" deserialize(archive, \"\");\n"); + jj.write(" }\n"); + + jj.write(" public int compareTo (Object peer_) throws ClassCastException {\n"); + boolean unimplemented = false; + for (JField f : mFields) { + if ((f.getType() instanceof JMap) + || (f.getType() instanceof JVector)) + { + unimplemented = true; + } + } + if (unimplemented) { + jj.write(" throw new UnsupportedOperationException(\"comparing " + + getName() + " is unimplemented\");\n"); + } else { + jj.write(" if (!(peer_ instanceof "+getName()+")) {\n"); + jj.write(" throw new ClassCastException(\"Comparing different types of records.\");\n"); + jj.write(" }\n"); + jj.write(" "+getName()+" peer = ("+getName()+") peer_;\n"); + jj.write(" int ret = 0;\n"); + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaCompareTo()); + jj.write(" if (ret != 0) return ret;\n"); + } + jj.write(" return ret;\n"); + } + jj.write(" }\n"); + + jj.write(" public boolean equals(Object peer_) {\n"); jj.write(" if (!(peer_ instanceof "+getName()+")) {\n"); - jj.write(" throw new ClassCastException(\"Comparing different types of records.\");\n"); + jj.write(" return false;\n"); + jj.write(" }\n"); + jj.write(" if (peer_ == this) {\n"); + jj.write(" return true;\n"); jj.write(" }\n"); jj.write(" "+getName()+" peer = ("+getName()+") peer_;\n"); - jj.write(" int ret = 0;\n"); + jj.write(" boolean ret = false;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); - jj.write(jf.genJavaCompareTo()); - jj.write(" if (ret != 0) return ret;\n"); + jj.write(jf.genJavaEquals()); + jj.write(" if (!ret) return ret;\n"); } jj.write(" return ret;\n"); - } - jj.write(" }\n"); - - jj.write(" public boolean equals(Object peer_) {\n"); - jj.write(" if (!(peer_ instanceof "+getName()+")) {\n"); - jj.write(" return false;\n"); - jj.write(" }\n"); - jj.write(" if (peer_ == this) {\n"); - jj.write(" return true;\n"); - jj.write(" }\n"); - jj.write(" "+getName()+" peer = ("+getName()+") peer_;\n"); - jj.write(" boolean ret = false;\n"); - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaEquals()); - jj.write(" if (!ret) return ret;\n"); - } - jj.write(" return ret;\n"); - jj.write(" }\n"); + jj.write(" }\n"); - jj.write(" public int hashCode() {\n"); - jj.write(" int result = 17;\n"); - jj.write(" int ret;\n"); - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - jj.write(jf.genJavaHashCode()); - jj.write(" result = 37*result + ret;\n"); + jj.write(" public int hashCode() {\n"); + jj.write(" int result = 17;\n"); + jj.write(" int ret;\n"); + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + jj.write(jf.genJavaHashCode()); + jj.write(" result = 37*result + ret;\n"); + } + jj.write(" return result;\n"); + jj.write(" }\n"); + jj.write(" public static String signature() {\n"); + jj.write(" return \""+getSignature()+"\";\n"); + jj.write(" }\n"); + + jj.write("}\n"); + } finally { + if (jj != null) { + jj.close(); + } } - jj.write(" return result;\n"); - jj.write(" }\n"); - jj.write(" public static String signature() {\n"); - jj.write(" return \""+getSignature()+"\";\n"); - jj.write(" }\n"); - jj.write("}\n"); - - jj.close(); } public void genCsharpCode(File outputDirectory) throws IOException { @@ -577,173 +583,180 @@ public void genCsharpCode(File outputDirectory) throws IOException { throw new IOException(outputDirectory + " is not a directory."); } File csharpFile = new File(outputDirectory, getName()+".cs"); - FileWriter cs = new FileWriter(csharpFile); - cs.write("// File generated by hadoop record compiler. Do not edit.\n"); - cs.write("/**\n"); - cs.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); - cs.write("* or more contributor license agreements. See the NOTICE file\n"); - cs.write("* distributed with this work for additional information\n"); - cs.write("* regarding copyright ownership. The ASF licenses this file\n"); - cs.write("* to you under the Apache License, Version 2.0 (the\n"); - cs.write("* \"License\"); you may not use this file except in compliance\n"); - cs.write("* with the License. You may obtain a copy of the License at\n"); - cs.write("*\n"); - cs.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); - cs.write("*\n"); - cs.write("* Unless required by applicable law or agreed to in writing, software\n"); - cs.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); - cs.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); - cs.write("* See the License for the specific language governing permissions and\n"); - cs.write("* limitations under the License.\n"); - cs.write("*/\n"); - cs.write("\n"); - cs.write("using System;\n"); - cs.write("using Org.Apache.Jute;\n"); - cs.write("\n"); - cs.write("namespace "+getCsharpNameSpace()+"\n"); - cs.write("{\n"); - - String className = getCsharpName(); - cs.write("public class "+className+" : IRecord, IComparable \n"); - cs.write("{\n"); - cs.write(" public "+ className +"() {\n"); - cs.write(" }\n"); - - cs.write(" public "+className+"(\n"); - int fIdx = 0; - int fLen = mFields.size(); - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpConstructorParam(jf.getCsharpName())); - cs.write((fLen-1 == fIdx)?"":",\n"); - } - cs.write(") {\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpConstructorSet(jf.getCsharpName())); - } - cs.write(" }\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpGetSet(fIdx)); + FileWriter cs = null; + + try { + cs = new FileWriter(csharpFile); + + cs.write("// File generated by hadoop record compiler. Do not edit.\n"); + cs.write("/**\n"); + cs.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + cs.write("* or more contributor license agreements. See the NOTICE file\n"); + cs.write("* distributed with this work for additional information\n"); + cs.write("* regarding copyright ownership. The ASF licenses this file\n"); + cs.write("* to you under the Apache License, Version 2.0 (the\n"); + cs.write("* \"License\"); you may not use this file except in compliance\n"); + cs.write("* with the License. You may obtain a copy of the License at\n"); + cs.write("*\n"); + cs.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + cs.write("*\n"); + cs.write("* Unless required by applicable law or agreed to in writing, software\n"); + cs.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + cs.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + cs.write("* See the License for the specific language governing permissions and\n"); + cs.write("* limitations under the License.\n"); + cs.write("*/\n"); cs.write("\n"); - } - cs.write(" public void Serialize(IOutputArchive a_, String tag) {\n"); - cs.write(" a_.StartRecord(this,tag);\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpWriteMethodName()); - } - cs.write(" a_.EndRecord(this,tag);\n"); - cs.write(" }\n"); + cs.write("using System;\n"); + cs.write("using Org.Apache.Jute;\n"); + cs.write("\n"); + cs.write("namespace "+getCsharpNameSpace()+"\n"); + cs.write("{\n"); + + String className = getCsharpName(); + cs.write("public class "+className+" : IRecord, IComparable \n"); + cs.write("{\n"); + cs.write(" public "+ className +"() {\n"); + cs.write(" }\n"); + + cs.write(" public "+className+"(\n"); + int fIdx = 0; + int fLen = mFields.size(); + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpConstructorParam(jf.getCsharpName())); + cs.write((fLen-1 == fIdx)?"":",\n"); + } + cs.write(") {\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpConstructorSet(jf.getCsharpName())); + } + cs.write(" }\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpGetSet(fIdx)); + cs.write("\n"); + } + cs.write(" public void Serialize(IOutputArchive a_, String tag) {\n"); + cs.write(" a_.StartRecord(this,tag);\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpWriteMethodName()); + } + cs.write(" a_.EndRecord(this,tag);\n"); + cs.write(" }\n"); - cs.write(" public void Deserialize(IInputArchive a_, String tag) {\n"); - cs.write(" a_.StartRecord(tag);\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpReadMethodName()); - } - cs.write(" a_.EndRecord(tag);\n"); - cs.write("}\n"); - - cs.write(" public override String ToString() {\n"); - cs.write(" try {\n"); - cs.write(" System.IO.MemoryStream ms = new System.IO.MemoryStream();\n"); - cs.write(" MiscUtil.IO.EndianBinaryWriter writer =\n"); - cs.write(" new MiscUtil.IO.EndianBinaryWriter(MiscUtil.Conversion.EndianBitConverter.Big, ms, System.Text.Encoding.UTF8);\n"); - cs.write(" BinaryOutputArchive a_ = \n"); - cs.write(" new BinaryOutputArchive(writer);\n"); - cs.write(" a_.StartRecord(this,\"\");\n"); - fIdx = 0; - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpWriteMethodName()); - } - cs.write(" a_.EndRecord(this,\"\");\n"); - cs.write(" ms.Position = 0;\n"); - cs.write(" return System.Text.Encoding.UTF8.GetString(ms.ToArray());\n"); - cs.write(" } catch (Exception ex) {\n"); - cs.write(" Console.WriteLine(ex.StackTrace);\n"); - cs.write(" }\n"); - cs.write(" return \"ERROR\";\n"); - cs.write(" }\n"); - - cs.write(" public void Write(MiscUtil.IO.EndianBinaryWriter writer) {\n"); - cs.write(" BinaryOutputArchive archive = new BinaryOutputArchive(writer);\n"); - cs.write(" Serialize(archive, \"\");\n"); - cs.write(" }\n"); - - cs.write(" public void ReadFields(MiscUtil.IO.EndianBinaryReader reader) {\n"); - cs.write(" BinaryInputArchive archive = new BinaryInputArchive(reader);\n"); - cs.write(" Deserialize(archive, \"\");\n"); - cs.write(" }\n"); - - cs.write(" public int CompareTo (object peer_) {\n"); - boolean unimplemented = false; - for (JField f : mFields) { - if ((f.getType() instanceof JMap) - || (f.getType() instanceof JVector)) - { - unimplemented = true; + cs.write(" public void Deserialize(IInputArchive a_, String tag) {\n"); + cs.write(" a_.StartRecord(tag);\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpReadMethodName()); } - } - if (unimplemented) { - cs.write(" throw new InvalidOperationException(\"comparing " - + getCsharpName() + " is unimplemented\");\n"); - } else { + cs.write(" a_.EndRecord(tag);\n"); + cs.write("}\n"); + + cs.write(" public override String ToString() {\n"); + cs.write(" try {\n"); + cs.write(" System.IO.MemoryStream ms = new System.IO.MemoryStream();\n"); + cs.write(" MiscUtil.IO.EndianBinaryWriter writer =\n"); + cs.write(" new MiscUtil.IO.EndianBinaryWriter(MiscUtil.Conversion.EndianBitConverter.Big, ms, System.Text.Encoding.UTF8);\n"); + cs.write(" BinaryOutputArchive a_ = \n"); + cs.write(" new BinaryOutputArchive(writer);\n"); + cs.write(" a_.StartRecord(this,\"\");\n"); + fIdx = 0; + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpWriteMethodName()); + } + cs.write(" a_.EndRecord(this,\"\");\n"); + cs.write(" ms.Position = 0;\n"); + cs.write(" return System.Text.Encoding.UTF8.GetString(ms.ToArray());\n"); + cs.write(" } catch (Exception ex) {\n"); + cs.write(" Console.WriteLine(ex.StackTrace);\n"); + cs.write(" }\n"); + cs.write(" return \"ERROR\";\n"); + cs.write(" }\n"); + + cs.write(" public void Write(MiscUtil.IO.EndianBinaryWriter writer) {\n"); + cs.write(" BinaryOutputArchive archive = new BinaryOutputArchive(writer);\n"); + cs.write(" Serialize(archive, \"\");\n"); + cs.write(" }\n"); + + cs.write(" public void ReadFields(MiscUtil.IO.EndianBinaryReader reader) {\n"); + cs.write(" BinaryInputArchive archive = new BinaryInputArchive(reader);\n"); + cs.write(" Deserialize(archive, \"\");\n"); + cs.write(" }\n"); + + cs.write(" public int CompareTo (object peer_) {\n"); + boolean unimplemented = false; + for (JField f : mFields) { + if ((f.getType() instanceof JMap) + || (f.getType() instanceof JVector)) + { + unimplemented = true; + } + } + if (unimplemented) { + cs.write(" throw new InvalidOperationException(\"comparing " + + getCsharpName() + " is unimplemented\");\n"); + } else { + cs.write(" if (!(peer_ is "+getCsharpName()+")) {\n"); + cs.write(" throw new InvalidOperationException(\"Comparing different types of records.\");\n"); + cs.write(" }\n"); + cs.write(" "+getCsharpName()+" peer = ("+getCsharpName()+") peer_;\n"); + cs.write(" int ret = 0;\n"); + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpCompareTo()); + cs.write(" if (ret != 0) return ret;\n"); + } + cs.write(" return ret;\n"); + } + cs.write(" }\n"); + + cs.write(" public override bool Equals(object peer_) {\n"); cs.write(" if (!(peer_ is "+getCsharpName()+")) {\n"); - cs.write(" throw new InvalidOperationException(\"Comparing different types of records.\");\n"); + cs.write(" return false;\n"); + cs.write(" }\n"); + cs.write(" if (peer_ == this) {\n"); + cs.write(" return true;\n"); cs.write(" }\n"); - cs.write(" "+getCsharpName()+" peer = ("+getCsharpName()+") peer_;\n"); - cs.write(" int ret = 0;\n"); + cs.write(" bool ret = false;\n"); + cs.write(" " + getCsharpName() + " peer = (" + getCsharpName() + ")peer_;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); - cs.write(jf.genCsharpCompareTo()); - cs.write(" if (ret != 0) return ret;\n"); + cs.write(jf.genCsharpEquals()); + cs.write(" if (!ret) return ret;\n"); } cs.write(" return ret;\n"); - } - cs.write(" }\n"); - - cs.write(" public override bool Equals(object peer_) {\n"); - cs.write(" if (!(peer_ is "+getCsharpName()+")) {\n"); - cs.write(" return false;\n"); - cs.write(" }\n"); - cs.write(" if (peer_ == this) {\n"); - cs.write(" return true;\n"); - cs.write(" }\n"); - cs.write(" bool ret = false;\n"); - cs.write(" " + getCsharpName() + " peer = (" + getCsharpName() + ")peer_;\n"); - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpEquals()); - cs.write(" if (!ret) return ret;\n"); - } - cs.write(" return ret;\n"); - cs.write(" }\n"); + cs.write(" }\n"); - cs.write(" public override int GetHashCode() {\n"); - cs.write(" int result = 17;\n"); - cs.write(" int ret;\n"); - for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { - JField jf = i.next(); - cs.write(jf.genCsharpHashCode()); - cs.write(" result = 37*result + ret;\n"); + cs.write(" public override int GetHashCode() {\n"); + cs.write(" int result = 17;\n"); + cs.write(" int ret;\n"); + for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { + JField jf = i.next(); + cs.write(jf.genCsharpHashCode()); + cs.write(" result = 37*result + ret;\n"); + } + cs.write(" return result;\n"); + cs.write(" }\n"); + cs.write(" public static string Signature() {\n"); + cs.write(" return \""+getSignature()+"\";\n"); + cs.write(" }\n"); + + cs.write("}\n"); + cs.write("}\n"); + } finally { + if (cs != null) { + cs.close(); + } } - cs.write(" return result;\n"); - cs.write(" }\n"); - cs.write(" public static string Signature() {\n"); - cs.write(" return \""+getSignature()+"\";\n"); - cs.write(" }\n"); - - cs.write("}\n"); - cs.write("}\n"); - - cs.close(); } public static String getCsharpFQName(String name) { diff --git a/src/java/main/org/apache/zookeeper/version/util/VerGen.java b/src/java/main/org/apache/zookeeper/version/util/VerGen.java index 93a07106856..7285a2b564b 100644 --- a/src/java/main/org/apache/zookeeper/version/util/VerGen.java +++ b/src/java/main/org/apache/zookeeper/version/util/VerGen.java @@ -34,7 +34,7 @@ static void printUsage() { System.exit(1); } - public static void generateFile(File outputDir, Version version, String rev, String buildDate) + public static void generateFile(File outputDir, Version version, String rev, String buildDate) throws IOException { String path = PACKAGE_NAME.replaceAll("\\.", "/"); File pkgdir = new File(outputDir, path); @@ -91,10 +91,6 @@ public static void generateFile(File outputDir, Version version, String rev, Str w.write(" String BUILD_DATE=\"" + buildDate + "\";\n"); w.write("}\n"); - } catch (IOException e) { - System.out.println("Unable to generate version.Info file: " - + e.getMessage()); - System.exit(1); } finally { if (w != null) { try { @@ -172,6 +168,10 @@ public static void main(String[] args) { System.err.println( "All version-related parameters must be valid integers!"); throw e; + } catch (IOException e) { + System.out.println("Unable to generate version.Info file: " + + e.getMessage()); + System.exit(1); } } From c14cdd3673ebdd8014118ec961068ee51f2327b1 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Mon, 10 Apr 2017 10:19:34 -0700 Subject: [PATCH 405/444] ZOOKEEPER-2743: Netty connection leaks JMX connection bean. See https://issues.apache.org/jira/browse/ZOOKEEPER-2743 for details on the symptom and diagnostic. There are many ways of fixing this. For example we can enforce certain ordering of the close operations to eliminate the race condition however that would incur non trivial performance penalty as a result of synchronization. I think the solution in this patch is simple and effective as it observes that the bean unregister call is idempotent and the call itself is trivial so we just always unregister connection upon close the connection, to ensure the bean is unregistered regardless of the races. I've also stress tested this solution on internal Jenkins which indicates no failures of https://issues.apache.org/jira/browse/ZOOKEEPER-2707 for the past two days. A side note is NIO connection in theory would suffer the same problem however I am unable to reproduce the same racing with existing unit test. So I just leave NIO as it is, for now. Author: Michael Han Reviewers: Rakesh Radhakrishnan Closes #211 from hanm/ZOOKEEPER-2743 (cherry picked from commit b3ef40bffe49c1e42aee23a600a775194f36bca0) Signed-off-by: Michael Han --- .../main/org/apache/zookeeper/server/NettyServerCnxn.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index 203f0e60b86..99058d1fc39 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -89,6 +89,12 @@ public void close() { LOG.debug("close called for sessionid:0x" + Long.toHexString(sessionId)); } + + // ZOOKEEPER-2743: + // Always unregister connection upon close to prevent + // connection bean leak under certain race conditions. + factory.unregisterConnection(this); + synchronized(factory.cnxns){ // if this is not in cnxns then it's already closed if (!factory.cnxns.remove(this)) { @@ -113,7 +119,6 @@ public void close() { if (channel.isOpen()) { channel.close(); } - factory.unregisterConnection(this); } @Override From 2cc142572a7ef85dcd4b07d212267f442e6be9f1 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Mon, 24 Apr 2017 09:54:07 -0700 Subject: [PATCH 406/444] ZOOKEEPER-2722: fix flaky testSessionEstablishment test. Make sure client is connected to a quorum before issuing a write operation to avoid possible race condition between connected to a RO server and forming a new quorum. Cherry picked from commit 212201131f8eb01dd2209deb37656ccc4096de77 with a tweak to make it work with branch-3.4. Author: Michael Han Reviewers: Rakesh Radhakrishnan , Camille Fournier Closes #235 from hanm/ZOOKEEPER-2722 --- .../org/apache/zookeeper/test/ClientBase.java | 34 ++++++++++++++++--- .../zookeeper/test/ReadOnlyModeTest.java | 19 +++++++++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 6037ff2f3ca..08cc13cb33a 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -97,7 +97,10 @@ public void process(WatchedEvent event) { /* nada */ } public static class CountdownWatcher implements Watcher { // XXX this doesn't need to be volatile! (Should probably be final) volatile CountDownLatch clientConnected; + // Set to true when connected to a read-only server, or a read-write (quorum) server. volatile boolean connected; + // Set to true when connected to a quorum server. + volatile boolean syncConnected; public CountdownWatcher() { reset(); @@ -105,16 +108,24 @@ public CountdownWatcher() { synchronized public void reset() { clientConnected = new CountDownLatch(1); connected = false; + syncConnected = false; } synchronized public void process(WatchedEvent event) { - if (event.getState() == KeeperState.SyncConnected || - event.getState() == KeeperState.ConnectedReadOnly) { + KeeperState state = event.getState(); + if (state == KeeperState.SyncConnected) { connected = true; - notifyAll(); - clientConnected.countDown(); + syncConnected = true; + } else if (state == KeeperState.ConnectedReadOnly) { + connected = true; + syncConnected = false; } else { connected = false; - notifyAll(); + syncConnected = false; + } + + notifyAll(); + if (connected) { + clientConnected.countDown(); } } synchronized public boolean isConnected() { @@ -134,6 +145,19 @@ synchronized public void waitForConnected(long timeout) } } + synchronized public void waitForSyncConnected(long timeout) + throws InterruptedException, TimeoutException + { + long expire = System.currentTimeMillis() + timeout; + long left = timeout; + while(!syncConnected && left > 0) { + wait(left); + left = expire - System.currentTimeMillis(); + } + if (!syncConnected) { + throw new TimeoutException("Failed to connect to read-write ZooKeeper server."); + } + } synchronized public void waitForDisconnected(long timeout) throws InterruptedException, TimeoutException { diff --git a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java index 3b7a149ab53..1dbf6478c17 100644 --- a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java +++ b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java @@ -46,8 +46,11 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; public class ReadOnlyModeTest extends ZKTestCase { + private static final org.slf4j.Logger LOG = LoggerFactory + .getLogger(ReadOnlyModeTest.class); private static int CONNECTION_TIMEOUT = QuorumBase.CONNECTION_TIMEOUT; private QuorumUtil qu = new QuorumUtil(1); @@ -207,17 +210,29 @@ public void testSessionEstablishment() throws Exception { Assert.assertSame("should be in r/o mode", States.CONNECTEDREADONLY, zk .getState()); long fakeId = zk.getSessionId(); + LOG.info("Connected as r/o mode with state {} and session id {}", + zk.getState(), fakeId); watcher.reset(); qu.start(2); Assert.assertTrue("waiting for server up", ClientBase.waitForServerUp( "127.0.0.1:" + qu.getPeer(2).clientPort, CONNECTION_TIMEOUT)); - watcher.waitForConnected(CONNECTION_TIMEOUT); + LOG.info("Server 127.0.0.1:{} is up", qu.getPeer(2).clientPort); + // ZOOKEEPER-2722: wait until we can connect to a read-write server after the quorum + // is formed. Otherwise, it is possible that client first connects to a read-only server, + // then drops the connection because of shutting down of the read-only server caused + // by leader election / quorum forming between the read-only server and the newly started + // server. If we happen to execute the zk.create after the read-only server is shutdown and + // before the quorum is formed, we will get a ConnectLossException. + watcher.waitForSyncConnected(CONNECTION_TIMEOUT); + Assert.assertEquals("Should be in read-write mode", States.CONNECTED, + zk.getState()); + LOG.info("Connected as rw mode with state {} and session id {}", + zk.getState(), zk.getSessionId()); zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Assert.assertFalse("fake session and real session have same id", zk .getSessionId() == fakeId); - zk.close(); } From 04b7553698d38b10f53aa8861fb07986e469c34f Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Mon, 24 Apr 2017 10:07:37 -0700 Subject: [PATCH 407/444] ZOOKEEPER-2758: Fix typo Continued from #210 and targeting the 3.4 branch as requested. Author: Jeff Widman Reviewers: Michael Han Closes #228 from jeffwidman/patch-2 --- src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 6d4512199f5..27060e09a57 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1668,7 +1668,7 @@ imok - incorrect placement of transasction log + incorrect placement of transaction log The most performance critical part of ZooKeeper is the From 73ec93cfa07c860121d544e924fc5a425a7564d0 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 27 Apr 2017 14:15:04 -0700 Subject: [PATCH 408/444] ZOOKEEPER-2759: Flaky test: org.apache.zookeeper.server.quorum.QuorumCnxManagerTest.testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid I noticed some inconsistent test results from `testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid`. It can be seen failing on Apache infra here: http://mail-archives.apache.org/mod_mbox/zookeeper-dev/201701.mbox/%3c374175863.2852.1485127554310.JavaMail.jenkinscrius%3e The problem can be "reproduced" by adding a `Thread.sleep(4000)` as the first line of the finally block for `RecvWorker.run` in `QuorumCnxManager.java`. The issue is caused by a race condition between the removal of the relevant `sid` from `senderWorkerMap` for `peer0` and the 3 second delay from `assertEventuallyNotConnected`. Other tests in this class do not experience the same problem because `testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid` is unique in that it is the only test using `assertEventuallyNotConnected` where the peer with the lower `sid` is not using SASL. This means that the peer with the lower `sid` will always create send and receive workers in `handleConnection` (as it will not throw an exception on `authServer.authenticate(sock, din)`) and may not destroy them in time for the test assertion. Author: Abraham Fine Reviewers: Michael Han Closes #229 from afine/ZOOKEEPER-2759 and squashes the following commits: 55869fa [Abraham Fine] add some comments 112a70b [Abraham Fine] fix tests with mockito c567c49 [Abraham Fine] ZOOKEEPER-2759: Flaky test: org.apache.zookeeper.server.quorum.QuorumCnxManagerTest.testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid --- ivy.xml | 2 +- .../server/quorum/QuorumCnxManager.java | 18 +++++- .../server/quorum/QuorumCnxManagerTest.java | 60 ++++++++++++++++--- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/ivy.xml b/ivy.xml index 1a9af7612ab..8aa76c53a9d 100644 --- a/ivy.xml +++ b/ivy.xml @@ -56,7 +56,7 @@ - diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 6a5ebb4f343..33f19437ffa 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -175,11 +175,25 @@ public QuorumCnxManager(final long mySid, boolean listenOnAllIPs, int quorumCnxnThreadsSize, boolean quorumSaslAuthEnabled) { + this(mySid, view, authServer, authLearner, socketTimeout, listenOnAllIPs, + quorumCnxnThreadsSize, quorumSaslAuthEnabled, new ConcurrentHashMap()); + } + + // visible for testing + public QuorumCnxManager(final long mySid, + Map view, + QuorumAuthServer authServer, + QuorumAuthLearner authLearner, + int socketTimeout, + boolean listenOnAllIPs, + int quorumCnxnThreadsSize, + boolean quorumSaslAuthEnabled, + ConcurrentHashMap senderWorkerMap) { + this.senderWorkerMap = senderWorkerMap; + this.recvQueue = new ArrayBlockingQueue(RECV_CAPACITY); this.queueSendMap = new ConcurrentHashMap>(); - this.senderWorkerMap = new ConcurrentHashMap(); this.lastMessageSent = new ConcurrentHashMap(); - String cnxToValue = System.getProperty("zookeeper.cnxTimeout"); if(cnxToValue != null){ this.cnxTO = new Integer(cnxToValue); diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java index 6b6c0b4c331..ecf59acf30b 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -68,6 +69,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + public class QuorumCnxManagerTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(QuorumCnxManagerTest.class); private int count; @@ -182,9 +191,9 @@ public void testClientAuthAgainstNoAuthServerWithLowerSid() /** * Peer0 has auth configured, Peer1 has no auth configured. - * Peer1 connects to peer0, but is disconnected, because peer1's sid is + * Peer0 connects to peer1, but is disconnected, because peer1's sid is * higher than peer0. - * Peer0 connects to peer1, but is disconnected, because peer1 cannot + * Peer1 connects to peer0, but is disconnected, because peer1 cannot * handle auth. */ @Test(timeout = 30000) @@ -218,16 +227,36 @@ public void testNoAuthLearnerConnectToAuthRequiredServerWithLowerSid() * No auth learner connects to a server that requires auth, when the server * has a higher sid. * The connection should fail in both directions. + * + * peer0 should attempt to connect to peer1, but disconnect as its sid is lower + * peer1 should attempt to connect to peer0, peer0 will accept and add an entry to + * the senderWorkerMap but peer1 will disconnect because peer1 will start speaking SASL + * and peer0 will consider this invalid. + * + * Due to the unique behavior of peer0 creating an entry + * in senderWorkerMap for peer1 and then deleting it we use mockito spies to track + * this behavior. */ @Test(timeout = 30000) public void testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid() throws Exception { - QuorumCnxManager peer0 = createAndStartManager(0); - QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", - "QuorumLearner", true, true); + ConcurrentHashMap senderWorkerMap0 = + spy(new ConcurrentHashMap()); + ConcurrentHashMap senderWorkerMap1 = + spy(new ConcurrentHashMap()); + + QuorumCnxManager peer0 = createAndStartManager(0, senderWorkerMap0); + QuorumCnxManager peer1 = createAndStartManager(1, "QuorumServer", "QuorumLearner", + true, true, senderWorkerMap1); peer0.connectOne(1); peer1.connectOne(0); + assertEventuallyNotConnected(peer0, 1); + + verify(senderWorkerMap0, timeout(10000)).put(eq(1L), any(QuorumCnxManager.SendWorker.class)); + verify(senderWorkerMap0, timeout(10000)).remove(eq(1L), any(QuorumCnxManager.SendWorker.class)); + + verify(senderWorkerMap1, never()).put(anyLong(), any(QuorumCnxManager.SendWorker.class)); } /** @@ -679,9 +708,14 @@ public void testNullQuorumAuthServerWithValidQuorumAuthPacket() } private QuorumCnxManager createAndStartManager(long sid) { + return createAndStartManager(sid, new ConcurrentHashMap()); + } + + private QuorumCnxManager createAndStartManager(long sid, ConcurrentHashMap senderWorkerMap) { QuorumCnxManager peer = new QuorumCnxManager(sid, peers, new NullQuorumAuthServer(), new NullQuorumAuthLearner(), 10000, - false, quorumCnxnThreadsSize, false); + false, quorumCnxnThreadsSize, false, + senderWorkerMap); executor.submit(peer.listener); InetSocketAddress electionAddr = peer.view.get(sid).electionAddr; waitForElectionAddrBinding(electionAddr, 15); @@ -692,14 +726,24 @@ private QuorumCnxManager createAndStartManager(long sid, String serverLoginContext, String learnerLoginContext, boolean serverRequireSasl, - boolean learnerRequireSasl) + boolean learnerRequireSasl) throws Exception { + return createAndStartManager(sid, serverLoginContext, learnerLoginContext, serverRequireSasl, learnerRequireSasl, new ConcurrentHashMap()); + + } + + private QuorumCnxManager createAndStartManager(long sid, + String serverLoginContext, + String learnerLoginContext, + boolean serverRequireSasl, + boolean learnerRequireSasl, + ConcurrentHashMap senderWorkerMap) throws Exception { QuorumAuthLearner authClient = new SaslQuorumAuthLearner(learnerRequireSasl, "NOT_USING_KRB_PRINCIPAL", learnerLoginContext); QuorumAuthServer authServer = new SaslQuorumAuthServer(serverRequireSasl, serverLoginContext, authzHosts); QuorumCnxManager peer = new QuorumCnxManager(sid, peers, - authServer, authClient, 10000, false, quorumCnxnThreadsSize, true); + authServer, authClient, 10000, false, quorumCnxnThreadsSize, true, senderWorkerMap); executor.submit(peer.listener); InetSocketAddress electionAddr = peer.view.get(sid).electionAddr; waitForElectionAddrBinding(electionAddr, 15); From ad26e22dffc35af991d32bf95aa8ada9f99322ed Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Fri, 28 Apr 2017 08:52:17 -0700 Subject: [PATCH 409/444] ZOOKEEPER-2759: Flaky test: org.apache.zookeeper.server.quorum.QuorumCnxManagerTest.testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid Remove extra assertion accidentally added in https://github.com/apache/zookeeper/pull/229 as per hanm Author: Abraham Fine Reviewers: Michael Han Closes #242 from afine/ZOOKEEPER-2759_fixed --- .../apache/zookeeper/server/quorum/QuorumCnxManagerTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java index ecf59acf30b..9917cfc7388 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumCnxManagerTest.java @@ -251,8 +251,6 @@ public void testNoAuthLearnerConnectToAuthRequiredServerWithHigherSid() peer0.connectOne(1); peer1.connectOne(0); - assertEventuallyNotConnected(peer0, 1); - verify(senderWorkerMap0, timeout(10000)).put(eq(1L), any(QuorumCnxManager.SendWorker.class)); verify(senderWorkerMap0, timeout(10000)).remove(eq(1L), any(QuorumCnxManager.SendWorker.class)); From 593c99c3cde9d9f24e62d1e613108e3c24d73d4c Mon Sep 17 00:00:00 2001 From: Abhishek Singh Chouhan Date: Thu, 18 May 2017 14:14:39 -0700 Subject: [PATCH 410/444] =?UTF-8?q?ZOOKEEPER-2785:=20Server=20inappropriat?= =?UTF-8?q?ely=20throttles=20connections=20under=20loa=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …d before SASL completes Author: Abhishek Singh Chouhan Reviewers: Michael Han Closes #256 from abhishek-chouhan/ZOOKEEPER-2785 (cherry picked from commit 0a3e2d1d4bdbb96f41da7111db3913ef08068722) Signed-off-by: Michael Han --- src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index b0a834102aa..66e5afec534 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -1008,6 +1008,7 @@ public void processPacket(ServerCnxn cnxn, ByteBuffer incomingBuffer) throws IOE Record rsp = processSasl(incomingBuffer,cnxn); ReplyHeader rh = new ReplyHeader(h.getXid(), 0, KeeperException.Code.OK.intValue()); cnxn.sendResponse(rh,rsp, "response"); // not sure about 3rd arg..what is it? + return; } else { Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(), From 3c4f01f6450f55d2fcc605f66c6c6584ccff1584 Mon Sep 17 00:00:00 2001 From: Jiang Jiafu Date: Thu, 18 May 2017 14:31:01 -0700 Subject: [PATCH 411/444] Ephemeral znode will not be removed when sesstion timeout, if the system time of ZooKeeper node changes unexpectedly. Author: Jiang Jiafu Author: JiangJiafu Reviewers: Michael Han Closes #253 from JiangJiafu/ZOOKEEPER-2774 --- .../main/org/apache/zookeeper/ClientCnxn.java | 5 +- .../apache/zookeeper/ClientCnxnSocket.java | 3 +- src/java/main/org/apache/zookeeper/Login.java | 10 +- src/java/main/org/apache/zookeeper/Shell.java | 7 +- .../main/org/apache/zookeeper/ZKUtil.java | 2 +- .../org/apache/zookeeper/common/Time.java | 52 +++++++++ .../zookeeper/server/ConnectionBean.java | 4 +- .../server/FinalRequestProcessor.java | 7 +- .../server/PrepRequestProcessor.java | 12 +- .../org/apache/zookeeper/server/Request.java | 3 +- .../apache/zookeeper/server/ServerStats.java | 5 +- .../zookeeper/server/SessionTrackerImpl.java | 9 +- .../zookeeper/server/ZooKeeperServer.java | 4 - .../server/quorum/AuthFastLeaderElection.java | 3 +- .../server/quorum/FastLeaderElection.java | 3 +- .../zookeeper/server/quorum/Follower.java | 3 +- .../zookeeper/server/quorum/Leader.java | 15 +-- .../zookeeper/test/system/GenerateLoad.java | 21 ++-- .../org/apache/zookeeper/common/TimeTest.java | 109 ++++++++++++++++++ .../server/quorum/QuorumPeerMainTest.java | 5 +- .../org/apache/zookeeper/test/ClientBase.java | 21 ++-- .../zookeeper/test/ClientHammerTest.java | 7 +- .../zookeeper/test/LoadFromLogTest.java | 19 +-- .../zookeeper/test/ReadOnlyModeTest.java | 8 +- .../test/StaticHostProviderTest.java | 9 +- .../org/apache/zookeeper/test/TestHammer.java | 5 +- .../zookeeper/test/ZooKeeperTestClient.java | 3 +- 27 files changed, 268 insertions(+), 86 deletions(-) create mode 100644 src/java/main/org/apache/zookeeper/common/Time.java create mode 100644 src/java/test/org/apache/zookeeper/common/TimeTest.java diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 08934b0f856..3cf2f755044 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -60,6 +60,7 @@ import org.apache.zookeeper.ZooKeeper.WatchRegistration; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.proto.AuthPacket; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.CreateResponse; @@ -1041,7 +1042,7 @@ public void run() { clientCnxnSocket.updateNow(); clientCnxnSocket.updateLastSendAndHeard(); int to; - long lastPingRwServer = System.currentTimeMillis(); + long lastPingRwServer = Time.currentElapsedTime(); final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds while (state.isAlive()) { try { @@ -1126,7 +1127,7 @@ public void run() { // If we are in read-only mode, seek for read/write server if (state == States.CONNECTEDREADONLY) { - long now = System.currentTimeMillis(); + long now = Time.currentElapsedTime(); int idlePingRwServer = (int) (now - lastPingRwServer); if (idlePingRwServer >= pingRwTimeout) { lastPingRwServer = now; diff --git a/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java b/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java index 5ca0ba77bcc..b67653141ba 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxnSocket.java @@ -27,6 +27,7 @@ import org.apache.jute.BinaryInputArchive; import org.apache.zookeeper.ClientCnxn.Packet; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.proto.ConnectResponse; import org.apache.zookeeper.server.ByteBufferInputStream; import org.slf4j.Logger; @@ -74,7 +75,7 @@ void introduce(ClientCnxn.SendThread sendThread, long sessionId) { } void updateNow() { - now = System.currentTimeMillis(); + now = Time.currentElapsedTime(); } int getIdleRecv() { diff --git a/src/java/main/org/apache/zookeeper/Login.java b/src/java/main/org/apache/zookeeper/Login.java index 3e21aae7c8c..c4975be1997 100644 --- a/src/java/main/org/apache/zookeeper/Login.java +++ b/src/java/main/org/apache/zookeeper/Login.java @@ -33,6 +33,7 @@ import javax.security.auth.callback.CallbackHandler; import org.apache.zookeeper.client.ZooKeeperSaslClient; +import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.security.auth.kerberos.KerberosTicket; @@ -74,7 +75,8 @@ public class Login { private String keytabFile = null; private String principal = null; - private long lastLogin = 0; + // Initialize 'lastLogin' to do a login at first time + private long lastLogin = Time.currentElapsedTime() - MIN_TIME_BEFORE_RELOGIN; /** * LoginThread constructor. The constructor starts the thread used @@ -128,7 +130,7 @@ public void run() { LOG.info("TGT refresh thread started."); while (true) { // renewal thread's main loop. if it exits from here, thread will exit. KerberosTicket tgt = getTGT(); - long now = System.currentTimeMillis(); + long now = Time.currentWallTime(); long nextRefresh; Date nextRefreshDate; if (tgt == null) { @@ -306,7 +308,7 @@ private long getRefreshTime(KerberosTicket tgt) { (TICKET_RENEW_WINDOW + (TICKET_RENEW_JITTER * rng.nextDouble()))); if (proposedRefresh > expires) { // proposedRefresh is too far in the future: it's after ticket expires: simply return now. - return System.currentTimeMillis(); + return Time.currentWallTime(); } else { return proposedRefresh; @@ -327,7 +329,7 @@ private synchronized KerberosTicket getTGT() { } private boolean hasSufficientTimeElapsed() { - long now = System.currentTimeMillis(); + long now = Time.currentElapsedTime(); if (now - getLastLogin() < MIN_TIME_BEFORE_RELOGIN ) { LOG.warn("Not attempting to re-login since the last re-login was " + "attempted less than " + (MIN_TIME_BEFORE_RELOGIN/1000) + " seconds"+ diff --git a/src/java/main/org/apache/zookeeper/Shell.java b/src/java/main/org/apache/zookeeper/Shell.java index 789c481a634..97efad34d4e 100644 --- a/src/java/main/org/apache/zookeeper/Shell.java +++ b/src/java/main/org/apache/zookeeper/Shell.java @@ -39,8 +39,9 @@ import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.log4j.Logger; +import org.apache.zookeeper.common.Time; -/** +/** * A base class for running a Unix command. * * Shell can be used to run unix commands like du or @@ -146,7 +147,7 @@ protected void setWorkingDirectory(File dir) { /** check to see if a command needs to be executed and execute if needed */ protected void run() throws IOException { - if (lastTime + interval > System.currentTimeMillis()) + if (lastTime + interval > Time.currentElapsedTime()) return; exitCode = 0; // reset for next run runCommand(); @@ -245,7 +246,7 @@ public void run() { LOG.warn("Error while closing the error stream", ioe); } process.destroy(); - lastTime = System.currentTimeMillis(); + lastTime = Time.currentElapsedTime(); } } diff --git a/src/java/main/org/apache/zookeeper/ZKUtil.java b/src/java/main/org/apache/zookeeper/ZKUtil.java index 4713a08a934..e901832cfb2 100644 --- a/src/java/main/org/apache/zookeeper/ZKUtil.java +++ b/src/java/main/org/apache/zookeeper/ZKUtil.java @@ -121,4 +121,4 @@ public static List listSubTreeBFS(ZooKeeper zk, final String pathRoot) t return tree; } -} \ No newline at end of file +} diff --git a/src/java/main/org/apache/zookeeper/common/Time.java b/src/java/main/org/apache/zookeeper/common/Time.java new file mode 100644 index 00000000000..83e53f056b9 --- /dev/null +++ b/src/java/main/org/apache/zookeeper/common/Time.java @@ -0,0 +1,52 @@ +/** + * 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.common; + +import java.util.Date; + +public class Time { + /** + * Returns time in milliseconds as does System.currentTimeMillis(), + * but uses elapsed time from an arbitrary epoch more like System.nanoTime(). + * The difference is that if somebody changes the system clock, + * Time.currentElapsedTime will change but nanoTime won't. On the other hand, + * all of ZK assumes that time is measured in milliseconds. + * @return The time in milliseconds from some arbitrary point in time. + */ + public static long currentElapsedTime() { + return System.nanoTime() / 1000000; + } + + /** + * Explicitly returns system dependent current wall time. + * @return Current time in msec. + */ + public static long currentWallTime() { + return System.currentTimeMillis(); + } + + /** + * This is to convert the elapsedTime to a Date. + * @return A date object indicated by the elapsedTime. + */ + public static Date elapsedTimeToDate(long elapsedTime) { + long wallTime = currentWallTime() + elapsedTime - currentElapsedTime(); + return new Date(wallTime); + } +} \ No newline at end of file diff --git a/src/java/main/org/apache/zookeeper/server/ConnectionBean.java b/src/java/main/org/apache/zookeeper/server/ConnectionBean.java index 917aacfdcdc..58917e05f2b 100644 --- a/src/java/main/org/apache/zookeeper/server/ConnectionBean.java +++ b/src/java/main/org/apache/zookeeper/server/ConnectionBean.java @@ -22,10 +22,10 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Arrays; -import java.util.Date; import javax.management.ObjectName; +import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.MBeanRegistry; @@ -164,7 +164,7 @@ public String getLastZxid() { } public String getLastResponseTime() { - return new Date(stats.getLastResponseTime()).toString(); + return Time.elapsedTimeToDate(stats.getLastResponseTime()).toString(); } public long getLastLatency() { diff --git a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java index 7278064382a..65f7ac03472 100644 --- a/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/FinalRequestProcessor.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.jute.Record; +import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; @@ -165,7 +166,7 @@ public void processRequest(Request request) { lastOp = "PING"; cnxn.updateStatsForResponse(request.cxid, request.zxid, lastOp, - request.createTime, System.currentTimeMillis()); + request.createTime, Time.currentElapsedTime()); cnxn.sendResponse(new ReplyHeader(-2, zks.getZKDatabase().getDataTreeLastProcessedZxid(), 0), null, "response"); @@ -176,7 +177,7 @@ public void processRequest(Request request) { lastOp = "SESS"; cnxn.updateStatsForResponse(request.cxid, request.zxid, lastOp, - request.createTime, System.currentTimeMillis()); + request.createTime, Time.currentElapsedTime()); zks.finishSessionInit(request.cnxn, true); return; @@ -385,7 +386,7 @@ public void processRequest(Request request) { zks.serverStats().updateLatency(request.createTime); cnxn.updateStatsForResponse(request.cxid, lastZxid, lastOp, - request.createTime, System.currentTimeMillis()); + request.createTime, Time.currentElapsedTime()); try { cnxn.sendResponse(hdr, rsp, "response"); diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 1248b08115d..58497b76db1 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -35,6 +35,7 @@ import org.apache.jute.Record; import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; @@ -318,7 +319,7 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, throws KeeperException, IOException, RequestProcessorException { request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid, - zks.getTime(), type); + Time.currentWallTime(), type); switch (type) { case OpCode.create: @@ -557,9 +558,9 @@ protected void pRequest(Request request) throws RequestProcessorException { try { ByteBufferInputStream.byteBuffer2Record(request.request, multiRequest); } catch(IOException e) { - request.hdr = new TxnHeader(request.sessionId, request.cxid, zks.getNextZxid(), - zks.getTime(), OpCode.multi); - throw e; + request.hdr = new TxnHeader(request.sessionId, request.cxid, zks.getNextZxid(), + Time.currentWallTime(), OpCode.multi); + throw e; } List txns = new ArrayList(); //Each op in a multi-op must have the same zxid! @@ -616,7 +617,8 @@ protected void pRequest(Request request) throws RequestProcessorException { index++; } - request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid, zks.getTime(), request.type); + request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid, + Time.currentWallTime(), request.type); request.txn = new MultiTxn(txns); break; diff --git a/src/java/main/org/apache/zookeeper/server/Request.java b/src/java/main/org/apache/zookeeper/server/Request.java index 80d2b99d569..bb8b1ca3fbc 100644 --- a/src/java/main/org/apache/zookeeper/server/Request.java +++ b/src/java/main/org/apache/zookeeper/server/Request.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.OpCode; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.txn.TxnHeader; @@ -75,7 +76,7 @@ public Request(ServerCnxn cnxn, long sessionId, int xid, int type, public final List authInfo; - public final long createTime = System.currentTimeMillis(); + public final long createTime = Time.currentElapsedTime(); private Object owner; diff --git a/src/java/main/org/apache/zookeeper/server/ServerStats.java b/src/java/main/org/apache/zookeeper/server/ServerStats.java index dbee6d54094..788599522f2 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerStats.java +++ b/src/java/main/org/apache/zookeeper/server/ServerStats.java @@ -19,6 +19,9 @@ package org.apache.zookeeper.server; + +import org.apache.zookeeper.common.Time; + /** * Basic Server Statistics */ @@ -102,7 +105,7 @@ public String toString(){ } // mutators synchronized void updateLatency(long requestCreateTime) { - long latency = System.currentTimeMillis() - requestCreateTime; + long latency = Time.currentElapsedTime() - requestCreateTime; totalLatency += latency; count++; if (latency < minLatency) { diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java index 938e9dac289..cdaaf2beb03 100644 --- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java +++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.SessionExpiredException; +import org.apache.zookeeper.common.Time; /** * This is a full featured SessionTracker. It tracks session in grouped by tick @@ -74,7 +75,7 @@ public static class SessionImpl implements Session { public static long initializeNextSession(long id) { long nextSid = 0; - nextSid = (System.currentTimeMillis() << 24) >>> 8; + nextSid = (Time.currentElapsedTime() << 24) >>> 8; nextSid = nextSid | (id <<56); return nextSid; } @@ -98,7 +99,7 @@ public SessionTrackerImpl(SessionExpirer expirer, this.expirer = expirer; this.expirationInterval = tickTime; this.sessionsWithTimeout = sessionsWithTimeout; - nextExpirationTime = roundToInterval(System.currentTimeMillis()); + nextExpirationTime = roundToInterval(Time.currentElapsedTime()); this.nextSessionId = initializeNextSession(sid); for (Entry e : sessionsWithTimeout.entrySet()) { addSession(e.getKey(), e.getValue()); @@ -141,7 +142,7 @@ synchronized public String toString() { synchronized public void run() { try { while (running) { - currentTime = System.currentTimeMillis(); + currentTime = Time.currentElapsedTime(); if (nextExpirationTime > currentTime) { this.wait(nextExpirationTime - currentTime); continue; @@ -174,7 +175,7 @@ synchronized public boolean touchSession(long sessionId, int timeout) { if (s == null || s.isClosing()) { return false; } - long expireTime = roundToInterval(System.currentTimeMillis() + timeout); + long expireTime = roundToInterval(Time.currentElapsedTime() + timeout); if (s.tickTime >= expireTime) { // Nothing needs to be done return true; diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java index 66e5afec534..f29ee90ebd5 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java @@ -325,10 +325,6 @@ public void setZxid(long zxid) { hzxid.set(zxid); } - long getTime() { - return System.currentTimeMillis(); - } - private void close(long sessionId) { submitRequest(null, sessionId, OpCode.closeSession, 0, null, null); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java index 5bf54f81498..d32a725abf4 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java @@ -36,6 +36,7 @@ import java.util.concurrent.TimeUnit; import java.util.Random; +import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -412,7 +413,7 @@ class WorkerSender implements Runnable { WorkerSender(int attempts) { maxAttempts = attempts; rand = new Random(java.lang.Thread.currentThread().getId() - + System.currentTimeMillis()); + + Time.currentElapsedTime()); } long genChallenge() { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index 2a3d4fd4da1..dc5f099bbe7 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -27,6 +27,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.quorum.QuorumCnxManager.Message; @@ -801,7 +802,7 @@ public Vote lookForLeader() throws InterruptedException { self.jmxLeaderElectionBean = null; } if (self.start_fle == 0) { - self.start_fle = System.currentTimeMillis(); + self.start_fle = Time.currentElapsedTime(); } try { HashMap recvset = new HashMap(); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java index a17af496e94..e439aaa60cd 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java @@ -22,6 +22,7 @@ import java.net.InetSocketAddress; import org.apache.jute.Record; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; @@ -58,7 +59,7 @@ public String toString() { * @throws InterruptedException */ void followLeader() throws InterruptedException { - self.end_fle = System.currentTimeMillis(); + self.end_fle = Time.currentElapsedTime(); long electionTimeTaken = self.end_fle - self.start_fle; self.setElectionTimeTaken(electionTimeTaken); LOG.info("FOLLOWING - LEADER ELECTION TOOK - {}", electionTimeTaken); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index 44e6b4f4e56..bd7bf35bf00 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -41,6 +41,7 @@ import javax.security.sasl.SaslException; import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; @@ -365,7 +366,7 @@ public void halt() { * @throws InterruptedException */ void lead() throws IOException, InterruptedException { - self.end_fle = System.currentTimeMillis(); + self.end_fle = Time.currentElapsedTime(); long electionTimeTaken = self.end_fle - self.start_fle; self.setElectionTimeTaken(electionTimeTaken); LOG.info("LEADING - LEADER ELECTION TOOK - {}", electionTimeTaken); @@ -885,12 +886,12 @@ public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws Interrupt self.setAcceptedEpoch(epoch); connectingFollowers.notifyAll(); } else { - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); long cur = start; long end = start + self.getInitLimit()*self.getTickTime(); while(waitingForNewEpoch && cur < end) { connectingFollowers.wait(end - cur); - cur = System.currentTimeMillis(); + cur = Time.currentElapsedTime(); } if (waitingForNewEpoch) { throw new InterruptedException("Timeout while waiting for epoch from quorum"); @@ -922,12 +923,12 @@ public void waitForEpochAck(long id, StateSummary ss) throws IOException, Interr electionFinished = true; electingFollowers.notifyAll(); } else { - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); long cur = start; long end = start + self.getInitLimit()*self.getTickTime(); while(!electionFinished && cur < end) { electingFollowers.wait(end - cur); - cur = System.currentTimeMillis(); + cur = Time.currentElapsedTime(); } if (!electionFinished) { throw new InterruptedException("Timeout while waiting for epoch to be acked by quorum"); @@ -1010,12 +1011,12 @@ public void waitForNewLeaderAck(long sid, long zxid, LearnerType learnerType) quorumFormed = true; newLeaderProposal.ackSet.notifyAll(); } else { - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); long cur = start; long end = start + self.getInitLimit() * self.getTickTime(); while (!quorumFormed && cur < end) { newLeaderProposal.ackSet.wait(end - cur); - cur = System.currentTimeMillis(); + cur = Time.currentElapsedTime(); } if (!quorumFormed) { throw new InterruptedException( diff --git a/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java b/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java index cfd4e7b5245..b6ac04aa323 100644 --- a/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java +++ b/src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java @@ -53,6 +53,7 @@ import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.common.Time; public class GenerateLoad { protected static final Logger LOG = LoggerFactory.getLogger(GenerateLoad.class); @@ -194,7 +195,7 @@ static class ReporterThread extends Thread { public void run() { try { - currentInterval = System.currentTimeMillis() / INTERVAL; + currentInterval = Time.currentElapsedTime() / INTERVAL; // Give things time to report; Thread.sleep(INTERVAL * 2); long min = 99999; @@ -202,7 +203,7 @@ public void run() { long total = 0; int number = 0; while (true) { - long now = System.currentTimeMillis(); + long now = Time.currentElapsedTime(); long lastInterval = currentInterval; currentInterval += 1; long count = remove(lastInterval); @@ -249,13 +250,13 @@ public void run() { } synchronized static void sendChange(int percentage) { - long now = System.currentTimeMillis(); + long now = Time.currentElapsedTime(); long start = now; ReporterThread.percentage = percentage; for (SlaveThread st : slaves.toArray(new SlaveThread[0])) { st.send(percentage); } - now = System.currentTimeMillis(); + now = Time.currentElapsedTime(); long delay = now - start; if (delay > 1000) { System.out.println("Delay of " + delay + " to send new percentage"); @@ -387,7 +388,7 @@ public void processResult(int rc, String path, Object ctx, byte[] data, errors++; } else { finished++; - rlatency += System.currentTimeMillis() - (Long) ctx; + rlatency += Time.currentElapsedTime() - (Long) ctx; reads++; } } @@ -401,7 +402,7 @@ public void processResult(int rc, String path, Object ctx, Stat stat) { errors++; } else { finished++; - wlatency += System.currentTimeMillis() - (Long) ctx; + wlatency += Time.currentElapsedTime() - (Long) ctx; writes++; } } @@ -427,7 +428,7 @@ public void run() { if (percentage == -1 || (finished == 0 && errors == 0)) { continue; } - String report = System.currentTimeMillis() + " " + String report = Time.currentElapsedTime() + " " + percentage + " " + finished + " " + errors + " " + outstanding + "\n"; /* String subreport = reads + " " @@ -547,9 +548,9 @@ public boolean isConnected() { synchronized public boolean waitConnected(long timeout) throws InterruptedException { - long endTime = System.currentTimeMillis() + timeout; - while (!connected && System.currentTimeMillis() < endTime) { - wait(endTime - System.currentTimeMillis()); + long endTime = Time.currentElapsedTime() + timeout; + while (!connected && Time.currentElapsedTime() < endTime) { + wait(endTime - Time.currentElapsedTime()); } return connected; } diff --git a/src/java/test/org/apache/zookeeper/common/TimeTest.java b/src/java/test/org/apache/zookeeper/common/TimeTest.java new file mode 100644 index 00000000000..d938556939c --- /dev/null +++ b/src/java/test/org/apache/zookeeper/common/TimeTest.java @@ -0,0 +1,109 @@ +/** + * 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.common; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.test.ClientBase; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Command line program for demonstrating robustness to clock + * changes. + *

            + * How to run: + * ant clean compile-test + * echo build/test/lib/*.jar build/lib/*.jar build/classes build/test/classes | sed -e 's/ /:/g' > cp + * java -cp $(cat cp) org.apache.zookeeper.common.TimeTest | tee log-without-patch + *

            + * After test program starts, in another window, do commands: + * date -s '+1hour' + * date -s '-1hour' + *

            + * As long as there isn't any expired event, the experiment is successful. + */ +public class TimeTest extends ClientBase { + private static final long mt0 = System.currentTimeMillis(); + private static final long nt0 = Time.currentElapsedTime(); + + private static AtomicInteger watchCount = new AtomicInteger(0); + + + public static void main(String[] args) throws Exception { + System.out.printf("Starting\n"); + final TimeTest test = new TimeTest(); + System.out.printf("After construct\n"); + test.setUp(); + ZooKeeper zk = test.createClient(); + zk.create("/ephemeral", new byte[]{1, 2, 3}, + ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); + while (Time.currentElapsedTime() - nt0 < 100000) { + System.out.printf("%d\t%s\n", discrepancy(), + zk.exists("/ephemeral", + watchCount.get() == 0 ? createWatcher() : null) != null); + waitByYielding(500); + } + } + + private static Watcher createWatcher() { + watchCount.incrementAndGet(); + return new Watcher() { + @Override + public void process(WatchedEvent event) { + watchCount.decrementAndGet(); + System.out.printf("%d event = %s\n", discrepancy(), event); + } + }; + + } + + private static void waitByYielding(long delay) { + long t0 = Time.currentElapsedTime(); + while (Time.currentElapsedTime() < t0 + delay) { + Thread.yield(); + } + } + + private static long discrepancy() { + return (System.currentTimeMillis() - mt0) - (Time.currentElapsedTime() - nt0); + } + + @Test + public void testElapsedTimeToDate() throws Exception { + long walltime = Time.currentWallTime(); + long elapsedTime = Time.currentElapsedTime(); + Thread.sleep(200); + + Calendar cal = Calendar.getInstance(); + cal.setTime(Time.elapsedTimeToDate(elapsedTime)); + int calculatedDate = cal.get(Calendar.HOUR_OF_DAY); + cal.setTime(new Date(walltime)); + int realDate = cal.get(Calendar.HOUR_OF_DAY); + + Assert.assertEquals(calculatedDate, realDate); + } +} 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 5ea92cb25db..e8ba8bb591e 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java @@ -47,6 +47,7 @@ import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper.States; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.util.ZxidUtils; @@ -814,9 +815,9 @@ public void testQuorumPeerExitTime() throws Exception { q1.start(); // Let the notifications timeout Thread.sleep(30000); - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); q1.shutdown(); - long end = System.currentTimeMillis(); + long end = Time.currentElapsedTime(); if ((end - start) > maxwait) { Assert.fail("QuorumPeer took " + (end -start) + " to shutdown, expected " + maxwait); diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index 08cc13cb33a..f2ad4d51e70 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -42,6 +42,7 @@ import junit.framework.TestCase; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; @@ -134,11 +135,11 @@ synchronized public boolean isConnected() { synchronized public void waitForConnected(long timeout) throws InterruptedException, TimeoutException { - long expire = System.currentTimeMillis() + timeout; + long expire = Time.currentElapsedTime() + timeout; long left = timeout; while(!connected && left > 0) { wait(left); - left = expire - System.currentTimeMillis(); + left = expire - Time.currentElapsedTime(); } if (!connected) { throw new TimeoutException("Did not connect"); @@ -161,11 +162,11 @@ synchronized public void waitForSyncConnected(long timeout) synchronized public void waitForDisconnected(long timeout) throws InterruptedException, TimeoutException { - long expire = System.currentTimeMillis() + timeout; + long expire = Time.currentElapsedTime() + timeout; long left = timeout; while(connected && left > 0) { wait(left); - left = expire - System.currentTimeMillis(); + left = expire - Time.currentElapsedTime(); } if (connected) { throw new TimeoutException("Did not disconnect"); @@ -254,7 +255,7 @@ public static List parseHostPortList(String hplist) { } public static boolean waitForServerUp(String hp, long timeout) { - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); while (true) { try { // if there are multiple hostports, just take the first one @@ -269,7 +270,7 @@ public static boolean waitForServerUp(String hp, long timeout) { LOG.info("server " + hp + " not up " + e); } - if (System.currentTimeMillis() > start + timeout) { + if (Time.currentElapsedTime() > start + timeout) { break; } try { @@ -281,7 +282,7 @@ public static boolean waitForServerUp(String hp, long timeout) { return false; } public static boolean waitForServerDown(String hp, long timeout) { - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); while (true) { try { HostPort hpobj = parseHostPortList(hp).get(0); @@ -290,7 +291,7 @@ public static boolean waitForServerDown(String hp, long timeout) { return true; } - if (System.currentTimeMillis() > start + timeout) { + if (Time.currentElapsedTime() > start + timeout) { break; } try { @@ -304,7 +305,7 @@ public static boolean waitForServerDown(String hp, long timeout) { public static boolean waitForServerState(QuorumPeer qp, int timeout, String serverState) { - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); while (true) { try { Thread.sleep(250); @@ -313,7 +314,7 @@ public static boolean waitForServerState(QuorumPeer qp, int timeout, } if (qp.getServerState().equals(serverState)) return true; - if (System.currentTimeMillis() > start + timeout) { + if (Time.currentElapsedTime() > start + timeout) { return false; } } diff --git a/src/java/test/org/apache/zookeeper/test/ClientHammerTest.java b/src/java/test/org/apache/zookeeper/test/ClientHammerTest.java index 581402cfa89..025ccbe4cd7 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientHammerTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientHammerTest.java @@ -22,6 +22,7 @@ import java.util.Date; import java.util.List; +import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; @@ -124,7 +125,7 @@ public void runHammer(final int threadCount, final int childCount) { try { HammerThread[] threads = new HammerThread[threadCount]; - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); for (int i = 0; i < threads.length; i++) { ZooKeeper zk = createClient(); String prefix = "/test-" + i; @@ -157,7 +158,7 @@ public void testHammerSuper() throws Throwable { final int childCount = 10; HammerThread[] threads = new HammerThread[threadCount]; - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); for (int i = 0; i < threads.length; i++) { String prefix = "/test-" + i; { @@ -218,7 +219,7 @@ public void verifyHammer(long start, HammerThread[] threads, int childCount) * HAMMERTHREAD_LATENCY * safetyFactor); } LOG.info(new Date() + " Total time " - + (System.currentTimeMillis() - start)); + + (Time.currentElapsedTime() - start)); ZooKeeper zk = createClient(); try { diff --git a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java index 419683f0faf..94b0f97ec02 100644 --- a/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java +++ b/src/java/test/org/apache/zookeeper/test/LoadFromLogTest.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.zookeeper.common.Time; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; @@ -155,7 +156,7 @@ public void testTxnFailure() throws Exception { dt.createNode("/test", new byte[0], null, 0, -1, 1, 1); for (count = 1; count <= 3; count++) { dt.createNode("/test/" + count, new byte[0], null, 0, -1, count, - System.currentTimeMillis()); + Time.currentElapsedTime()); } DataNode zk = dt.getNode("/test"); @@ -204,15 +205,15 @@ private void doOp(FileTxnSnapLog logFile, int type, String path, if (type == OpCode.delete) { txn = new DeleteTxn(path); txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, - System.currentTimeMillis(), OpCode.delete); + Time.currentElapsedTime(), OpCode.delete); } else if (type == OpCode.create) { txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, - System.currentTimeMillis(), OpCode.create); + Time.currentElapsedTime(), OpCode.create); txn = new CreateTxn(path, new byte[0], null, false, cversion); } else if (type == OpCode.multi) { txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, - System.currentTimeMillis(), OpCode.create); + Time.currentElapsedTime(), OpCode.create); txn = new CreateTxn(path, new byte[0], null, false, cversion); ArrayList txnList = new ArrayList(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -223,7 +224,7 @@ else if (type == OpCode.multi) { txnList.add(txact); txn = new MultiTxn(txnList); txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, - System.currentTimeMillis(), OpCode.multi); + Time.currentElapsedTime(), OpCode.multi); } logFile.processTransaction(txnHeader, dt, null, txn); @@ -250,7 +251,7 @@ public void testPad() throws Exception { File tmpDir = ClientBase.createTmpDir(); FileTxnLog txnLog = new FileTxnLog(tmpDir); TxnHeader txnHeader = new TxnHeader(0xabcd, 0x123, 0x123, - System.currentTimeMillis(), OpCode.create); + Time.currentElapsedTime(), OpCode.create); Record txn = new CreateTxn("/Test", new byte[0], null, false, 1); txnLog.append(txnHeader, txn); FileInputStream in = new FileInputStream(tmpDir.getPath() + "/log." + @@ -444,9 +445,9 @@ public void testReloadSnapshotWithMissingParent() throws Exception { private ZooKeeper getConnectedZkClient() throws IOException { ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); while (!connected) { - long end = System.currentTimeMillis(); + long end = Time.currentElapsedTime(); if (end - start > 5000) { Assert.assertTrue("Could not connect with server in 5 seconds", false); @@ -459,4 +460,4 @@ private ZooKeeper getConnectedZkClient() throws IOException { } return zk; } -} \ No newline at end of file +} diff --git a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java index 1dbf6478c17..cc266d1f320 100644 --- a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java +++ b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java @@ -42,6 +42,7 @@ import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.After; import org.junit.Before; @@ -178,13 +179,12 @@ public void process(WatchedEvent event) { // kill peer and wait no more than 5 seconds for read-only server // to be started (which should take one tickTime (2 seconds)) qu.shutdown(2); - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); while (!(zk.getState() == States.CONNECTEDREADONLY)) { Thread.sleep(200); // FIXME this was originally 5 seconds, but realistically, on random/slow/virt hosts, there is no way to guarantee this - Assert.assertTrue("Can't connect to the server", System - .currentTimeMillis() - - start < 30000); + Assert.assertTrue("Can't connect to the server", + Time.currentElapsedTime() - start < 30000); } // At this point states list should contain, in the given order, diff --git a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java index 75d3c5901d2..aa78a4b20ed 100644 --- a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java +++ b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java @@ -26,6 +26,7 @@ import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.StaticHostProvider; +import org.apache.zookeeper.common.Time; import org.junit.Test; import org.slf4j.Logger; @@ -58,9 +59,9 @@ public void testNextGoesRoundAndSleeps() throws UnknownHostException { hostProvider.next(0); --size; } - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); hostProvider.next(1000); - long stop = System.currentTimeMillis(); + long stop = Time.currentElapsedTime(); assertTrue(900 <= stop - start); } @@ -72,9 +73,9 @@ public void testNextDoesNotSleepForZero() throws UnknownHostException { hostProvider.next(0); --size; } - long start = System.currentTimeMillis(); + long start = Time.currentElapsedTime(); hostProvider.next(0); - long stop = System.currentTimeMillis(); + long stop = Time.currentElapsedTime(); assertTrue(5 > stop - start); } diff --git a/src/java/test/org/apache/zookeeper/test/TestHammer.java b/src/java/test/org/apache/zookeeper/test/TestHammer.java index 09a678b28c1..a73d6df35df 100644 --- a/src/java/test/org/apache/zookeeper/test/TestHammer.java +++ b/src/java/test/org/apache/zookeeper/test/TestHammer.java @@ -24,6 +24,7 @@ import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.common.Time; public class TestHammer implements VoidCallback { @@ -32,7 +33,7 @@ public class TestHammer implements VoidCallback { */ static int REPS = 50000; public static void main(String[] args) { - long startTime = System.currentTimeMillis(); + long startTime = Time.currentElapsedTime(); ZooKeeper zk = null; try { zk = new ZooKeeper(args[0], 10000, null); @@ -51,7 +52,7 @@ public static void main(String[] args) { e.printStackTrace(); } } - System.out.println("creates/sec=" + (REPS*1000/(System.currentTimeMillis()-startTime))); + System.out.println("creates/sec=" + (REPS*1000/(Time.currentElapsedTime()-startTime))); } public void processResult(int rc, String path, Object ctx) { diff --git a/src/java/test/org/apache/zookeeper/test/ZooKeeperTestClient.java b/src/java/test/org/apache/zookeeper/test/ZooKeeperTestClient.java index 67ca52faed2..0bbba61d5d9 100644 --- a/src/java/test/org/apache/zookeeper/test/ZooKeeperTestClient.java +++ b/src/java/test/org/apache/zookeeper/test/ZooKeeperTestClient.java @@ -32,6 +32,7 @@ import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Stat; import org.junit.Assert; @@ -40,7 +41,7 @@ public class ZooKeeperTestClient extends ZKTestCase implements Watcher { protected static final String dirOnZK = "/test_dir"; - protected String testDirOnZK = dirOnZK + "/" + System.currentTimeMillis(); + protected String testDirOnZK = dirOnZK + "/" + Time.currentElapsedTime(); LinkedBlockingQueue events = new LinkedBlockingQueue(); From 6522d3f4d2bc7de363b3c2f562a2e48cafa86d45 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Sun, 21 May 2017 15:55:35 -0700 Subject: [PATCH 412/444] ZOOKEEPER-2732: Cleanup findbug warnings in branch-3.4: Performance Warnings rakeshadr Apologies for recreating this, I accidentally pushed a bad branch in https://github.com/apache/zookeeper/pull/231 and GitHub will not let me reset the head for that PR. This should be rebased and ready to be merged. Author: Abraham Fine Reviewers: Rakesh Radhakrishnan Closes #258 from afine/ZOOKEEPER-2732 --- .../main/org/apache/zookeeper/ZooKeeperMain.java | 4 ++-- .../main/org/apache/zookeeper/server/DataTree.java | 10 +++++----- .../zookeeper/server/quorum/FastLeaderElection.java | 6 +++--- .../zookeeper/server/quorum/QuorumCnxManager.java | 2 +- .../server/quorum/flexible/QuorumHierarchical.java | 12 +++++++----- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index 26721f54fb0..48aaa734146 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -90,8 +90,8 @@ public boolean getPrintWatches( ) { static void usage() { System.err.println("ZooKeeper -server host:port cmd args"); - for (String cmd : commandMap.keySet()) { - System.err.println("\t"+cmd+ " " + commandMap.get(cmd)); + for (Map.Entry entry : commandMap.entrySet()) { + System.err.println("\t" + entry.getKey() + " " + entry.getValue()); } } diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java index 656e4e9d209..f2537f35edd 100644 --- a/src/java/main/org/apache/zookeeper/server/DataTree.java +++ b/src/java/main/org/apache/zookeeper/server/DataTree.java @@ -1133,13 +1133,13 @@ public synchronized void dumpWatches(PrintWriter pwriter, boolean byPath) { * @param pwriter the output to write to */ public void dumpEphemerals(PrintWriter pwriter) { - Set keys = ephemerals.keySet(); + Set>> entrySet = ephemerals.entrySet(); pwriter.println("Sessions with Ephemerals (" - + keys.size() + "):"); - for (long k : keys) { - pwriter.print("0x" + Long.toHexString(k)); + + entrySet.size() + "):"); + for (Map.Entry> entry : entrySet) { + pwriter.print("0x" + Long.toHexString(entry.getKey())); pwriter.println(":"); - HashSet tmp = ephemerals.get(k); + HashSet tmp = entry.getValue(); if (tmp != null) { synchronized (tmp) { for (String path : tmp) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index dc5f099bbe7..066f3858596 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -122,15 +122,15 @@ static public class Notification { * epoch of the proposed leader */ long peerEpoch; - + @Override public String toString() { - return new String(Long.toHexString(version) + " (message format version), " + return Long.toHexString(version) + " (message format version), " + leader + " (n.leader), 0x" + Long.toHexString(zxid) + " (n.zxid), 0x" + Long.toHexString(electionEpoch) + " (n.round), " + state + " (n.state), " + sid + " (n.sid), 0x" - + Long.toHexString(peerEpoch) + " (n.peerEpoch) "); + + Long.toHexString(peerEpoch) + " (n.peerEpoch) "; } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 33f19437ffa..2b131c4e588 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -196,7 +196,7 @@ public QuorumCnxManager(final long mySid, this.lastMessageSent = new ConcurrentHashMap(); String cnxToValue = System.getProperty("zookeeper.cnxTimeout"); if(cnxToValue != null){ - this.cnxTO = new Integer(cnxToValue); + this.cnxTO = Integer.parseInt(cnxToValue); } this.mySid = mySid; diff --git a/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumHierarchical.java b/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumHierarchical.java index d37881f909a..428391e0e07 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumHierarchical.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/flexible/QuorumHierarchical.java @@ -206,8 +206,9 @@ private void parse(Properties quorumProp){ * different places, so we have a separate method. */ private void computeGroupWeight(){ - for(long sid : serverGroup.keySet()){ - Long gid = serverGroup.get(sid); + for(Entry entry : serverGroup.entrySet()){ + Long sid = entry.getKey(); + Long gid = entry.getValue(); if(!groupWeight.containsKey(gid)) groupWeight.put(gid, serverWeight.get(sid)); else { @@ -254,9 +255,10 @@ public boolean containsQuorum(HashSet set){ * Check if all groups have majority */ int majGroupCounter = 0; - for(long gid : expansion.keySet()) { - LOG.debug("Group info: " + expansion.get(gid) + ", " + gid + ", " + groupWeight.get(gid)); - if(expansion.get(gid) > (groupWeight.get(gid) / 2) ) + for(Entry entry : expansion.entrySet()) { + Long gid = entry.getKey(); + LOG.debug("Group info: " + entry.getValue() + ", " + gid + ", " + groupWeight.get(gid)); + if(entry.getValue() > (groupWeight.get(gid) / 2) ) majGroupCounter++; } From a378ea163b7110fa6f8aaf5fb07986d7901b1950 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Wed, 24 May 2017 02:25:24 -0700 Subject: [PATCH 413/444] ZOOKEEPER-2733: Cleanup findbug warnings in branch-3.4: Dodgy code Warnings Author: Abraham Fine Reviewers: Rakesh Radhakrishnan Closes #236 from afine/ZOOKEEPER-2733 --- .../server/PrepRequestProcessor.java | 9 ++++++--- .../apache/zookeeper/server/PurgeTxnLog.java | 15 ++++++++++---- .../server/SyncRequestProcessor.java | 2 +- .../server/persistence/FileTxnLog.java | 2 -- .../zookeeper/server/quorum/Follower.java | 2 ++ .../server/quorum/LearnerHandler.java | 2 +- .../zookeeper/server/quorum/Observer.java | 2 ++ .../quorum/auth/SaslQuorumAuthLearner.java | 10 ++-------- .../quorum/auth/SaslQuorumAuthServer.java | 2 +- .../zookeeper/server/upgrade/DataTreeV1.java | 17 ---------------- .../zookeeper/server/upgrade/UpgradeMain.java | 20 ++++++++++--------- src/java/test/config/findbugsExcludeFile.xml | 7 ++++++- 12 files changed, 43 insertions(+), 47 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java index 58497b76db1..825c22adafc 100644 --- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java @@ -505,6 +505,8 @@ protected void pRequest2Txn(int type, long zxid, Request request, Record record, version = currentVersion + 1; request.txn = new CheckVersionTxn(path, version); break; + default: + LOG.error("Invalid OpCode: {} received by PrepRequestProcessor", type); } } @@ -588,9 +590,7 @@ protected void pRequest(Request request) throws RequestProcessorException { try { pRequest2Txn(op.getType(), zxid, request, subrequest, false); } catch (KeeperException e) { - if (ke == null) { - ke = e; - } + ke = e; request.hdr.setType(OpCode.error); request.txn = new ErrorTxn(e.code().intValue()); LOG.info("Got user-level KeeperException when processing " @@ -641,6 +641,9 @@ protected void pRequest(Request request) throws RequestProcessorException { zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); break; + default: + LOG.warn("unknown type " + request.type); + break; } } catch (KeeperException e) { if (request.hdr != null) { diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java index d4effafb036..11112a4c3de 100644 --- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java @@ -133,11 +133,18 @@ public boolean accept(File f){ } } // add all non-excluded log files - List files = new ArrayList(Arrays.asList(txnLog - .getDataDir().listFiles(new MyFileFilter(PREFIX_LOG)))); + List files = new ArrayList(); + File[] fileArray = txnLog.getDataDir().listFiles(new MyFileFilter(PREFIX_LOG)); + if (fileArray != null) { + files.addAll(Arrays.asList(fileArray)); + } + // add all non-excluded snapshot files to the deletion list - files.addAll(Arrays.asList(txnLog.getSnapDir().listFiles( - new MyFileFilter(PREFIX_SNAPSHOT)))); + fileArray = txnLog.getSnapDir().listFiles(new MyFileFilter(PREFIX_SNAPSHOT)); + if (fileArray != null) { + files.addAll(Arrays.asList(fileArray)); + } + // remove the old files for(File f: files) { diff --git a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java index f622b2446b0..317b4acdf99 100644 --- a/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java +++ b/src/java/main/org/apache/zookeeper/server/SyncRequestProcessor.java @@ -140,7 +140,7 @@ public void run() { if (zks.getZKDatabase().append(si)) { logCount++; if (logCount > (snapCount / 2 + randRoll)) { - randRoll = r.nextInt(snapCount/2); + setRandRoll(r.nextInt(snapCount/2)); // roll the log zks.getZKDatabase().rollLog(); // take a snapshot diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index b1682c378d5..690cfca6aaa 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -637,8 +637,6 @@ public boolean next() throws IOException { crc.update(bytes, 0, bytes.length); if (crcValue != crc.getValue()) throw new IOException(CRC_ERROR); - if (bytes == null || bytes.length == 0) - return false; hdr = new TxnHeader(); record = SerializeUtils.deserializeTxn(bytes, hdr); } catch (EOFException e) { diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java index e439aaa60cd..27c33757b2c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Follower.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Follower.java @@ -136,6 +136,8 @@ protected void processPacket(QuorumPacket qp) throws IOException{ case Leader.SYNC: fzk.sync(); break; + default: + LOG.error("Invalid packet type: {} received by Observer", qp.getType()); } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 51ed7e7035c..4820490991f 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -272,7 +272,7 @@ static public String packetToString(QuorumPacket p) { type = "PROPOSAL"; TxnHeader hdr = new TxnHeader(); try { - txn = SerializeUtils.deserializeTxn(p.getData(), hdr); + SerializeUtils.deserializeTxn(p.getData(), hdr); // mess = "transaction: " + txn.toString(); } catch (IOException e) { LOG.warn("Unexpected exception",e); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java index 53f516fb2dc..ded4a24521c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Observer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Observer.java @@ -125,6 +125,8 @@ protected void processPacket(QuorumPacket qp) throws IOException{ ObserverZooKeeperServer obs = (ObserverZooKeeperServer)zk; obs.commitRequest(request); break; + default: + LOG.error("Invalid packet type: {} received by Observer", qp.getType()); } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java index 9a38bb88d8b..8c76d3e3726 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java @@ -173,14 +173,8 @@ private void send(DataOutputStream dout, byte[] response) BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); BinaryOutputArchive boa = BinaryOutputArchive .getArchive(bufferedOutput); - if (response == null) { - authPacket = QuorumAuth.createPacket( - QuorumAuth.Status.IN_PROGRESS, response); - } else { - authPacket = QuorumAuth.createPacket( - QuorumAuth.Status.IN_PROGRESS, response); - } - + authPacket = QuorumAuth.createPacket( + QuorumAuth.Status.IN_PROGRESS, response); boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); bufferedOutput.flush(); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java index a64513a9a9c..1aa2b329203 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java @@ -169,7 +169,7 @@ private void send(DataOutputStream dout, byte[] challenge, QuorumAuthPacket authPacket; if (challenge == null && s != QuorumAuth.Status.SUCCESS) { authPacket = QuorumAuth.createPacket( - QuorumAuth.Status.IN_PROGRESS, challenge); + QuorumAuth.Status.IN_PROGRESS, null); } else { authPacket = QuorumAuth.createPacket(s, challenge); } diff --git a/src/java/main/org/apache/zookeeper/server/upgrade/DataTreeV1.java b/src/java/main/org/apache/zookeeper/server/upgrade/DataTreeV1.java index e3d0633f604..678f4c6c900 100644 --- a/src/java/main/org/apache/zookeeper/server/upgrade/DataTreeV1.java +++ b/src/java/main/org/apache/zookeeper/server/upgrade/DataTreeV1.java @@ -347,14 +347,6 @@ static public class ProcessTxnResult { public long zxid; - public int err; - - public int type; - - public String path; - - public Stat stat; - /** * Equality is defined as the clientId and the cxid being the same. This * allows us to use hash tables to track completion of transactions. @@ -394,8 +386,6 @@ public ProcessTxnResult processTxn(TxnHeader header, Record txn) { rc.clientId = header.getClientId(); rc.cxid = header.getCxid(); rc.zxid = header.getZxid(); - rc.type = header.getType(); - rc.err = 0; if (rc.zxid > lastProcessedZxid) { lastProcessedZxid = rc.zxid; } @@ -406,7 +396,6 @@ public ProcessTxnResult processTxn(TxnHeader header, Record txn) { createNode(createTxn.getPath(), createTxn.getData(), createTxn .getAcl(), createTxn.getEphemeral() ? header .getClientId() : 0, header.getZxid(), header.getTime()); - rc.path = createTxn.getPath(); break; case OpCode.delete: DeleteTxn deleteTxn = (DeleteTxn) txn; @@ -416,22 +405,16 @@ public ProcessTxnResult processTxn(TxnHeader header, Record txn) { case OpCode.setData: SetDataTxn setDataTxn = (SetDataTxn) txn; debug = "Set data for transaction for " + setDataTxn.getPath(); - rc.stat = setData(setDataTxn.getPath(), setDataTxn.getData(), - setDataTxn.getVersion(), header.getZxid(), header - .getTime()); break; case OpCode.setACL: SetACLTxn setACLTxn = (SetACLTxn) txn; debug = "Set ACL for transaction for " + setACLTxn.getPath(); - rc.stat = setACL(setACLTxn.getPath(), setACLTxn.getAcl(), - setACLTxn.getVersion()); break; case OpCode.closeSession: killSession(header.getClientId()); break; case OpCode.error: ErrorTxn errTxn = (ErrorTxn) txn; - rc.err = errTxn.getErr(); break; } } catch (KeeperException e) { diff --git a/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java b/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java index 779c4818714..6648a1276f8 100644 --- a/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java +++ b/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java @@ -114,15 +114,17 @@ private void createAllDirs() throws IOException { */ void copyFiles(File srcDir, File dstDir, String filter) throws IOException { File[] list = srcDir.listFiles(); - for (File file: list) { - String name = file.getName(); - if (name.startsWith(filter)) { - // we need to copy this file - File dest = new File(dstDir, name); - LOG.info("Renaming " + file + " to " + dest); - if (!file.renameTo(dest)) { - throw new IOException("Unable to rename " - + file + " to " + dest); + if (list != null) { + for (File file: list) { + String name = file.getName(); + if (name.startsWith(filter)) { + // we need to copy this file + File dest = new File(dstDir, name); + LOG.info("Renaming " + file + " to " + dest); + if (!file.renameTo(dest)) { + throw new IOException("Unable to rename " + + file + " to " + dest); + } } } } diff --git a/src/java/test/config/findbugsExcludeFile.xml b/src/java/test/config/findbugsExcludeFile.xml index 7c6c5323e14..de8e0782e73 100644 --- a/src/java/test/config/findbugsExcludeFile.xml +++ b/src/java/test/config/findbugsExcludeFile.xml @@ -96,7 +96,12 @@ - + + + + + + From 2019d29f908ac61e0b4e10e329d47fe8da99c244 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Wed, 24 May 2017 03:47:24 -0700 Subject: [PATCH 414/444] ZOOKEEPER-2731: Cleanup findbug warnings in branch-3.4: Malicious code vulnerability Warnings There are two interesting parts to this change. The first is in the Jute compiler. Fields that are declared buffers (translated to byte[] in java) now perform a clone in the constructor and while "getting and setting", following best practice. This prevents accidental changes to arrays once passed into or out of jute records but may negatively impact memory usage and performance. Would be interested in hearing if people think this is acceptable. The second is in ZooDefs. We are currently declaring our predefined ACL lists with `new ArrayList(Collections.singletonList(new ACL(...`. This seems strange to me as we appear to be converting a List type to an ArrayList. Would be great if someone could shed some light on why we do this. I think this logic can be simplified to `Collections.singletonList(new ACL(...` with the added bonus that the resulting list is immutable (making FindBugs happy). Thanks, Abe Author: Abraham Fine Reviewers: Michael Han , Rakesh Radhakrishnan Closes #232 from afine/ZOOKEEPER-2731 --- src/java/main/org/apache/zookeeper/Environment.java | 2 +- src/java/main/org/apache/zookeeper/ZooKeeperMain.java | 2 +- .../main/org/apache/zookeeper/server/ServerCnxn.java | 2 +- src/java/test/config/findbugsExcludeFile.xml | 11 +++++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/Environment.java b/src/java/main/org/apache/zookeeper/Environment.java index 34cac4ca922..51797a18d77 100644 --- a/src/java/main/org/apache/zookeeper/Environment.java +++ b/src/java/main/org/apache/zookeeper/Environment.java @@ -31,7 +31,7 @@ * */ public class Environment { - public static String JAAS_CONF_KEY = "java.security.auth.login.config"; + public static final String JAAS_CONF_KEY = "java.security.auth.login.config"; public static class Entry { private String k; diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index 48aaa734146..74b05c262c9 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -50,7 +50,7 @@ */ public class ZooKeeperMain { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperMain.class); - protected static final Map commandMap = new HashMap( ); + static final Map commandMap = new HashMap( ); protected MyCommandOptions cl = new MyCommandOptions(); protected HashMap history = new HashMap( ); diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java index b554df96f2f..ad259e3fe5d 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java @@ -234,7 +234,7 @@ public String toString() { protected final static int isroCmd = ByteBuffer.wrap("isro".getBytes()) .getInt(); - protected final static Map cmd2String = new HashMap(); + final static Map cmd2String = new HashMap(); private static final String ZOOKEEPER_4LW_COMMANDS_WHITELIST = "zookeeper.4lw.commands.whitelist"; diff --git a/src/java/test/config/findbugsExcludeFile.xml b/src/java/test/config/findbugsExcludeFile.xml index de8e0782e73..b1a4f3dde5e 100644 --- a/src/java/test/config/findbugsExcludeFile.xml +++ b/src/java/test/config/findbugsExcludeFile.xml @@ -74,6 +74,11 @@ + + + + + @@ -136,4 +141,10 @@ + + + + + From 3a5381499761992e1023b27748b18e280d7ebac2 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Wed, 24 May 2017 14:33:40 -0700 Subject: [PATCH 415/444] ZOOKEEPER-2762: Cleanup findbug warnings in branch-3.4: Multithreaded correctness Warnings Author: Abraham Fine Reviewers: Rakesh Radhakrishnan Closes #239 from afine/ZOOKEEPER-2762 --- .../server/quorum/FastLeaderElection.java | 35 ++++++++++--------- .../zookeeper/server/quorum/Leader.java | 6 ++-- .../server/quorum/LearnerHandler.java | 6 ++-- .../server/quorum/QuorumCnxManager.java | 28 ++++----------- .../zookeeper/server/quorum/QuorumPeer.java | 5 +-- src/java/test/config/findbugsExcludeFile.xml | 3 ++ .../server/quorum/FLEDontCareTest.java | 2 +- 7 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java index 066f3858596..67e926730dd 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/FastLeaderElection.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.jmx.MBeanRegistry; @@ -261,7 +262,7 @@ public void run() { ToSend notmsg = new ToSend(ToSend.mType.notification, current.getId(), current.getZxid(), - logicalclock, + logicalclock.get(), self.getPeerState(), response.sid, current.getPeerEpoch()); @@ -348,12 +349,12 @@ public void run() { * lagging behind. */ if((ackstate == QuorumPeer.ServerState.LOOKING) - && (n.electionEpoch < logicalclock)){ + && (n.electionEpoch < logicalclock.get())){ Vote v = getVote(); ToSend notmsg = new ToSend(ToSend.mType.notification, v.getId(), v.getZxid(), - logicalclock, + logicalclock.get(), self.getPeerState(), response.sid, v.getPeerEpoch()); @@ -499,7 +500,7 @@ void halt(){ QuorumPeer self; Messenger messenger; - volatile long logicalclock; /* Election instance */ + AtomicLong logicalclock = new AtomicLong(); /* Election instance */ long proposedLeader; long proposedZxid; long proposedEpoch; @@ -509,7 +510,7 @@ void halt(){ * Returns the current vlue of the logical clock counter */ public long getLogicalClock(){ - return logicalclock; + return logicalclock.get(); } /** @@ -582,13 +583,13 @@ private void sendNotifications() { ToSend notmsg = new ToSend(ToSend.mType.notification, proposedLeader, proposedZxid, - logicalclock, + logicalclock.get(), QuorumPeer.ServerState.LOOKING, sid, proposedEpoch); if(LOG.isDebugEnabled()){ LOG.debug("Sending Notification: " + proposedLeader + " (n.leader), 0x" + - Long.toHexString(proposedZxid) + " (n.zxid), 0x" + Long.toHexString(logicalclock) + + Long.toHexString(proposedZxid) + " (n.zxid), 0x" + Long.toHexString(logicalclock.get()) + " (n.round), " + sid + " (recipient), " + self.getId() + " (myid), 0x" + Long.toHexString(proposedEpoch) + " (n.peerEpoch)"); } @@ -684,7 +685,7 @@ protected boolean checkLeader( if(leader != self.getId()){ if(votes.get(leader) == null) predicate = false; else if(votes.get(leader).getState() != ServerState.LEADING) predicate = false; - } else if(logicalclock != electionEpoch) { + } else if(logicalclock.get() != electionEpoch) { predicate = false; } @@ -812,7 +813,7 @@ public Vote lookForLeader() throws InterruptedException { int notTimeout = finalizeWait; synchronized(this){ - logicalclock++; + logicalclock.incrementAndGet(); updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch()); } @@ -860,8 +861,8 @@ else if(self.getVotingView().containsKey(n.sid)) { switch (n.state) { case LOOKING: // If notification > current, replace and send messages out - if (n.electionEpoch > logicalclock) { - logicalclock = n.electionEpoch; + if (n.electionEpoch > logicalclock.get()) { + logicalclock.set(n.electionEpoch); recvset.clear(); if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) { @@ -872,11 +873,11 @@ else if(self.getVotingView().containsKey(n.sid)) { getPeerEpoch()); } sendNotifications(); - } else if (n.electionEpoch < logicalclock) { + } else if (n.electionEpoch < logicalclock.get()) { if(LOG.isDebugEnabled()){ LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x" + Long.toHexString(n.electionEpoch) - + ", logicalclock=0x" + Long.toHexString(logicalclock)); + + ", logicalclock=0x" + Long.toHexString(logicalclock.get())); } break; } else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, @@ -896,7 +897,7 @@ else if(self.getVotingView().containsKey(n.sid)) { if (termPredicate(recvset, new Vote(proposedLeader, proposedZxid, - logicalclock, proposedEpoch))) { + logicalclock.get(), proposedEpoch))) { // Verify if there is any change in the proposed leader while((n = recvqueue.poll(finalizeWait, @@ -918,7 +919,7 @@ else if(self.getVotingView().containsKey(n.sid)) { Vote endVote = new Vote(proposedLeader, proposedZxid, - logicalclock, + logicalclock.get(), proposedEpoch); leaveInstance(endVote); return endVote; @@ -934,7 +935,7 @@ else if(self.getVotingView().containsKey(n.sid)) { * Consider all notifications from the same epoch * together. */ - if(n.electionEpoch == logicalclock){ + if(n.electionEpoch == logicalclock.get()){ recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, @@ -966,7 +967,7 @@ else if(self.getVotingView().containsKey(n.sid)) { if(ooePredicate(outofelection, outofelection, n)) { synchronized(this){ - logicalclock = n.electionEpoch; + logicalclock.set(n.electionEpoch); self.setPeerState((n.leader == self.getId()) ? ServerState.LEADING: learningState()); } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java index bd7bf35bf00..a9fd8d099a4 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Leader.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Leader.java @@ -376,7 +376,7 @@ void lead() throws IOException, InterruptedException { zk.registerJMX(new LeaderBean(this, zk), self.jmxLocalPeerBean); try { - self.tick = 0; + self.tick.set(0); zk.loadData(); leaderStateSummary = new StateSummary(self.getCurrentEpoch(), zk.getLastProcessedZxid()); @@ -424,7 +424,7 @@ void lead() throws IOException, InterruptedException { + "Perhaps the initTicks need to be increased."); } Thread.sleep(self.tickTime); - self.tick++; + self.tick.incrementAndGet(); return; } @@ -465,7 +465,7 @@ void lead() throws IOException, InterruptedException { while (true) { Thread.sleep(self.tickTime / 2); if (!tickSkip) { - self.tick++; + self.tick.incrementAndGet(); } HashSet syncedSet = new HashSet(); diff --git a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java index 4820490991f..884cc63d62c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/LearnerHandler.java @@ -314,7 +314,7 @@ static public String packetToString(QuorumPacket p) { public void run() { try { leader.addLearnerHandler(this); - tickOfNextAckDeadline = leader.self.tick + tickOfNextAckDeadline = leader.self.tick.get() + leader.self.initLimit + leader.self.syncLimit; ia = BinaryInputArchive.getArchive(bufferedInput); @@ -565,7 +565,7 @@ public void run() { if (LOG.isTraceEnabled()) { ZooTrace.logQuorumPacket(LOG, traceMask, 'i', qp); } - tickOfNextAckDeadline = leader.self.tick + leader.self.syncLimit; + tickOfNextAckDeadline = leader.self.tick.get() + leader.self.syncLimit; ByteBuffer bb; @@ -710,6 +710,6 @@ void queuePacket(QuorumPacket p) { public boolean synced() { return isAlive() - && leader.self.tick <= tickOfNextAckDeadline; + && leader.self.tick.get() <= tickOfNextAckDeadline; } } diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 2b131c4e588..cc45562926c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -363,10 +363,7 @@ private boolean startConnection(Socket sock, Long sid) vsw.finish(); senderWorkerMap.put(sid, sw); - if (!queueSendMap.containsKey(sid)) { - queueSendMap.put(sid, new ArrayBlockingQueue( - SEND_CAPACITY)); - } + queueSendMap.putIfAbsent(sid, new ArrayBlockingQueue(SEND_CAPACITY)); sw.start(); rw.start(); @@ -504,11 +501,7 @@ private void handleConnection(Socket sock, DataInputStream din) vsw.finish(); senderWorkerMap.put(sid, sw); - - if (!queueSendMap.containsKey(sid)) { - queueSendMap.put(sid, new ArrayBlockingQueue( - SEND_CAPACITY)); - } + queueSendMap.putIfAbsent(sid, new ArrayBlockingQueue(SEND_CAPACITY)); sw.start(); rw.start(); @@ -535,19 +528,12 @@ public void toSend(Long sid, ByteBuffer b) { /* * Start a new connection if doesn't have one already. */ - if (!queueSendMap.containsKey(sid)) { - ArrayBlockingQueue bq = new ArrayBlockingQueue( - SEND_CAPACITY); - queueSendMap.put(sid, bq); - addToSendQueue(bq, b); - + ArrayBlockingQueue bq = new ArrayBlockingQueue(SEND_CAPACITY); + ArrayBlockingQueue bqExisting = queueSendMap.putIfAbsent(sid, bq); + if (bqExisting != null) { + addToSendQueue(bqExisting, b); } else { - ArrayBlockingQueue bq = queueSendMap.get(sid); - if(bq != null){ - addToSendQueue(bq, b); - } else { - LOG.error("No queue for server " + sid); - } + addToSendQueue(bq, b); } connectOne(sid); 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 9eeeb5d3ee1..05d0a1b5831 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import javax.security.sasl.SaslException; @@ -347,7 +348,7 @@ synchronized void setBCVote(Vote v) { /** * The current tick */ - protected volatile int tick; + protected AtomicInteger tick = new AtomicInteger(); /** * Whether or not to listen on all IPs for the two quorum ports @@ -1165,7 +1166,7 @@ public void setInitLimit(int initLimit) { * Get the current tick */ public int getTick() { - return tick; + return tick.get(); } /** diff --git a/src/java/test/config/findbugsExcludeFile.xml b/src/java/test/config/findbugsExcludeFile.xml index b1a4f3dde5e..f19bec20098 100644 --- a/src/java/test/config/findbugsExcludeFile.xml +++ b/src/java/test/config/findbugsExcludeFile.xml @@ -110,6 +110,9 @@ + + + diff --git a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java index 3d4a02c2bad..ffc7ab10dc7 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/FLEDontCareTest.java @@ -236,7 +236,7 @@ public void testOutofElection() { * fle represents the FLE instance of server 3.Here we set * its logical clock to 1. */ - fle.logicalclock = 0x1; + fle.logicalclock.set(0x1); /* From 159ead6f1d503c635957b7b92d122043321d8451 Mon Sep 17 00:00:00 2001 From: Jiang Jiafu Date: Fri, 26 May 2017 15:29:16 -0700 Subject: [PATCH 416/444] =?UTF-8?q?ZOOKEEPER-2691:=20recreateSocketAddress?= =?UTF-8?q?es=20may=20recreate=20the=20unreachable=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …IP address Author: Jiang Jiafu Author: JiangJiafu Reviewers: Michael Han , Abraham Fine , Edward Ribeiro Closes #173 from JiangJiafu/ZOOKEEPER-2691 --- .../content/xdocs/zookeeperAdmin.xml | 20 ++++++ .../zookeeper/server/quorum/QuorumPeer.java | 63 ++++++++++++++++--- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 27060e09a57..1faa77365f9 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1076,6 +1076,26 @@ server.3=zoo3:2888:3888 + + zookeeper.ipReachableTimeout + + + (Java system property: zookeeper.ipReachableTimeout) + + New in 3.4.11: + Set this timeout value for IP addresses reachable checking when hostname is resolved, as mesured in + milliseconds. + By default, ZooKeeper will use the first IP address of the hostname(without any reachable checking). + When zookeeper.ipReachableTimeout is set(larger than 0), ZooKeeper will will try to pick up the first + IP address which is reachable. This is done by calling Java API InetAddress.isReachable(long timeout) + function, in which this timeout value is used. If none of such reachable IP address can be found, the + first IP address of the hostname will be used anyway. + + + + + 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 05d0a1b5831..53f607750f5 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -138,19 +138,19 @@ private QuorumServer(long id, InetSocketAddress addr, public QuorumServer(long id, String hostname, Integer port, Integer electionPort, LearnerType type) { - this.id = id; - this.hostname=hostname; - if (port!=null){ + this.id = id; + this.hostname=hostname; + if (port!=null){ this.port=port; - } - if (electionPort!=null){ + } + if (electionPort!=null){ this.electionPort=electionPort; - } - if (type!=null){ + } + if (type!=null){ this.type = type; - } - this.recreateSocketAddresses(); } + this.recreateSocketAddresses(); + } /** * Performs a DNS lookup of hostname and (re)creates the this.addr and @@ -164,7 +164,23 @@ public QuorumServer(long id, String hostname, public void recreateSocketAddresses() { InetAddress address = null; try { - address = InetAddress.getByName(this.hostname); + // the time, in milliseconds, before {@link InetAddress#isReachable} aborts + // in {@link #getReachableAddress}. + int ipReachableTimeout = 0; + String ipReachableValue = System.getProperty("zookeeper.ipReachableTimeout"); + if (ipReachableValue != null) { + try { + ipReachableTimeout = Integer.parseInt(ipReachableValue); + } catch (NumberFormatException e) { + LOG.error("{} is not a valid number", ipReachableValue); + } + } + // zookeeper.ipReachableTimeout is not defined + if (ipReachableTimeout <= 0) { + address = InetAddress.getByName(this.hostname); + } else { + address = getReachableAddress(this.hostname, ipReachableTimeout); + } LOG.info("Resolved hostname: {} to address: {}", this.hostname, address); this.addr = new InetSocketAddress(address, this.port); if (this.electionPort > 0){ @@ -186,6 +202,33 @@ public void recreateSocketAddresses() { } } + /** + * Resolve the hostname to IP addresses, and find one reachable address. + * + * @param hostname the name of the host + * @param timeout the time, in milliseconds, before {@link InetAddress#isReachable} + * aborts + * @return a reachable IP address. If no such IP address can be found, + * just return the first IP address of the hostname. + * + * @exception UnknownHostException + */ + public InetAddress getReachableAddress(String hostname, int timeout) + throws UnknownHostException { + InetAddress[] addresses = InetAddress.getAllByName(hostname); + for (InetAddress a : addresses) { + try { + if (a.isReachable(timeout)) { + return a; + } + } catch (IOException e) { + LOG.warn("IP address {} is unreachable", a); + } + } + // All the IP addresses are unreachable, just return the first one. + return addresses[0]; + } + public InetSocketAddress addr; public InetSocketAddress electionAddr; From 3289ebbaa48d85ceb9dc5154f5547f37cf7d300c Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Wed, 31 May 2017 20:55:57 -0700 Subject: [PATCH 417/444] ZOOKEEPER-2788: remove unused variable MAX_CONNECTION_ATTEMPTS. Author: Abraham Fine Reviewers: Michael Han Closes #268 from afine/ZOOKEEPER-2788_3.4 --- .../apache/zookeeper/server/quorum/QuorumCnxManager.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index cc45562926c..878008b64cc 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -82,12 +82,7 @@ public class QuorumCnxManager { // stale notifications to peers static final int SEND_CAPACITY = 1; - static final int PACKETMAXSIZE = 1024 * 512; - /* - * Maximum number of attempts to connect to a peer - */ - - static final int MAX_CONNECTION_ATTEMPTS = 2; + static final int PACKETMAXSIZE = 1024 * 512; /* * Max buffer size to be read from the network. From 51cdeb407cfb7887e647ba7d34718232e6108409 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 8 Jun 2017 08:42:19 -0700 Subject: [PATCH 418/444] ZOOKEEPER-1748: Add option for tcp keepalive As referenced in https://issues.apache.org/jira/browse/ZOOKEEPER-1748 and https://github.com/apache/zookeeper/pull/83, add the option to use keepalived on quorum connections. These connections are often idle and long-lived, thus tend to be silently dropped by intermediate networking infrastructure (AWS Security Groups' state tables, for example). This PR adds the option to use the system's keepalive functionality when creating the socket for quorum connections. It does not change existing behavior. Author: Ben Author: Ben Sherman Reviewers: Michael Han , Abe Fine Closes #274 from bensherman/ZOOKEEPER-1748 --- docs/zookeeperAdmin.html | 17 ++++++++++++++++- .../server/quorum/QuorumCnxManager.java | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html index 140c997156b..f074a35d6e5 100644 --- a/docs/zookeeperAdmin.html +++ b/docs/zookeeperAdmin.html @@ -1446,7 +1446,22 @@

            Cluster Options

            - +
            +tcpKeepAlive +
            +
            +

            (Java system property: zookeeper.tcpKeepAlive)

            +

            +New in 3.4.11: + Sets the keepAlive flag on the sockets used by quorum members to perform elections. + This will allow for connections between quorum members to remain up when there is + network infrastructure that may otherwise terminate them. Some NATs and firewalls may + terminate or lose state for long running or idle connections.

            + Enabling this option relies on OS level settings to work properly, check your operating + system's options regarding TCP keepalive for more information. + Defaults to false.

            +
            +

            diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java index 878008b64cc..ec6be4a2a88 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumCnxManager.java @@ -107,6 +107,7 @@ public class QuorumCnxManager { final long mySid; final int socketTimeout; final Map view; + final boolean tcpKeepAlive = Boolean.getBoolean("zookeeper.tcpKeepAlive"); final boolean listenOnAllIPs; private ThreadPoolExecutor connectionExecutor; private final Set inprogressConnections = Collections @@ -661,6 +662,7 @@ public void softHalt() { */ private void setSockOpts(Socket sock) throws SocketException { sock.setTcpNoDelay(true); + sock.setKeepAlive(tcpKeepAlive); sock.setSoTimeout(socketTimeout); } From 06889c82fdf2093aba800b31f89628fbfd0c08a5 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 8 Jun 2017 08:53:13 -0700 Subject: [PATCH 419/444] ZOOKEEPER-2798: Fix flaky test: org.apache.zookeeper.test.ReadOnlyModeTest.testConnectionEvents When a client connection occurs, the `ClientCnxn`'s `state` is changed and then a watcher event is queued. This test is unreliable because it expects the watcher to fire immediately after the state changes. This test adds a `CountDownLatch` to the watcher, to better track when it has been fired. This flaky test can be reliably reproduced by adding a `Thread.sleep` right before `states.add(event.getState());`. Author: Abraham Fine Reviewers: Michael Han Closes #270 from afine/ZOOKEEPER-2798 --- .../org/apache/zookeeper/test/ClientBase.java | 19 +++++++++++++++++++ .../zookeeper/test/ReadOnlyModeTest.java | 14 ++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java index f2ad4d51e70..7595a029bb9 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientBase.java +++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java @@ -102,6 +102,8 @@ public static class CountdownWatcher implements Watcher { volatile boolean connected; // Set to true when connected to a quorum server. volatile boolean syncConnected; + // Set to true when connected to a quorum server in read-only mode + volatile boolean readOnlyConnected; public CountdownWatcher() { reset(); @@ -110,18 +112,22 @@ synchronized public void reset() { clientConnected = new CountDownLatch(1); connected = false; syncConnected = false; + readOnlyConnected = false; } synchronized public void process(WatchedEvent event) { KeeperState state = event.getState(); if (state == KeeperState.SyncConnected) { connected = true; syncConnected = true; + readOnlyConnected = false; } else if (state == KeeperState.ConnectedReadOnly) { connected = true; syncConnected = false; + readOnlyConnected = true; } else { connected = false; syncConnected = false; + readOnlyConnected = false; } notifyAll(); @@ -159,6 +165,19 @@ synchronized public void waitForSyncConnected(long timeout) throw new TimeoutException("Failed to connect to read-write ZooKeeper server."); } } + synchronized public void waitForReadOnlyConnected(long timeout) + throws InterruptedException, TimeoutException + { + long expire = System.currentTimeMillis() + timeout; + long left = timeout; + while(!readOnlyConnected && left > 0) { + wait(left); + left = expire - System.currentTimeMillis(); + } + if (!readOnlyConnected) { + throw new TimeoutException("Failed to connect in read-only mode to ZooKeeper server."); + } + } synchronized public void waitForDisconnected(long timeout) throws InterruptedException, TimeoutException { diff --git a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java index cc266d1f320..a99e4aead8e 100644 --- a/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java +++ b/src/java/test/org/apache/zookeeper/test/ReadOnlyModeTest.java @@ -156,13 +156,9 @@ public void testReadOnlyClient() throws Exception { */ @Test(timeout = 90000) public void testConnectionEvents() throws Exception { - final List states = new ArrayList(); + CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, - new Watcher() { - public void process(WatchedEvent event) { - states.add(event.getState()); - } - }, true); + watcher, true); boolean success = false; for (int i = 0; i < 30; i++) { try { @@ -175,6 +171,7 @@ public void process(WatchedEvent event) { } } Assert.assertTrue("Did not succeed in connecting in 30s", success); + Assert.assertFalse("The connection should not be read-only yet", watcher.readOnlyConnected); // kill peer and wait no more than 5 seconds for read-only server // to be started (which should take one tickTime (2 seconds)) @@ -187,10 +184,7 @@ public void process(WatchedEvent event) { Time.currentElapsedTime() - start < 30000); } - // At this point states list should contain, in the given order, - // SyncConnected, Disconnected, and ConnectedReadOnly states - Assert.assertTrue("ConnectedReadOnly event wasn't received", states - .get(2) == KeeperState.ConnectedReadOnly); + watcher.waitForReadOnlyConnected(5000); zk.close(); } From ca4a91dc7ef01c34e3849d99fa3764003fc89715 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Fri, 9 Jun 2017 09:35:13 -0700 Subject: [PATCH 420/444] fix documentation source for ZOOKEEPER-1748. As referenced in https://issues.apache.org/jira/browse/ZOOKEEPER-1748 and https://github.com/apache/zookeeper/pull/83, add the option to use keepalived on quorum connections. These connections are often idle and long-lived, thus tend to be silently dropped by intermediate networking infrastructure (AWS Security Groups' state tables, for example). This PR adds the option to use the system's keepalive functionality when creating the socket for quorum connections. It does not change existing behavior. Author: Ben Sherman Author: Ben Reviewers: Michael Han Closes #274 from bensherman/ZOOKEEPER-1748 --- .../content/xdocs/zookeeperAdmin.xml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 1faa77365f9..f0cecfc653d 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1096,6 +1096,29 @@ server.3=zoo3:2888:3888
            + + tcpKeepAlive + + + (Java system property: zookeeper.tcpKeepAlive) + + New in 3.4.11: + Setting this to true sets the TCP keepAlive flag on the + sockets used by quorum members to perform elections. + This will allow for connections between quorum members to + remain up when there is network infrastructure that may + otherwise break them. Some NATs and firewalls may terminate + or lose state for long running or idle connections. + + Enabling this option relies on OS level settings to work + properly, check your operating system's options regarding TCP + keepalive for more information. Defaults to + false. + + + + From 411293a0b1167d0e5b6763f952e7bb11b4946285 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Fri, 9 Jun 2017 10:57:27 -0700 Subject: [PATCH 421/444] Fix a typo in doc source in commit ca4a91dc7ef01c34e3849d99fa3764003fc89715 that breaks the build. --- src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index f0cecfc653d..3c8c86c8ad8 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1101,7 +1101,7 @@ server.3=zoo3:2888:3888 (Java system property: zookeeper.tcpKeepAlive) + role="bold">zookeeper.tcpKeepAlive) New in 3.4.11: Setting this to true sets the TCP keepAlive flag on the From ac5fa3f54fbf47437ed86bd02cd0e9fdb9edf5e6 Mon Sep 17 00:00:00 2001 From: Mohammad Arshad Date: Mon, 12 Jun 2017 16:18:05 -0700 Subject: [PATCH 422/444] ZOOKEEPER-2775: ZK Client not able to connect with Xid out of order error Same as PR https://github.com/apache/zookeeper/pull/254 but this is for branch-3.4 Author: Mohammad Arshad Reviewers: Michael Han Closes #278 from arshadmohammad/ZOOKEEPER-2775-branch-3.4 --- .../main/org/apache/zookeeper/ClientCnxn.java | 2 + .../zookeeper/{test => }/SaslAuthTest.java | 115 ++++++++++++++---- 2 files changed, 93 insertions(+), 24 deletions(-) rename src/java/test/org/apache/zookeeper/{test => }/SaslAuthTest.java (52%) diff --git a/src/java/main/org/apache/zookeeper/ClientCnxn.java b/src/java/main/org/apache/zookeeper/ClientCnxn.java index 3cf2f755044..94d2f577781 100644 --- a/src/java/main/org/apache/zookeeper/ClientCnxn.java +++ b/src/java/main/org/apache/zookeeper/ClientCnxn.java @@ -988,6 +988,8 @@ private void sendPing() { private boolean saslLoginFailed = false; private void startConnect() throws IOException { + // initializing it for new connection + saslLoginFailed = false; state = States.CONNECTING; InetSocketAddress addr; diff --git a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java b/src/java/test/org/apache/zookeeper/SaslAuthTest.java similarity index 52% rename from src/java/test/org/apache/zookeeper/test/SaslAuthTest.java rename to src/java/test/org/apache/zookeeper/SaslAuthTest.java index 6e75998b1c8..eac070335ec 100644 --- a/src/java/test/org/apache/zookeeper/test/SaslAuthTest.java +++ b/src/java/test/org/apache/zookeeper/SaslAuthTest.java @@ -16,54 +16,78 @@ * limitations under the License. */ -package org.apache.zookeeper.test; +package org.apache.zookeeper; + +import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.TestableZooKeeper; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.ClientCnxn.SendThread; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; +import org.apache.zookeeper.test.ClientBase; +import org.junit.AfterClass; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; public class SaslAuthTest extends ClientBase { - static { - System.setProperty("zookeeper.authProvider.1","org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); - + @BeforeClass + public static void init() { + System.setProperty("zookeeper.authProvider.1", + "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); try { File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); + String jaasContent = getJaasFileContent(); FileWriter fwriter = new FileWriter(saslConfFile); - - fwriter.write("" + - "Server {\n" + - " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + - " user_super=\"test\";\n" + - "};\n" + - "Client {\n" + - " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + - " username=\"super\"\n" + - " password=\"test\";\n" + - "};" + "\n"); + fwriter.write(jaasContent); fwriter.close(); - System.setProperty("java.security.auth.login.config",saslConfFile.getAbsolutePath()); - } - catch (IOException e) { - // could not create tmp directory to hold JAAS conf file : test will fail now. + System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); + } catch (IOException e) { + // could not create tmp directory to hold JAAS conf file : test will + // fail now. } } + private static String getJaasFileContent() { + StringBuilder jaasContent=new StringBuilder(); + String newLine = System.getProperty("line.separator"); + jaasContent.append("Server {"); + jaasContent.append(newLine); + jaasContent.append("org.apache.zookeeper.server.auth.DigestLoginModule required"); + jaasContent.append(newLine); + jaasContent.append("user_super=\"test\";"); + jaasContent.append(newLine); + jaasContent.append("};"); + jaasContent.append(newLine); + jaasContent.append("Client {"); + jaasContent.append(newLine); + jaasContent.append("org.apache.zookeeper.server.auth.DigestLoginModule required"); + jaasContent.append(newLine); + jaasContent.append("username=\"super\""); + jaasContent.append(newLine); + jaasContent.append("password=\"test\";"); + jaasContent.append(newLine); + jaasContent.append("};"); + jaasContent.append(newLine); + return jaasContent.toString(); + } + + @AfterClass + public static void clean() { + System.clearProperty("zookeeper.authProvider.1"); + System.clearProperty("java.security.auth.login.config"); + } + private AtomicInteger authFailed = new AtomicInteger(0); @Override @@ -142,5 +166,48 @@ public void testInvalidSaslIds() throws Exception { } } } + + @Test + public void testZKOperationsAfterClientSaslAuthFailure() throws Exception { + CountdownWatcher watcher = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); + watcher.waitForConnected(CONNECTION_TIMEOUT); + try { + setSaslFailureFlag(zk); + + // try node creation for around 15 second, + int totalTry = 10; + int tryCount = 0; + + boolean success = false; + while (!success && tryCount++ <= totalTry) { + try { + zk.create("/saslAuthFail", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT_SEQUENTIAL); + success = true; + } catch (KeeperException.ConnectionLossException e) { + Thread.sleep(1000); + // do nothing + } + } + assertTrue("ZNode creation is failing continuously after Sasl auth failure.", success); + + } finally { + zk.close(); + } + } + + // set saslLoginFailed to true to simulate the LoginException + private void setSaslFailureFlag(ZooKeeper zk) throws Exception { + Field cnxnField = zk.getClass().getDeclaredField("cnxn"); + cnxnField.setAccessible(true); + ClientCnxn clientCnxn = (ClientCnxn) cnxnField.get(zk); + Field sendThreadField = clientCnxn.getClass().getDeclaredField("sendThread"); + sendThreadField.setAccessible(true); + SendThread sendThread = (SendThread) sendThreadField.get(clientCnxn); + Field saslLoginFailedField = sendThread.getClass().getDeclaredField("saslLoginFailed"); + saslLoginFailedField.setAccessible(true); + saslLoginFailedField.setBoolean(sendThread, true); + } } From 83e0ab896991dc89af22e4a3b82af47f208d7f26 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 15 Jun 2017 15:35:58 -0700 Subject: [PATCH 423/444] ZOOKEEPER-2786: Flaky test: org.apache.zookeeper.test.ClientTest.testNonExistingOpCode On branch 3.4 we attempt to check that an invalid opcode in a request causes the server to disconnect the client with: ``` try { zk.exists("/m1", false); fail("The connection should have been closed"); } catch (KeeperException.ConnectionLossException expected) { } ``` This can run into a race with the reconnection logic in `ClientCnxn.java`https://github.com/apache/zookeeper/blob/branch-3.4/src/java/main/org/apache/zookeeper/ClientCnxn.java#L1052 We should use a watcher instead to track disconnects. Author: Abraham Fine Reviewers: Michael Han Closes #260 from afine/ZOOKEEPER-2786 --- .../test/org/apache/zookeeper/test/ClientTest.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/test/ClientTest.java b/src/java/test/org/apache/zookeeper/test/ClientTest.java index b2a1394f916..7b2d19a45e4 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientTest.java @@ -774,7 +774,8 @@ public void testClientCleanup() throws Throwable { */ @Test public void testNonExistingOpCode() throws Exception { - TestableZooKeeper zk = createClient(); + CountdownWatcher watcher = new CountdownWatcher(); + TestableZooKeeper zk = createClient(watcher); final String path = "/m1"; @@ -785,13 +786,9 @@ public void testNonExistingOpCode() throws Exception { request.setWatch(false); ExistsResponse response = new ExistsResponse(); ReplyHeader r = zk.submitRequest(h, request, response, null); - Assert.assertEquals(r.getErr(), Code.UNIMPLEMENTED.intValue()); - try { - zk.exists("/m1", false); - fail("The connection should have been closed"); - } catch (KeeperException.ConnectionLossException expected) { - } + // Sending a nonexisting opcode should cause the server to disconnect + watcher.waitForDisconnected(5000); } } From b3e514659393837a6e824038463c0a98efff2fdb Mon Sep 17 00:00:00 2001 From: Janek P Date: Sun, 18 Jun 2017 21:58:42 -0700 Subject: [PATCH 424/444] ZOOKEEPER-2730: Cleanup findbug warnings in branch-3.4: Disable Internationalization Warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …stematic manner Author: gosubpl Reviewers: Michael Han , Rakesh Radhakrishnan Closes #285 from gosubpl/ZOOKEEPER-2730 --- src/java/test/config/findbugsExcludeFile.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/java/test/config/findbugsExcludeFile.xml b/src/java/test/config/findbugsExcludeFile.xml index f19bec20098..f1bb79db0fa 100644 --- a/src/java/test/config/findbugsExcludeFile.xml +++ b/src/java/test/config/findbugsExcludeFile.xml @@ -150,4 +150,12 @@ + + + + + + From 677b5ccb7ce214eee750fda70eba3643d8c7a6c0 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Tue, 20 Jun 2017 09:10:04 -0700 Subject: [PATCH 425/444] ZOOKEEPER-2691: fix documentation issue. The configuration option should not have zookeeper as its prefix. --- src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml index 3c8c86c8ad8..cc5da1368d2 100644 --- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml +++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml @@ -1077,7 +1077,7 @@ server.3=zoo3:2888:3888 - zookeeper.ipReachableTimeout + ipReachableTimeout (Java system property: Date: Fri, 30 Jun 2017 13:45:30 -0700 Subject: [PATCH 426/444] ZOOKEEPER-2786: Flaky test: org.apache.zookeeper.test.ClientTest.testNonExistingOpCode See https://github.com/apache/zookeeper/pull/286 for description. Author: Abraham Fine Reviewers: Michael Han Closes #287 from afine/ZOOKEEPER-2786_3.4_fix --- .../org/apache/zookeeper/test/ClientTest.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/java/test/org/apache/zookeeper/test/ClientTest.java b/src/java/test/org/apache/zookeeper/test/ClientTest.java index 7b2d19a45e4..9dc48619e26 100644 --- a/src/java/test/org/apache/zookeeper/test/ClientTest.java +++ b/src/java/test/org/apache/zookeeper/test/ClientTest.java @@ -18,12 +18,11 @@ package org.apache.zookeeper.test; -import static org.junit.Assert.fail; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -33,6 +32,7 @@ import org.apache.zookeeper.KeeperException.InvalidACLException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; @@ -774,8 +774,16 @@ public void testClientCleanup() throws Throwable { */ @Test public void testNonExistingOpCode() throws Exception { - CountdownWatcher watcher = new CountdownWatcher(); - TestableZooKeeper zk = createClient(watcher); + final CountDownLatch clientDisconnected = new CountDownLatch(1); + Watcher watcher = new Watcher() { + @Override + public synchronized void process(WatchedEvent event) { + if (event.getState() == KeeperState.Disconnected) { + clientDisconnected.countDown(); + } + } + }; + TestableZooKeeper zk = new TestableZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); final String path = "/m1"; @@ -785,10 +793,13 @@ public void testNonExistingOpCode() throws Exception { request.setPath(path); request.setWatch(false); ExistsResponse response = new ExistsResponse(); + ReplyHeader r = zk.submitRequest(h, request, response, null); + Assert.assertEquals(r.getErr(), Code.UNIMPLEMENTED.intValue()); // Sending a nonexisting opcode should cause the server to disconnect - watcher.waitForDisconnected(5000); + Assert.assertTrue("failed to disconnect", + clientDisconnected.await(5000, TimeUnit.MILLISECONDS)); } } From 4eab48f4970d2a98594c95add6106780e5b2ea6f Mon Sep 17 00:00:00 2001 From: b00902108 Date: Mon, 3 Jul 2017 22:56:09 -0700 Subject: [PATCH 427/444] ZOOKEEPER-2818: Improve the ZooKeeper#setACL java doc Author: b00902108 Reviewers: Michael Han Closes #291 from brahmareddybattula/ZOOKEEPER-2818 (cherry picked from commit 7db83eb9dcbd26b79f852e93520db821dabff047) Signed-off-by: Michael Han --- .../org/apache/zookeeper/retry/ZooKeeperRetry.java | 4 ++-- src/java/main/org/apache/zookeeper/ZooKeeper.java | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java index feb4301245f..ce959a1a0c2 100644 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java +++ b/src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java @@ -217,12 +217,12 @@ public byte[] getData(String path, Watcher watcher, Stat stat) } @Override - public Stat setACL(String path, List acl, int version) + public Stat setACL(String path, List acl, int aclVersion) throws KeeperException, InterruptedException { int count = 0; do { try { - return super.setACL(path, acl, version); + return super.setACL(path, acl, aclVersion); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index ef5cd7dcefa..e6e464dd08a 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -1418,25 +1418,25 @@ public void getACL(final String path, Stat stat, ACLCallback cb, /** * Set the ACL for the node of the given path if such a node exists and the - * given version matches the version of the node. Return the stat of the + * given aclVersion matches the acl version of the node. Return the stat of the * node. *

            * A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. *

            * A KeeperException with error code KeeperException.BadVersion will be - * thrown if the given version does not match the node's version. + * thrown if the given aclVersion does not match the node's aclVersion. * - * @param path - * @param acl - * @param version + * @param path the given path for the node + * @param acl the given acl for the node + * @param aclVersion the given acl version of the node * @return the stat of the node. * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. * @throws org.apache.zookeeper.KeeperException.InvalidACLException If the acl is invalide. * @throws IllegalArgumentException if an invalid path is specified */ - public Stat setACL(final String path, List acl, int version) + public Stat setACL(final String path, List acl, int aclVersion) throws KeeperException, InterruptedException { final String clientPath = path; @@ -1452,7 +1452,7 @@ public Stat setACL(final String path, List acl, int version) throw new KeeperException.InvalidACLException(clientPath); } request.setAcl(acl); - request.setVersion(version); + request.setVersion(aclVersion); SetACLResponse response = new SetACLResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { From 3872e49f6fa6835e0fca2229bec41d1b3cad4553 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Thu, 6 Jul 2017 09:53:21 -0700 Subject: [PATCH 428/444] ZOOKEEPER-2811: PurgeTxnLog#validateAndGetFile: return tag has no argument. With this fix branch-3.4 pre-commit should generate a green build when all unit tests pass. Author: Michael Han Closes #305 from hanm/ZOOKEEPER-2811 --- src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java index 11112a4c3de..bb972c88490 100644 --- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java @@ -209,7 +209,7 @@ private static File validateAndGetFile(String path) { * error and usage and then exits * * @param number - * @return + * @return count */ private static int validateAndGetCount(String number) { int result = 0; From f8b5be0af45d2127b096ed41c11d3c0d7244df8e Mon Sep 17 00:00:00 2001 From: Michael Han Date: Mon, 27 Mar 2017 10:31:44 -0700 Subject: [PATCH 429/444] ZOOKEEPER-2740: close netty connection when exceptions occur during write to channel to prevent resource leak. This bug happily hides in code base for 7 years until it's revealed from a failed unit tests. This commit is cherry picked from ZOOKEEPER-2737 / 5c356f5a47402c000b5e206a536273afc75de883. Author: Michael Han Reviewers: Brian Nixon , Abraham Fine , Rakesh Radhakrishnan , Patrick Hunt Closes #207 from hanm/ZOOKEEPER-2737 --- .../org/apache/zookeeper/server/NettyServerCnxnFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index 2087eaa96e9..2d3c93fd9a2 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -113,8 +113,8 @@ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) if (cnxn != null) { if (LOG.isDebugEnabled()) { LOG.debug("Closing " + cnxn); - cnxn.close(); } + cnxn.close(); } } From 90652669bece7bcde11e16df3f5722d80b3c5b31 Mon Sep 17 00:00:00 2001 From: panyuxuan Date: Wed, 26 Jul 2017 17:24:20 -0700 Subject: [PATCH 430/444] ZOOKEEPER-2856: ZooKeeperSaslClient#respondToServer should log exception See https://issues.apache.org/jira/browse/ZOOKEEPER-2856 for details. When upstream like HBase call ZooKeeperSaslClient with security enabled, we sometimes get error in HBase logs like: `SASL authentication failed using login context 'Client'.` This error occures when getting SaslException in ZooKeeperSaslClient#respondToServer : `catch (SaslException e) {` `LOG.error("SASL authentication failed using login context '" +` ` this.getLoginContext() + "'.");` `saslState = SaslState.FAILED;` `gotLastPacket = true;` ` }` This error makes user confused without explicit exception message. So I think we can add exception message to the log. The patch uses parameterized logging to add the exception message to the log. Author: panyuxuan Reviewers: Michael Han Closes #318 from pyx1990/ZOOKEEPER-2856 (cherry picked from commit 41b30a74ec8b33255e99d97a102de53d315c28b3) Signed-off-by: Michael Han --- .../main/org/apache/zookeeper/client/ZooKeeperSaslClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java index f3c9d3c5453..af3303c6081 100644 --- a/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java +++ b/src/java/main/org/apache/zookeeper/client/ZooKeeperSaslClient.java @@ -245,7 +245,7 @@ public void respondToServer(byte[] serverToken, ClientCnxn cnxn) { } } catch (SaslException e) { LOG.error("SASL authentication failed using login context '" + - this.getLoginContext() + "'."); + this.getLoginContext() + "' with exception: {}", e); saslState = SaslState.FAILED; gotLastPacket = true; } From 76553b0ca69dbd9439b46ec318d1b04bc0e14f1f Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 27 Jul 2017 15:37:57 -0700 Subject: [PATCH 431/444] ZOOKEEPER-2829: Interface usability / compatibility improvements through Java annotation. This patch uses Apache Yetus audience annotations to label our publicly available interfaces and then generate our javadoc based on the annotations. The javadoc generated by this patch should be identical to our javadoc before with a few extra classes (that I think should have been included before anyway). HostProvider Record StaticHostProvider Transaction The "gotcha" with this patch is the way that java classes generated by jute are handled. There are four of these classes that need to be publicly documented: ACL, Id, Stat, StatPersisted (in addition to their superclass Record). I thought it would be safest to have the jute compiler always label these as "Public" and then we can filter out the ones we don't want in the javadoc ant task (by excluding the org.apache.zookeeper.server package and then pulling in the tools classes separately). See https://github.com/apache/zookeeper/pull/316 Author: Abraham Fine Reviewers: Michael Han Closes #317 from afine/ZOOKEEPER-2829_3.4 --- build.xml | 37 ++++++++++--------- ivy.xml | 2 + src/java/main/org/apache/jute/Record.java | 3 ++ .../org/apache/jute/compiler/JRecord.java | 2 + .../org/apache/zookeeper/AsyncCallback.java | 10 +++++ .../main/org/apache/zookeeper/CreateMode.java | 2 + .../org/apache/zookeeper/KeeperException.java | 32 ++++++++++++++-- .../apache/zookeeper/ServerAdminClient.java | 3 ++ .../org/apache/zookeeper/Transaction.java | 2 + .../org/apache/zookeeper/WatchedEvent.java | 2 + .../main/org/apache/zookeeper/Watcher.java | 6 +++ .../main/org/apache/zookeeper/ZooDefs.java | 6 +++ .../main/org/apache/zookeeper/ZooKeeper.java | 3 ++ .../org/apache/zookeeper/ZooKeeperMain.java | 12 +++--- .../zookeeper/client/FourLetterWordMain.java | 5 ++- .../apache/zookeeper/client/HostProvider.java | 3 ++ .../zookeeper/client/StaticHostProvider.java | 2 + .../apache/zookeeper/server/LogFormatter.java | 2 + .../apache/zookeeper/server/PurgeTxnLog.java | 2 + .../apache/zookeeper/server/ServerConfig.java | 2 + .../zookeeper/server/SnapshotFormatter.java | 2 + .../zookeeper/server/ZooKeeperServerMain.java | 2 + .../server/quorum/QuorumPeerConfig.java | 8 ++-- .../server/quorum/QuorumPeerMain.java | 2 + .../zookeeper/server/upgrade/UpgradeMain.java | 2 + 25 files changed, 125 insertions(+), 29 deletions(-) diff --git a/build.xml b/build.xml index d34ca808fbf..0c3e09285ef 100644 --- a/build.xml +++ b/build.xml @@ -117,7 +117,9 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> - + + + @@ -273,7 +275,7 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> + includes="org/apache/jute/**" debug="on" classpath="${ivy.lib}/audience-annotations-${audience-annotations.version}.jar" /> @@ -340,9 +342,9 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> - + + target="${javac.target}" source="${javac.source}" debug="on" classpath="${ivy.lib}/audience-annotations-${audience-annotations.version}.jar" /> @@ -498,23 +500,24 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> windowtitle="${Name} ${version} API" doctitle="${Name} ${version} API" bottom="Copyright &copy; ${year} The Apache Software Foundation" + doclet="org.apache.yetus.audience.tools.IncludePublicAnnotationsStandardDoclet" + docletpath="${ivy.lib}/audience-annotations-${audience-annotations.version}.jar" > - - - - - - - - - - + + + + - - - + + + + + + + + diff --git a/ivy.xml b/ivy.xml index 8aa76c53a9d..b102ceef159 100644 --- a/ivy.xml +++ b/ivy.xml @@ -49,6 +49,8 @@ + + diff --git a/src/java/main/org/apache/jute/Record.java b/src/java/main/org/apache/jute/Record.java index bc7a350eeaf..d955280578d 100644 --- a/src/java/main/org/apache/jute/Record.java +++ b/src/java/main/org/apache/jute/Record.java @@ -18,12 +18,15 @@ package org.apache.jute; +import org.apache.yetus.audience.InterfaceAudience; + import java.io.IOException; /** * Interface that is implemented by generated classes. * */ +@InterfaceAudience.Public public interface Record { public void serialize(OutputArchive archive, String tag) throws IOException; diff --git a/src/java/main/org/apache/jute/compiler/JRecord.java b/src/java/main/org/apache/jute/compiler/JRecord.java index 13965f7e815..7322f216b96 100644 --- a/src/java/main/org/apache/jute/compiler/JRecord.java +++ b/src/java/main/org/apache/jute/compiler/JRecord.java @@ -428,6 +428,8 @@ public void genJavaCode(File outputDirectory) throws IOException { jj.write("\n"); jj.write("package "+getJavaPackage()+";\n\n"); jj.write("import org.apache.jute.*;\n"); + jj.write("import org.apache.yetus.audience.InterfaceAudience;\n"); + jj.write("@InterfaceAudience.Public\n"); jj.write("public class "+getName()+" implements Record {\n"); for (Iterator i = mFields.iterator(); i.hasNext();) { JField jf = i.next(); diff --git a/src/java/main/org/apache/zookeeper/AsyncCallback.java b/src/java/main/org/apache/zookeeper/AsyncCallback.java index 4e8fbf4afbc..f02a824abfb 100644 --- a/src/java/main/org/apache/zookeeper/AsyncCallback.java +++ b/src/java/main/org/apache/zookeeper/AsyncCallback.java @@ -19,6 +19,7 @@ import java.util.List; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; @@ -29,11 +30,13 @@ *

            * ZooKeeper provides asynchronous version as equivalent to synchronous APIs. */ +@InterfaceAudience.Public public interface AsyncCallback { /** * This callback is used to retrieve the stat of the node. */ + @InterfaceAudience.Public interface StatCallback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -68,6 +71,7 @@ interface StatCallback extends AsyncCallback { /** * This callback is used to retrieve the data and stat of the node. */ + @InterfaceAudience.Public interface DataCallback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -100,6 +104,7 @@ public void processResult(int rc, String path, Object ctx, byte data[], /** * This callback is used to retrieve the ACL and stat of the node. */ + @InterfaceAudience.Public interface ACLCallback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -132,6 +137,7 @@ public void processResult(int rc, String path, Object ctx, /** * This callback is used to retrieve the children of the node. */ + @InterfaceAudience.Public interface ChildrenCallback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -162,6 +168,7 @@ public void processResult(int rc, String path, Object ctx, /** * This callback is used to retrieve the children and stat of the node. */ + @InterfaceAudience.Public interface Children2Callback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -183,6 +190,7 @@ public void processResult(int rc, String path, Object ctx, /** * This callback is used to retrieve the name of the node. */ + @InterfaceAudience.Public interface StringCallback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -226,6 +234,7 @@ interface StringCallback extends AsyncCallback { * org.apache.zookeeper.ZooKeeper#sync(String, * org.apache.zookeeper.AsyncCallback.VoidCallback, Object)}. */ + @InterfaceAudience.Public interface VoidCallback extends AsyncCallback { /** * Process the result of the asynchronous call. @@ -268,6 +277,7 @@ interface VoidCallback extends AsyncCallback { * See {@link org.apache.zookeeper.ZooKeeper#multi} for more information. * @since 3.4.7 */ + @InterfaceAudience.Public interface MultiCallback extends AsyncCallback { /** * Process the result of the asynchronous call. diff --git a/src/java/main/org/apache/zookeeper/CreateMode.java b/src/java/main/org/apache/zookeeper/CreateMode.java index d87f410c84a..84f5be0e93d 100644 --- a/src/java/main/org/apache/zookeeper/CreateMode.java +++ b/src/java/main/org/apache/zookeeper/CreateMode.java @@ -17,6 +17,7 @@ */ package org.apache.zookeeper; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; @@ -24,6 +25,7 @@ /*** * CreateMode value determines how the znode is created on ZooKeeper. */ +@InterfaceAudience.Public public enum CreateMode { /** diff --git a/src/java/main/org/apache/zookeeper/KeeperException.java b/src/java/main/org/apache/zookeeper/KeeperException.java index 2664411d36b..bdf4203e006 100644 --- a/src/java/main/org/apache/zookeeper/KeeperException.java +++ b/src/java/main/org/apache/zookeeper/KeeperException.java @@ -18,6 +18,8 @@ package org.apache.zookeeper; +import org.apache.yetus.audience.InterfaceAudience; + import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; @@ -25,6 +27,7 @@ import java.util.Map; @SuppressWarnings("serial") +@InterfaceAudience.Public public abstract class KeeperException extends Exception { /** * All multi-requests that result in an exception retain the results @@ -131,7 +134,7 @@ public static KeeperException create(Code code) { return new SessionMovedException(); case NOTREADONLY: return new NotReadOnlyException(); - + case OK: default: throw new IllegalArgumentException("Invalid exception code"); @@ -163,6 +166,7 @@ public void setCode(int code) { * javadoc to include in the user API spec. */ @Deprecated + @InterfaceAudience.Public public interface CodeDeprecated { /** * @deprecated deprecated in 3.1.0, use {@link Code#OK} instead @@ -288,6 +292,7 @@ public interface CodeDeprecated { * constants. The old, deprecated, values are in "camel case" while the new * enum values are in all CAPS. */ + @InterfaceAudience.Public public static enum Code implements CodeDeprecated { /** Everything is OK */ OK (Ok), @@ -482,7 +487,7 @@ void setMultiResults(List results) { * If this exception was thrown by a multi-request then the (partial) results * and error codes can be retrieved using this getter. * @return A copy of the list of results from the operations in the multi-request. - * + * * @since 3.4.0 * */ @@ -493,6 +498,7 @@ public List getResults() { /** * @see Code#APIERROR */ + @InterfaceAudience.Public public static class APIErrorException extends KeeperException { public APIErrorException() { super(Code.APIERROR); @@ -502,6 +508,7 @@ public APIErrorException() { /** * @see Code#AUTHFAILED */ + @InterfaceAudience.Public public static class AuthFailedException extends KeeperException { public AuthFailedException() { super(Code.AUTHFAILED); @@ -511,6 +518,7 @@ public AuthFailedException() { /** * @see Code#BADARGUMENTS */ + @InterfaceAudience.Public public static class BadArgumentsException extends KeeperException { public BadArgumentsException() { super(Code.BADARGUMENTS); @@ -523,6 +531,7 @@ public BadArgumentsException(String path) { /** * @see Code#BADVERSION */ + @InterfaceAudience.Public public static class BadVersionException extends KeeperException { public BadVersionException() { super(Code.BADVERSION); @@ -535,6 +544,7 @@ public BadVersionException(String path) { /** * @see Code#CONNECTIONLOSS */ + @InterfaceAudience.Public public static class ConnectionLossException extends KeeperException { public ConnectionLossException() { super(Code.CONNECTIONLOSS); @@ -544,6 +554,7 @@ public ConnectionLossException() { /** * @see Code#DATAINCONSISTENCY */ + @InterfaceAudience.Public public static class DataInconsistencyException extends KeeperException { public DataInconsistencyException() { super(Code.DATAINCONSISTENCY); @@ -553,6 +564,7 @@ public DataInconsistencyException() { /** * @see Code#INVALIDACL */ + @InterfaceAudience.Public public static class InvalidACLException extends KeeperException { public InvalidACLException() { super(Code.INVALIDACL); @@ -565,6 +577,7 @@ public InvalidACLException(String path) { /** * @see Code#INVALIDCALLBACK */ + @InterfaceAudience.Public public static class InvalidCallbackException extends KeeperException { public InvalidCallbackException() { super(Code.INVALIDCALLBACK); @@ -574,6 +587,7 @@ public InvalidCallbackException() { /** * @see Code#MARSHALLINGERROR */ + @InterfaceAudience.Public public static class MarshallingErrorException extends KeeperException { public MarshallingErrorException() { super(Code.MARSHALLINGERROR); @@ -583,6 +597,7 @@ public MarshallingErrorException() { /** * @see Code#NOAUTH */ + @InterfaceAudience.Public public static class NoAuthException extends KeeperException { public NoAuthException() { super(Code.NOAUTH); @@ -592,6 +607,7 @@ public NoAuthException() { /** * @see Code#NOCHILDRENFOREPHEMERALS */ + @InterfaceAudience.Public public static class NoChildrenForEphemeralsException extends KeeperException { public NoChildrenForEphemeralsException() { super(Code.NOCHILDRENFOREPHEMERALS); @@ -604,6 +620,7 @@ public NoChildrenForEphemeralsException(String path) { /** * @see Code#NODEEXISTS */ + @InterfaceAudience.Public public static class NodeExistsException extends KeeperException { public NodeExistsException() { super(Code.NODEEXISTS); @@ -616,6 +633,7 @@ public NodeExistsException(String path) { /** * @see Code#NONODE */ + @InterfaceAudience.Public public static class NoNodeException extends KeeperException { public NoNodeException() { super(Code.NONODE); @@ -628,6 +646,7 @@ public NoNodeException(String path) { /** * @see Code#NOTEMPTY */ + @InterfaceAudience.Public public static class NotEmptyException extends KeeperException { public NotEmptyException() { super(Code.NOTEMPTY); @@ -640,6 +659,7 @@ public NotEmptyException(String path) { /** * @see Code#OPERATIONTIMEOUT */ + @InterfaceAudience.Public public static class OperationTimeoutException extends KeeperException { public OperationTimeoutException() { super(Code.OPERATIONTIMEOUT); @@ -649,6 +669,7 @@ public OperationTimeoutException() { /** * @see Code#RUNTIMEINCONSISTENCY */ + @InterfaceAudience.Public public static class RuntimeInconsistencyException extends KeeperException { public RuntimeInconsistencyException() { super(Code.RUNTIMEINCONSISTENCY); @@ -658,15 +679,17 @@ public RuntimeInconsistencyException() { /** * @see Code#SESSIONEXPIRED */ + @InterfaceAudience.Public public static class SessionExpiredException extends KeeperException { public SessionExpiredException() { super(Code.SESSIONEXPIRED); } } - + /** * @see Code#SESSIONMOVED */ + @InterfaceAudience.Public public static class SessionMovedException extends KeeperException { public SessionMovedException() { super(Code.SESSIONMOVED); @@ -676,6 +699,7 @@ public SessionMovedException() { /** * @see Code#NOTREADONLY */ + @InterfaceAudience.Public public static class NotReadOnlyException extends KeeperException { public NotReadOnlyException() { super(Code.NOTREADONLY); @@ -685,6 +709,7 @@ public NotReadOnlyException() { /** * @see Code#SYSTEMERROR */ + @InterfaceAudience.Public public static class SystemErrorException extends KeeperException { public SystemErrorException() { super(Code.SYSTEMERROR); @@ -694,6 +719,7 @@ public SystemErrorException() { /** * @see Code#UNIMPLEMENTED */ + @InterfaceAudience.Public public static class UnimplementedException extends KeeperException { public UnimplementedException() { super(Code.UNIMPLEMENTED); diff --git a/src/java/main/org/apache/zookeeper/ServerAdminClient.java b/src/java/main/org/apache/zookeeper/ServerAdminClient.java index da17fcfcb55..9464f1ce368 100644 --- a/src/java/main/org/apache/zookeeper/ServerAdminClient.java +++ b/src/java/main/org/apache/zookeeper/ServerAdminClient.java @@ -26,11 +26,14 @@ import java.nio.ByteBuffer; import java.util.StringTokenizer; + +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.ZooTrace; +@InterfaceAudience.Public public class ServerAdminClient { private static final Logger LOG = LoggerFactory.getLogger(ServerAdminClient.class); diff --git a/src/java/main/org/apache/zookeeper/Transaction.java b/src/java/main/org/apache/zookeeper/Transaction.java index d8f0a76fdbc..d8f0e01a63f 100644 --- a/src/java/main/org/apache/zookeeper/Transaction.java +++ b/src/java/main/org/apache/zookeeper/Transaction.java @@ -17,6 +17,7 @@ package org.apache.zookeeper; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.data.ACL; import java.util.ArrayList; @@ -29,6 +30,7 @@ * @since 3.4.0 * */ +@InterfaceAudience.Public public class Transaction { private ZooKeeper zk; private List ops = new ArrayList(); diff --git a/src/java/main/org/apache/zookeeper/WatchedEvent.java b/src/java/main/org/apache/zookeeper/WatchedEvent.java index 63f29c3c631..851fc6c8567 100644 --- a/src/java/main/org/apache/zookeeper/WatchedEvent.java +++ b/src/java/main/org/apache/zookeeper/WatchedEvent.java @@ -17,6 +17,7 @@ */ package org.apache.zookeeper; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.proto.WatcherEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; @@ -27,6 +28,7 @@ * the current state of the ZooKeeper, and the path of the znode that * was involved in the event. */ +@InterfaceAudience.Public public class WatchedEvent { final private KeeperState keeperState; final private EventType eventType; diff --git a/src/java/main/org/apache/zookeeper/Watcher.java b/src/java/main/org/apache/zookeeper/Watcher.java index 12f2af36bfa..5c21485cdaa 100644 --- a/src/java/main/org/apache/zookeeper/Watcher.java +++ b/src/java/main/org/apache/zookeeper/Watcher.java @@ -18,6 +18,8 @@ package org.apache.zookeeper; +import org.apache.yetus.audience.InterfaceAudience; + /** * This interface specifies the public interface an event handler class must * implement. A ZooKeeper client will get various events from the ZooKeeper @@ -26,15 +28,18 @@ * is expected to be an instance of a class that implements Watcher interface. * */ +@InterfaceAudience.Public public interface Watcher { /** * This interface defines the possible states an Event may represent */ + @InterfaceAudience.Public public interface Event { /** * Enumeration of states the ZooKeeper may be at the event */ + @InterfaceAudience.Public public enum KeeperState { /** Unused, this state is never generated by the server */ @Deprecated @@ -112,6 +117,7 @@ public static KeeperState fromInt(int intValue) { /** * Enumeration of types of events that may occur on the ZooKeeper */ + @InterfaceAudience.Public public enum EventType { None (-1), NodeCreated (1), diff --git a/src/java/main/org/apache/zookeeper/ZooDefs.java b/src/java/main/org/apache/zookeeper/ZooDefs.java index c7f1b208c36..09bc95c3693 100644 --- a/src/java/main/org/apache/zookeeper/ZooDefs.java +++ b/src/java/main/org/apache/zookeeper/ZooDefs.java @@ -21,10 +21,14 @@ import java.util.ArrayList; import java.util.Collections; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; +@InterfaceAudience.Public public class ZooDefs { + + @InterfaceAudience.Public public interface OpCode { public final int notification = 0; @@ -67,6 +71,7 @@ public interface OpCode { public final int error = -1; } + @InterfaceAudience.Public public interface Perms { int READ = 1 << 0; @@ -81,6 +86,7 @@ public interface Perms { int ALL = READ | WRITE | CREATE | DELETE | ADMIN; } + @InterfaceAudience.Public public interface Ids { /** * This Id represents anyone. diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java index e6e464dd08a..bd4587a1696 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeper.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java @@ -18,6 +18,7 @@ package org.apache.zookeeper; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.AsyncCallback.*; import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.client.ConnectStringParser; @@ -83,6 +84,7 @@ * EventType None and KeeperState Disconnected. * */ +@InterfaceAudience.Public public class ZooKeeper { public static final String ZOOKEEPER_CLIENT_CNXN_SOCKET = "zookeeper.clientCnxnSocket"; @@ -313,6 +315,7 @@ protected Map> getWatches(int rc) { } } + @InterfaceAudience.Public public enum States { CONNECTING, ASSOCIATING, CONNECTED, CONNECTEDREADONLY, CLOSED, AUTH_FAILED, NOT_CONNECTED; diff --git a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java index 74b05c262c9..1ec7e68422f 100644 --- a/src/java/main/org/apache/zookeeper/ZooKeeperMain.java +++ b/src/java/main/org/apache/zookeeper/ZooKeeperMain.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.NoSuchElementException; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.AsyncCallback.DataCallback; @@ -48,6 +49,7 @@ * The command line client to ZooKeeper. * */ +@InterfaceAudience.Public public class ZooKeeperMain { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperMain.class); static final Map commandMap = new HashMap( ); @@ -642,7 +644,7 @@ protected boolean processZKCmd(MyCommandOptions co) String path = null; List acl = Ids.OPEN_ACL_UNSAFE; LOG.debug("Processing " + cmd); - + if (cmd.equals("quit")) { System.out.println("Quitting..."); zk.close(); @@ -675,9 +677,9 @@ protected boolean processZKCmd(MyCommandOptions co) if (args.length >=2) { connectToZK(args[1]); } else { - connectToZK(host); + connectToZK(host); } - } + } // Below commands all need a live connection if (zk == null || !zk.getState().isAlive()) { @@ -751,7 +753,7 @@ protected boolean processZKCmd(MyCommandOptions co) path = args[1]; stat = zk.exists(path, watch); if (stat == null) { - throw new KeeperException.NoNodeException(path); + throw new KeeperException.NoNodeException(path); } printStat(stat); } else if (cmd.equals("listquota") && args.length >= 2) { @@ -811,7 +813,7 @@ protected boolean processZKCmd(MyCommandOptions co) usage(); } } else if (cmd.equals("close")) { - zk.close(); + zk.close(); } else if (cmd.equals("sync") && args.length >= 2) { path = args[1]; zk.sync(path, new AsyncCallback.VoidCallback() { public void processResult(int rc, String path, Object ctx) { System.out.println("Sync returned " + rc); } }, null ); diff --git a/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java b/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java index a8ecb2201e1..a4175e46ce8 100644 --- a/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java +++ b/src/java/main/org/apache/zookeeper/client/FourLetterWordMain.java @@ -29,11 +29,14 @@ import java.net.Socket; import java.net.SocketTimeoutException; +import org.apache.yetus.audience.InterfaceAudience; + +@InterfaceAudience.Public public class FourLetterWordMain { //in milliseconds, socket should connect/read within this period otherwise SocketTimeoutException private static final int DEFAULT_SOCKET_TIMEOUT = 5000; protected static final Logger LOG = Logger.getLogger(FourLetterWordMain.class); - + /** * Send the 4letterword * @param host the destination host diff --git a/src/java/main/org/apache/zookeeper/client/HostProvider.java b/src/java/main/org/apache/zookeeper/client/HostProvider.java index c2815b3e7be..490dc32b458 100644 --- a/src/java/main/org/apache/zookeeper/client/HostProvider.java +++ b/src/java/main/org/apache/zookeeper/client/HostProvider.java @@ -18,6 +18,8 @@ package org.apache.zookeeper.client; +import org.apache.yetus.audience.InterfaceAudience; + import java.net.InetSocketAddress; /** @@ -39,6 +41,7 @@ * * A HostProvider that re-resolves the InetSocketAddress after a timeout. * * A HostProvider that prefers nearby hosts. */ +@InterfaceAudience.Public public interface HostProvider { public int size(); diff --git a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java index e207963fd66..5754b275cc5 100644 --- a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java +++ b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.List; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +35,7 @@ * Most simple HostProvider, resolves only on instantiation. * */ +@InterfaceAudience.Public public final class StaticHostProvider implements HostProvider { private static final Logger LOG = LoggerFactory .getLogger(StaticHostProvider.class); diff --git a/src/java/main/org/apache/zookeeper/server/LogFormatter.java b/src/java/main/org/apache/zookeeper/server/LogFormatter.java index cd1347d91db..c5274e58947 100644 --- a/src/java/main/org/apache/zookeeper/server/LogFormatter.java +++ b/src/java/main/org/apache/zookeeper/server/LogFormatter.java @@ -30,6 +30,7 @@ import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; import org.apache.jute.Record; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.persistence.FileHeader; @@ -37,6 +38,7 @@ import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.TxnHeader; +@InterfaceAudience.Public public class LogFormatter { private static final Logger LOG = LoggerFactory.getLogger(LogFormatter.class); diff --git a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java index bb972c88490..09cffbaf1e4 100644 --- a/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/PurgeTxnLog.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Set; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.Util; import org.slf4j.Logger; @@ -41,6 +42,7 @@ * files and snapdir files keeping the last "-n" snapshot files * and the corresponding logs. */ +@InterfaceAudience.Public public class PurgeTxnLog { private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnLog.class); diff --git a/src/java/main/org/apache/zookeeper/server/ServerConfig.java b/src/java/main/org/apache/zookeeper/server/ServerConfig.java index 626ae113cc4..c0dee3c57a4 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/ServerConfig.java @@ -21,6 +21,7 @@ import java.net.InetSocketAddress; import java.util.Arrays; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; @@ -30,6 +31,7 @@ * We use this instead of Properties as it's typed. * */ +@InterfaceAudience.Public public class ServerConfig { //// //// If you update the configuration parameters be sure diff --git a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java index bc434020f0d..1b131a3040a 100644 --- a/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java +++ b/src/java/main/org/apache/zookeeper/server/SnapshotFormatter.java @@ -31,12 +31,14 @@ import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; +import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.server.persistence.FileSnap; /** * Dump a snapshot file to stdout. */ +@InterfaceAudience.Public public class SnapshotFormatter { /** diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java index 1b0f59fe383..59c703b9221 100644 --- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java +++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServerMain.java @@ -24,6 +24,7 @@ import javax.management.JMException; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.ManagedUtil; @@ -33,6 +34,7 @@ /** * This class starts and runs a standalone ZooKeeperServer. */ +@InterfaceAudience.Public public class ZooKeeperServerMain { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerMain.class); 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 621f830d81d..83d41b34f08 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java @@ -31,6 +31,7 @@ import java.util.Properties; import java.util.Map.Entry; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -43,6 +44,7 @@ import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; +@InterfaceAudience.Public public class QuorumPeerConfig { private static final Logger LOG = LoggerFactory.getLogger(QuorumPeerConfig.class); @@ -400,7 +402,7 @@ public void parseProperties(Properties zkProp) // Now add observers to servers, once the quorums have been // figured out servers.putAll(observers); - + File myIdFile = new File(dataDir, "myid"); if (!myIdFile.exists()) { throw new IllegalArgumentException(myIdFile.toString() @@ -445,7 +447,7 @@ public void parseProperties(Properties zkProp) public int getInitLimit() { return initLimit; } public int getSyncLimit() { return syncLimit; } public int getElectionAlg() { return electionAlg; } - public int getElectionPort() { return electionPort; } + public int getElectionPort() { return electionPort; } public int getSnapRetainCount() { return snapRetainCount; @@ -459,7 +461,7 @@ public boolean getSyncEnabled() { return syncEnabled; } - public QuorumVerifier getQuorumVerifier() { + public QuorumVerifier getQuorumVerifier() { return quorumVerifier; } 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 4ea7e54caed..9644ced1faf 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -22,6 +22,7 @@ import javax.management.JMException; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.jmx.ManagedUtil; @@ -60,6 +61,7 @@ * "myid" that contains the server id as an ASCII decimal value. * */ +@InterfaceAudience.Public public class QuorumPeerMain { private static final Logger LOG = LoggerFactory.getLogger(QuorumPeerMain.class); diff --git a/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java b/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java index 6648a1276f8..8d6c0ab2cc7 100644 --- a/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java +++ b/src/java/main/org/apache/zookeeper/server/upgrade/UpgradeMain.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; +import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.DataTree; @@ -36,6 +37,7 @@ * it creates a backup in the dataDir/.bkup and snapShotDir/.bkup which * can be retrieved back to the snapShotDir and dataDir */ +@InterfaceAudience.Public public class UpgradeMain { File snapShotDir; File dataDir; From 7956f138a781b2b4bd166c50e49bbad83d40c2a8 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Wed, 20 Feb 2013 05:24:31 +0000 Subject: [PATCH 432/444] ZOOKEEPER-1643. Windows: fetch_and_add not 64bit-compatible, may not be correct (Erik Anderson via michim) git-svn-id: https://svn.apache.org/repos/asf/zookeeper/trunk@1448007 13f79535-47bb-0310-9956-ffa450edef68 --- src/c/src/mt_adaptor.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/c/src/mt_adaptor.c b/src/c/src/mt_adaptor.c index 7dc78789f13..1b18ac6229a 100644 --- a/src/c/src/mt_adaptor.c +++ b/src/c/src/mt_adaptor.c @@ -483,25 +483,9 @@ int32_t inc_ref_counter(zhandle_t* zh,int i) int32_t fetch_and_add(volatile int32_t* operand, int incr) { #ifndef WIN32 - int32_t result; - asm __volatile__( - "lock xaddl %0,%1\n" - : "=r"(result), "=m"(*(int *)operand) - : "0"(incr) - : "memory"); - return result; + return __sync_fetch_and_add(operand, incr); #else - volatile int32_t result; - _asm - { - mov eax, operand; //eax = v; - mov ebx, incr; // ebx = i; - mov ecx, 0x0; // ecx = 0; - lock xadd dword ptr [eax], ecx; - lock xadd dword ptr [eax], ebx; - mov result, ecx; // result = ebx; - } - return result; + return InterlockedExchangeAdd(operand, incr); #endif } From ce67fdd46fdc2eba8f4d546739dc486a5cd2dbb4 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 1 Aug 2017 08:43:29 -0700 Subject: [PATCH 433/444] ZOOKEEPER-2841: ZooKeeper public include files leak porting changes This patch primarily adds a cross-platform CMake build system. This is in addition to the Autotools system on Linux, until it can be deprecated, and this replaces the existing committed Visual Studio Solutions for Windows. As this commit deprecates (and breaks) the previously existing Visual Studio solutions and projects, they've been removed. Building on Windows now requires CMake, but this enables the support of any code-supported Visual Studio version. Support for Visual Studio 2017 was explicitly added by this patch, and support for future versions, barring software bugs, should be available automatically through CMake. The notable features lacking in comparison to Autotools are Solaris support, shared library builds, whitelisted symbol exports, libtool integration, and doxygen document generation. Almost everything else from Autotools has been ported, including header/function/library checks, and all targets (zookeeper, hashtable, cli, load_gen, and tests). The primary work involved for CMake (other than the writing of `CMakeLists.txt`) was transitioning the hand-written `winconfig.h` to an auto-generated `config.h` file, and guarding code with `#ifdef HAVE_FEATURE`. The `cmake_config.h.in` template was modeled after the Autotools config file so that the feature guards share the same names. This patch also refactors the Windows port of the C client. Notably, it moves as much porting code as possible out of the publicly included `winconfig.h` header and into the relevant source files, or the private `winport.h` header. While `load_gen.c` looks at first glance as if it were ported to Windows, it never actually was, so the erroneous `#include "win32port.h"` was removed, and the target is not built on Windows. The `include/winstdint.h` header was removed as it has been replaced by ``. This might break very old versions of Visual Studio; but in those cases, previous versions of the client are available. A bug for upstream libraries including the ZooKeeper headers was fixed by removing `#undef AF_INET6` entirely, and removing `#include `, as it "pulls in the world" and should not be included publicly. A guard was placed around `#define snprintf` in order to enable compiling with modern versions of MSVC, including Visual Studio 2015. A problem with `DLL_EXPORT` and `USE_STATIC_LIB` being redefined was fixed. There are existent warnings which this patch did not attempt to fix. Building tests on Windows is not supported, but was not previously supported. The tests need to be ported. Future work should resolve the `ACL` / `ZKACL` conflict, and potentially remove `include/winconfig.h` altogether. Author: Andrew Schwartzmeyer Reviewers: Michael Han Closes #313 from andschwa/cmake-backport-3.4 --- src/c/CMakeLists.txt | 236 ++++++++++++++++++++++++++++++ src/c/Cli.vcproj | 210 -------------------------- src/c/README | 22 +++ src/c/cmake_config.h.in | 154 +++++++++++++++++++ src/c/include/recordio.h | 9 +- src/c/include/winconfig.h | 189 +----------------------- src/c/include/winstdint.h | 247 ------------------------------- src/c/include/zookeeper.h | 16 +- src/c/src/load_gen.c | 4 - src/c/src/mt_adaptor.c | 7 +- src/c/src/recordio.c | 2 + src/c/src/winport.c | 14 +- src/c/src/winport.h | 33 +++-- src/c/src/zk_log.c | 5 +- src/c/src/zookeeper.c | 45 +++++- src/c/zookeeper.sln | 25 ---- src/c/zookeeper.vcproj | 300 -------------------------------------- 17 files changed, 519 insertions(+), 999 deletions(-) create mode 100644 src/c/CMakeLists.txt delete mode 100644 src/c/Cli.vcproj create mode 100644 src/c/cmake_config.h.in delete mode 100644 src/c/include/winstdint.h delete mode 100644 src/c/zookeeper.sln delete mode 100644 src/c/zookeeper.vcproj diff --git a/src/c/CMakeLists.txt b/src/c/CMakeLists.txt new file mode 100644 index 00000000000..50f50fc46c8 --- /dev/null +++ b/src/c/CMakeLists.txt @@ -0,0 +1,236 @@ +# 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. + +cmake_minimum_required(VERSION 3.6) + +project(zookeeper VERSION 3.4.10) +set(email user@zookeeper.apache.org) +set(description "zookeeper C client") + +# general options +include_directories(include tests generated ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) +if(UNIX) + add_compile_options(-Wall -fPIC) +elseif(WIN32) + add_compile_options(/W3) +endif() +add_definitions(-DUSE_STATIC_LIB) + +# TODO: Enable /WX and /W4 on Windows. Currently there are ~1000 warnings. +# TODO: Add Solaris support. +# TODO: Add a shared library option. +# TODO: Specify symbols to export. +# TODO: Generate doxygen documentation. + +# Sync API option +option(WANT_SYNCAPI "Enables Sync API support" ON) +if(WANT_SYNCAPI) + add_definitions(-DTHREADED) + if(WIN32) + add_compile_options(/MT) + endif() +endif() + +# CppUnit option +if(WIN32) + # The tests do not yet compile on Windows, so we set this to off by default. + # + # Note that CMake can does not have expressions except in conditionals, + # so we're left with this if/else/endif pattern. + set(DEFAULT_WANT_CPPUNIT OFF) +else() + set(DEFAULT_WANT_CPPUNIT ON) +endif() +option(WANT_CPPUNIT "Enables CppUnit and tests" ${DEFAULT_WANT_CPPUNIT}) + +# The function `to_have(in out)` converts a header name like `arpa/inet.h` +# into an Autotools style preprocessor definition `HAVE_ARPA_INET_H`. +# This is then set or unset in `configure_file()` step. +# +# Note that CMake functions do not have return values; instead an "out" +# variable must be passed, and explicitly set with parent scope. +function(to_have in out) + string(TOUPPER ${in} str) + string(REGEX REPLACE "/|\\." "_" str ${str}) + set(${out} "HAVE_${str}" PARENT_SCOPE) +endfunction() + +# include file checks +foreach(f generated/zookeeper.jute.h generated/zookeeper.jute.c) + if(EXISTS "${CMAKE_SOURCE_DIR}/${f}") + to_have(${f} name) + set(${name} 1) + else() + message(FATAL_ERROR + "jute files are missing!\n" + "Please run 'ant compile_jute' while in the ZooKeeper top level directory.") + endif() +endforeach() + +# header checks +include(CheckIncludeFile) +set(check_headers + arpa/inet.h + dlfcn.h + fcntl.h + inttypes.h + memory.h + netdb.h + netinet/in.h + stdint.h + stdlib.h + string.h + strings.h + sys/socket.h + sys/stat.h + sys/time.h + sys/types.h + unistd.h + sys/utsname.h) + +foreach(f ${check_headers}) + to_have(${f} name) + check_include_file(${f} ${name}) +endforeach() + +# function checks +include(CheckFunctionExists) +set(check_functions + getcwd + gethostbyname + gethostname + getlogin + getpwuid_r + gettimeofday + getuid + memmove + memset + poll + socket + strchr + strdup + strerror + strtol) + +foreach(fn ${check_functions}) + to_have(${fn} name) + check_function_exists(${fn} ${name}) +endforeach() + +# library checks +set(check_libraries rt m pthread) +foreach(lib ${check_libraries}) + to_have("lib${lib}" name) + find_library(${name} ${lib}) +endforeach() + +# IPv6 check +include(CheckStructHasMember) +check_struct_has_member("struct sockaddr_in6" sin6_addr "netinet/in.h" ZOO_IPV6_ENABLED) + +# configure +configure_file(cmake_config.h.in ${CMAKE_SOURCE_DIR}/include/config.h) + +# hashtable library +set(hashtable_sources src/hashtable/hashtable_itr.c src/hashtable/hashtable.c) +add_library(hashtable STATIC ${hashtable_sources}) + +# zookeeper library +set(zookeeper_sources + src/zookeeper.c + src/recordio.c + generated/zookeeper.jute.c + src/zk_log.c + src/zk_hashtable.c) + +if(WANT_SYNCAPI) + list(APPEND zookeeper_sources src/mt_adaptor.c) +else() + list(APPEND zookeeper_sources src/st_adaptor.c) +endif() + +if(WIN32) + list(APPEND zookeeper_sources src/winport.c) +endif() + +add_library(zookeeper STATIC ${zookeeper_sources}) +target_link_libraries(zookeeper PUBLIC hashtable) +if(UNIX) + target_link_libraries(zookeeper PUBLIC m rt) +elseif(WIN32) + # Link to Winsock 2.0 + target_link_libraries(zookeeper PUBLIC ws2_32) +endif() + +if(WANT_SYNCAPI AND NOT WIN32) + # TODO: Use `find_library()` or `FindThreads()` for `pthread`. + target_link_libraries(zookeeper PUBLIC pthread) +endif() + +# cli executable +add_executable(cli src/cli.c) +target_link_libraries(cli zookeeper) + +# load_gen executable +if(WANT_SYNCAPI AND NOT WIN32) + add_executable(load_gen src/load_gen.c) + target_link_libraries(load_gen zookeeper) +endif() + +# tests +set(test_sources + tests/TestDriver.cc + tests/LibCMocks.cc + tests/LibCSymTable.cc + tests/MocksBase.cc + tests/ZKMocks.cc + tests/Util.cc + tests/ThreadingUtil.cc + tests/TestZookeeperInit.cc + tests/TestZookeeperClose.cc + tests/TestClientRetry.cc + tests/TestOperations.cc + tests/TestMulti.cc + tests/TestWatchers.cc + tests/TestClient.cc) + +if(WANT_SYNCAPI) + list(APPEND test_sources tests/PthreadMocks.cc) +endif() + +if(WANT_CPPUNIT) + add_executable(zktest ${test_sources}) + target_compile_definitions(zktest + PRIVATE -DZKSERVER_CMD="${CMAKE_SOURCE_DIR}/tests/zkServer.sh") + # TODO: Use `find_library()` for `cppunit`. + target_link_libraries(zktest zookeeper cppunit dl) + + # This reads the link flags from the file `tests/wrappers.opt` into + # the variable `symbol_wrappers` for use in `target_link_libraries`. + # It is a holdover from the original build system. + file(STRINGS tests/wrappers.opt symbol_wrappers) + if(WANT_SYNCAPI) + file(STRINGS tests/wrappers-mt.opt symbol_wrappers_mt) + endif() + + target_link_libraries(zktest ${symbol_wrappers} ${symbol_wrappers_mt}) + + enable_testing() + add_test(NAME zktest_runner COMMAND zktest) + set_property(TEST zktest_runner PROPERTY ENVIRONMENT + "ZKROOT=${CMAKE_SOURCE_DIR}/../.." + "CLASSPATH=$CLASSPATH:$CLOVER_HOME/lib/clover.jar") +endif() diff --git a/src/c/Cli.vcproj b/src/c/Cli.vcproj deleted file mode 100644 index 39ed8a429d6..00000000000 --- a/src/c/Cli.vcproj +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/c/README b/src/c/README index ea3d5d316a4..51158455a2a 100644 --- a/src/c/README +++ b/src/c/README @@ -71,6 +71,28 @@ tar downloaded from Apache please skip to step 2. default only HTML documentation is generated. For information on other document formats please use "./configure --help" +Alternatively you can use the CMake build system. On Windows, this is required. +Follow steps 1 and 2 above, and then continue here. + +1) do a "cmake [OPTIONS]" to generate the makefile or msbuild files (the correct + build system will be generated based on your platform). Some options from above + are supported: + -DCMAKE_BUILD_TYPE Debug by default, Release enables optimzation etc. + -DWANT_SYNCAPI ON by default, OFF disables the Sync API support + -DWANT_CPPUNIT ON except on Windows, OFF disables the tests + -DBUILD_SHARED_LIBS not yet supported, only static libraries are built + other CMake options see "cmake --help" for generic options, such as generator + +2) do a "cmake --build ." to build the default targets. Alternatively you can + invoke "make" or "msbuild" manually. If the tests were enabled, use "ctest -V" + to run them. + +Current limitations of the CMake build system include lack of Solaris support, +no shared library option, no explicitly exported symbols (all are exported by +default), no versions on the libraries, and no documentation generation. +Features of CMake include a single, easily consumed cross-platform build system +to generate the ZooKeeper C Client libraries for any project, with little to no +configuration. EXAMPLE/SAMPLE C CLIENT SHELL diff --git a/src/c/cmake_config.h.in b/src/c/cmake_config.h.in new file mode 100644 index 00000000000..55efd8a86f9 --- /dev/null +++ b/src/c/cmake_config.h.in @@ -0,0 +1,154 @@ +/** + * 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. + */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_FCNTL_H 1 + +/* Define to 1 if you have the file `generated/zookeeper.jute.c'. */ +#cmakedefine HAVE_GENERATED_ZOOKEEPER_JUTE_C 1 + +/* Define to 1 if you have the file `generated/zookeeper.jute.h'. */ +#cmakedefine HAVE_GENERATED_ZOOKEEPER_JUTE_H 1 + +/* Define to 1 if you have the `getcwd' function. */ +#cmakedefine HAVE_GETCWD 1 + +/* Define to 1 if you have the `gethostbyname' function. */ +#cmakedefine HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the `gethostname' function. */ +#cmakedefine HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the `getlogin' function. */ +#cmakedefine HAVE_GETLOGIN 1 + +/* Define to 1 if you have the `getpwuid_r' function. */ +#cmakedefine HAVE_GETPWUID_R 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `getuid' function. */ +#cmakedefine HAVE_GETUID 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +#cmakedefine HAVE_LIBRT 1 + +/* Define to 1 if you have the `memmove' function. */ +#cmakedefine HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#cmakedefine HAVE_MEMSET 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the `poll' function. */ +#cmakedefine HAVE_POLL 1 + +/* Define to 1 if you have the `socket' function. */ +#cmakedefine HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strchr' function. */ +#cmakedefine HAVE_STRCHR 1 + +/* Define to 1 if you have the `strdup' function. */ +#cmakedefine HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#cmakedefine HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtol' function. */ +#cmakedefine HAVE_STRTOL 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UTSNAME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if IPv6 support is available. */ +#cmakedefine ZOO_IPV6_ENABLED 1 + +/* poll() second argument type */ +#define POLL_NFDS_TYPE nfds_t + +/* Name of package */ +#define PACKAGE "${PROJECT_NAME}" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "${email}" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "${description}" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "${description} ${PROJECT_VERSION}" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "${PROJECT_NAME}" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "${PROJECT_VERSION}" + +/* Version number of package */ +#define VERSION "${PROJECT_VERSION}" + +#endif diff --git a/src/c/include/recordio.h b/src/c/include/recordio.h index 90f458b4aaa..eed5f99c2ef 100644 --- a/src/c/include/recordio.h +++ b/src/c/include/recordio.h @@ -19,11 +19,12 @@ #define __RECORDIO_H__ #include -#ifndef WIN32 -#define STRUCT_INITIALIZER(l,r) .l = r -#else -#define STRUCT_INITIALIZER(l,r) r +#include /* for int64_t */ +#ifdef WIN32 #include "winconfig.h" +#define STRUCT_INITIALIZER(l,r) r +#else +#define STRUCT_INITIALIZER(l,r) .l = r #endif #ifdef __cplusplus diff --git a/src/c/include/winconfig.h b/src/c/include/winconfig.h index 9d936ec99e2..c273a932a24 100644 --- a/src/c/include/winconfig.h +++ b/src/c/include/winconfig.h @@ -1,196 +1,15 @@ -/* Define to 1 if you have the header file. */ -#undef HAVE_ARPA_INET_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H - -/* Define to 1 if you have the file `generated/zookeeper.jute.c'. */ -#define HAVE_GENERATED_ZOOKEEPER_JUTE_C 1 - -/* Define to 1 if you have the file `generated/zookeeper.jute.h'. */ -#define HAVE_GENERATED_ZOOKEEPER_JUTE_H 1 - -/* Define to 1 if you have the `getcwd' function. */ -#undef HAVE_GETCWD - -/* Define to 1 if you have the `gethostbyname' function. */ -#undef HAVE_GETHOSTBYNAME - -/* Define to 1 if you have the `gethostname' function. */ -#define HAVE_GETHOSTNAME 1 - -/* Define to 1 if you have the `getlogin' function. */ -#undef HAVE_GETLOGIN - -/* Define to 1 if you have the `getpwuid_r' function. */ -#undef HAVE_GETPWUID_R - -/* Define to 1 if you have the `gettimeofday' function. */ -#undef HAVE_GETTIMEOFDAY - -/* Define to 1 if you have the `getuid' function. */ -#undef HAVE_GETUID - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `memmove' function. */ -#undef HAVE_MEMMOVE - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the `memset' function. */ -#undef HAVE_MEMSET - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETDB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETINET_IN_H - -/* Define to 1 if you have the `poll' function. */ -#undef HAVE_POLL - -/* Define to 1 if you have the `socket' function. */ -#undef HAVE_SOCKET - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strchr' function. */ -#define HAVE_STRCHR 1 - -/* Define to 1 if you have the `strdup' function. */ -#define HAVE_STRDUP 1 - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strtol' function. */ -#undef HAVE_STRTOL - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKET_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_UTSNAME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Name of package */ -#define PACKAGE "c-client-src" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "user@zookeeper.apache.org" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "zookeeper C client" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "zookeeper C client 3.4.10 win32" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "c-client-src" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "3.4.10" - -/* poll() second argument type */ -#define POLL_NFDS_TYPE - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS - -/* Define to 1 if you can safely include both and . */ -#define TIME_WITH_SYS_TIME - -/* Version number of package */ -#define VERSION "3.4.10" - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ +#ifndef WINCONFIG_H_ +#define WINCONFIG_H_ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #define inline __inline #endif -#ifdef WIN32 + #define __attribute__(x) #define __func__ __FUNCTION__ -#ifndef _WIN32_WINNT_NT4 -#define _WIN32_WINNT_NT4 0x0400 -#endif - -#define NTDDI_VERSION _WIN32_WINNT_NT4 -#define _WIN32_WINNT _WIN32_WINNT_NT4 - -#define _CRT_SECURE_NO_WARNINGS -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include -#undef AF_INET6 -#undef min -#undef max - -#include - -#define strtok_r strtok_s -#define localtime_r(a,b) localtime_s(b,a) -#define get_errno() errno=GetLastError() -#define random rand -#define snprintf _snprintf - -#define ACL ZKACL // Conflict with windows API - -#define EAI_ADDRFAMILY WSAEINVAL -#define EHOSTDOWN EPIPE -#define ESTALE ENODEV - -#ifndef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK -#endif - -#ifndef EINPROGRESS -#define EINPROGRESS WSAEINPROGRESS -#endif +#define ACL ZKACL /* Conflict with windows API */ -typedef int pid_t; #endif diff --git a/src/c/include/winstdint.h b/src/c/include/winstdint.h deleted file mode 100644 index d02608a5972..00000000000 --- a/src/c/include/winstdint.h +++ /dev/null @@ -1,247 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. 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. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#define INTMAX_C INT64_C -#define UINTMAX_C UINT64_C - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ] diff --git a/src/c/include/zookeeper.h b/src/c/include/zookeeper.h index 005f1f066ba..2503d69eb39 100644 --- a/src/c/include/zookeeper.h +++ b/src/c/include/zookeeper.h @@ -20,12 +20,22 @@ #define ZOOKEEPER_H_ #include -#ifndef WIN32 + +#include "config.h" + +#ifdef HAVE_SYS_SOCKET_H #include +#endif + +#ifdef HAVE_SYS_TIME_H #include -#else -#include "winconfig.h" #endif + +#ifdef WIN32 +#include /* must always be included before ws2tcpip.h */ +#include /* for struct sock_addr and socklen_t */ +#endif + #include #include diff --git a/src/c/src/load_gen.c b/src/c/src/load_gen.c index 0e172e83bf1..546d5d16d74 100644 --- a/src/c/src/load_gen.c +++ b/src/c/src/load_gen.c @@ -19,13 +19,9 @@ #include #include "zookeeper_log.h" #include -#ifndef WIN32 #ifdef THREADED #include #endif -#else -#include "win32port.h" -#endif #include #include diff --git a/src/c/src/mt_adaptor.c b/src/c/src/mt_adaptor.c index 1b18ac6229a..52d86d5d994 100644 --- a/src/c/src/mt_adaptor.c +++ b/src/c/src/mt_adaptor.c @@ -19,7 +19,7 @@ #define THREADED #endif -#ifndef DLL_EXPORT +#if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) # define USE_STATIC_LIB #endif @@ -373,8 +373,7 @@ void *do_io(void *v) int interest; int timeout; int maxfd=1; - int rc; - + zookeeper_interest(zh, &fd, &interest, &tv); if (fd != -1) { fds[1].fd=fd; @@ -436,7 +435,7 @@ void *do_io(void *v) } #endif // dispatch zookeeper events - rc = zookeeper_process(zh, interest); + zookeeper_process(zh, interest); // check the current state of the zhandle and terminate // if it is_unrecoverable() if(is_unrecoverable(zh)) diff --git a/src/c/src/recordio.c b/src/c/src/recordio.c index b17cb79a365..0fcf48e5ff7 100644 --- a/src/c/src/recordio.c +++ b/src/c/src/recordio.c @@ -23,6 +23,8 @@ #include #ifndef WIN32 #include +#else +#include /* for _htonl and _ntohl */ #endif void deallocate_String(char **s) diff --git a/src/c/src/winport.c b/src/c/src/winport.c index aeef3a84e61..3592ea149b5 100644 --- a/src/c/src/winport.c +++ b/src/c/src/winport.c @@ -18,8 +18,10 @@ #ifdef WIN32 #include "winport.h" -#include -#include +#include +#include /* for int64_t */ +#include /* must always be included before ws2tcpip.h */ +#include /* for SOCKET */ int pthread_mutex_lock(pthread_mutex_t* _mutex ){ int rc = WaitForSingleObject( *_mutex, // handle to mutex @@ -255,6 +257,14 @@ int pthread_setspecific(pthread_key_t key, const void *value) return ((rc != 0 ) ? 0 : GetLastError()); } +int gettimeofday(struct timeval *tp, void *tzp) { + int64_t now = 0; + if (tzp != 0) { errno = EINVAL; return -1; } + GetSystemTimeAsFileTime( (LPFILETIME)&now ); + tp->tv_sec = (long)(now / 10000000 - 11644473600LL); + tp->tv_usec = (now / 10) % 1000000; + return 0; +} int close(SOCKET fd) { return closesocket(fd); diff --git a/src/c/src/winport.h b/src/c/src/winport.h index 32272c03d87..da6028cd3db 100644 --- a/src/c/src/winport.h +++ b/src/c/src/winport.h @@ -25,9 +25,31 @@ #define WINPORT_H_ #ifdef WIN32 -#include +#include "winconfig.h" + +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include /* must always be included before ws2tcpip.h */ +#include /* for struct sock_addr used in zookeeper.h */ + +/* POSIX names are deprecated, use ISO conformant names instead. */ +#define strdup _strdup +#define getcwd _getcwd +#define getpid _getpid + +/* Windows "secure" versions of POSIX reentrant functions */ +#define strtok_r strtok_s +#define localtime_r(a,b) localtime_s(b,a) + +/* After this version of MSVC, snprintf became a defined function, + and so cannot be redefined, nor can #ifndef be used to guard it. */ +#if ((defined(_MSC_VER) && _MSC_VER < 1900) || !defined(_MSC_VER)) +#define snprintf _snprintf +#endif + + #include #include +#include /* for int64_t */ #include #include @@ -105,14 +127,7 @@ int pthread_key_delete(pthread_key_t key); void *pthread_getspecific(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *value); -inline int gettimeofday(struct timeval *tp, void *tzp) { - int64_t now = 0; - if (tzp != 0) { errno = EINVAL; return -1; } - GetSystemTimeAsFileTime( (LPFILETIME)&now ); - tp->tv_sec = (long)(now / 10000000 - 11644473600LL); - tp->tv_usec = (now / 10) % 1000000; - return 0; -} +int gettimeofday(struct timeval *tp, void *tzp); int close(SOCKET fd); int Win32WSAStartup(); void Win32WSACleanup(); diff --git a/src/c/src/zk_log.c b/src/c/src/zk_log.c index 37ff856ca0d..6b4fdfa2bcd 100644 --- a/src/c/src/zk_log.c +++ b/src/c/src/zk_log.c @@ -16,13 +16,16 @@ * limitations under the License. */ -#ifndef DLL_EXPORT +#if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) # define USE_STATIC_LIB #endif #include "zookeeper_log.h" #ifndef WIN32 #include +#else +typedef DWORD pid_t; +#include /* for getpid */ #endif #include diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index e9d462ed229..a8fef67a61b 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -16,7 +16,7 @@ * limitations under the License. */ -#ifndef DLL_EXPORT +#if !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) # define USE_STATIC_LIB #endif @@ -41,16 +41,33 @@ #include #include -#ifndef WIN32 +#ifdef HAVE_SYS_TIME_H #include +#endif + +#ifdef HAVE_SYS_SOCKET_H #include +#endif + +#ifdef HAVE_POLL #include +#endif + +#ifdef HAVE_NETINET_IN_H #include #include +#endif + +#ifdef HAVE_ARPA_INET_H #include +#endif + +#ifdef HAVE_NETDB_H #include -#include -#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include // needed for _POSIX_MONOTONIC_CLOCK #endif #ifdef HAVE_SYS_UTSNAME_H @@ -61,6 +78,15 @@ #include #endif +#ifdef WIN32 +#define random rand /* replace POSIX random with Windows rand */ +#include /* for getpid */ +#include /* for getcwd */ +#define EAI_ADDRFAMILY WSAEINVAL /* is this still needed? */ +#define EHOSTDOWN EPIPE +#define ESTALE ENODEV +#endif + #define IF_DEBUG(x) if(logLevel==ZOO_LOG_LEVEL_DEBUG) {x;} const int ZOOKEEPER_WRITE = 1 << 0; @@ -1611,7 +1637,16 @@ int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, #endif rc = connect(zh->fd, (struct sockaddr*) &zh->addrs[zh->connect_index], sizeof(struct sockaddr_in)); #ifdef WIN32 - get_errno(); + errno = GetLastError(); + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + #if _MSC_VER >= 1600 switch (errno) { case WSAEWOULDBLOCK: diff --git a/src/c/zookeeper.sln b/src/c/zookeeper.sln deleted file mode 100644 index 42f41c95264..00000000000 --- a/src/c/zookeeper.sln +++ /dev/null @@ -1,25 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zookeeper", "zookeeper.vcproj", "{5754FB2B-5EA5-4988-851D-908CA533A626}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cli", "Cli.vcproj", "{050228F9-070F-4806-A2B5-E6B95D8EC4AF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.ActiveCfg = Debug|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Debug|Win32.Build.0 = Debug|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.ActiveCfg = Release|Win32 - {5754FB2B-5EA5-4988-851D-908CA533A626}.Release|Win32.Build.0 = Release|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Debug|Win32.Build.0 = Debug|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.ActiveCfg = Release|Win32 - {050228F9-070F-4806-A2B5-E6B95D8EC4AF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/c/zookeeper.vcproj b/src/c/zookeeper.vcproj deleted file mode 100644 index dc3ab43e3e6..00000000000 --- a/src/c/zookeeper.vcproj +++ /dev/null @@ -1,300 +0,0 @@ -??? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From be1409cc9a14ac2e28693e0e02a0ba6d9713565e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Sch=C3=BCttel?= Date: Tue, 1 Aug 2017 08:55:24 -0700 Subject: [PATCH 434/444] ZOOKEEPER-2614 ZOOKEEPER-1576: Port to branch 3.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a backport of ZOOKEEPER-1576 to the 3.4-line. When running Zookeeper as an ensemble in a dynamic environment such as Kubernetes, the DNS entry of a Zookeeper pod is apparently instantly purged as one of the nodes goes down. This leads to an UnknownHostException when interacting with the cluster, even though a healthy majority of nodes is still working. This behavior is also observed in a firewall situation as described in ZOOOKEEPER-1576. This fix catches and logs the UnkownHostException and continues trying the next node. Thanks to Vishal Khandelwal for providing the patch. Author: Thomas Schüttel Reviewers: Edward Ribeiro , Michael Han Closes #320 from tschuettel/ZOOKEEPER-2614 --- .../zookeeper/client/StaticHostProvider.java | 48 ++++++++++--------- .../test/StaticHostProviderTest.java | 33 +++++++++---- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java index 5754b275cc5..58e3e6b6d8e 100644 --- a/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java +++ b/src/java/main/org/apache/zookeeper/client/StaticHostProvider.java @@ -52,33 +52,35 @@ public final class StaticHostProvider implements HostProvider { * * @param serverAddresses * possibly unresolved ZooKeeper server addresses - * @throws UnknownHostException * @throws IllegalArgumentException * if serverAddresses is empty or resolves to an empty list */ - public StaticHostProvider(Collection serverAddresses) - throws UnknownHostException { + public StaticHostProvider(Collection serverAddresses) { for (InetSocketAddress address : serverAddresses) { - InetAddress ia = address.getAddress(); - InetAddress resolvedAddresses[] = InetAddress.getAllByName((ia!=null) ? ia.getHostAddress(): - address.getHostName()); - for (InetAddress resolvedAddress : resolvedAddresses) { - // If hostName is null but the address is not, we can tell that - // the hostName is an literal IP address. Then we can set the host string as the hostname - // safely to avoid reverse DNS lookup. - // As far as i know, the only way to check if the hostName is null is use toString(). - // Both the two implementations of InetAddress are final class, so we can trust the return value of - // the toString() method. - if (resolvedAddress.toString().startsWith("/") - && resolvedAddress.getAddress() != null) { - this.serverAddresses.add( - new InetSocketAddress(InetAddress.getByAddress( - address.getHostName(), - resolvedAddress.getAddress()), - address.getPort())); - } else { - this.serverAddresses.add(new InetSocketAddress(resolvedAddress.getHostAddress(), address.getPort())); - } + try { + InetAddress ia = address.getAddress(); + InetAddress resolvedAddresses[] = InetAddress.getAllByName((ia != null) ? ia.getHostAddress() : + address.getHostName()); + for (InetAddress resolvedAddress : resolvedAddresses) { + // If hostName is null but the address is not, we can tell that + // the hostName is an literal IP address. Then we can set the host string as the hostname + // safely to avoid reverse DNS lookup. + // As far as i know, the only way to check if the hostName is null is use toString(). + // Both the two implementations of InetAddress are final class, so we can trust the return value of + // the toString() method. + if (resolvedAddress.toString().startsWith("/") + && resolvedAddress.getAddress() != null) { + this.serverAddresses.add( + new InetSocketAddress(InetAddress.getByAddress( + address.getHostName(), + resolvedAddress.getAddress()), + address.getPort())); + } else { + this.serverAddresses.add(new InetSocketAddress(resolvedAddress.getHostAddress(), address.getPort())); + } + } + } catch (UnknownHostException e) { + LOG.error("Unable to connect to server: {}", address, e); } } diff --git a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java index aa78a4b20ed..8414be97887 100644 --- a/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java +++ b/src/java/test/org/apache/zookeeper/test/StaticHostProviderTest.java @@ -43,7 +43,7 @@ public class StaticHostProviderTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(StaticHostProviderTest.class); @Test - public void testNextGoesRound() throws UnknownHostException { + public void testNextGoesRound() { HostProvider hostProvider = getHostProvider((byte) 2); InetSocketAddress first = hostProvider.next(0); assertTrue(first instanceof InetSocketAddress); @@ -52,7 +52,7 @@ public void testNextGoesRound() throws UnknownHostException { } @Test - public void testNextGoesRoundAndSleeps() throws UnknownHostException { + public void testNextGoesRoundAndSleeps() { byte size = 2; HostProvider hostProvider = getHostProvider(size); while (size > 0) { @@ -66,7 +66,7 @@ public void testNextGoesRoundAndSleeps() throws UnknownHostException { } @Test - public void testNextDoesNotSleepForZero() throws UnknownHostException { + public void testNextDoesNotSleepForZero() { byte size = 2; HostProvider hostProvider = getHostProvider(size); while (size > 0) { @@ -87,7 +87,7 @@ public void testTwoConsequitiveCallsToNextReturnDifferentElement() } @Test - public void testOnConnectDoesNotReset() throws UnknownHostException { + public void testOnConnectDoesNotReset() { HostProvider hostProvider = getHostProvider((byte) 2); InetSocketAddress first = hostProvider.next(0); hostProvider.onConnected(); @@ -111,8 +111,26 @@ public void testLiteralIPNoReverseNS() throws Exception { } } - private StaticHostProvider getHostProviderUnresolved(byte size) - throws UnknownHostException { + @Test(expected = IllegalArgumentException.class) + public void testTwoInvalidHostAddresses() { + ArrayList list = new ArrayList(); + list.add(new InetSocketAddress("a", 2181)); + list.add(new InetSocketAddress("b", 2181)); + new StaticHostProvider(list); + } + + public void testOneInvalidHostAddresses() { + Collection addr = getUnresolvedServerAddresses((byte) 1); + addr.add(new InetSocketAddress("a", 2181)); + + StaticHostProvider sp = new StaticHostProvider(addr); + InetSocketAddress n1 = sp.next(0); + InetSocketAddress n2 = sp.next(0); + + assertEquals(n2, n1); + } + + private StaticHostProvider getHostProviderUnresolved(byte size) { return new StaticHostProvider(getUnresolvedServerAddresses(size)); } @@ -125,8 +143,7 @@ private Collection getUnresolvedServerAddresses(byte size) { return list; } - private StaticHostProvider getHostProvider(byte size) - throws UnknownHostException { + private StaticHostProvider getHostProvider(byte size) { ArrayList list = new ArrayList( size); while (size > 0) { From 1789e0d6f5d4dd170193f4c13fb381e98597db77 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Thu, 3 Aug 2017 08:54:08 -0700 Subject: [PATCH 435/444] ZOOKEEPER-2355: Ephemeral node is never deleted if follower fails while reading the proposal packet. This commit is a port of the commit ca22b3db19371f6b0f5507b7dd80b283cddc7700 from branch-3.5 to branch-3.4. Changes include a few interfaces that required by the test case. The test case itself is also updated so it works with 3.4 code base. arshadmohammad rakeshadr Could you please review this to close the loop of ZOOKEEPER-2355? Author: Michael Han Reviewers: Mohammad Arshad Closes #304 from hanm/ZOOKEEPER-2355 --- .../zookeeper/server/quorum/Learner.java | 8 +- .../zookeeper/server/quorum/QuorumPeer.java | 6 +- .../server/quorum/QuorumPeerMain.java | 33 ++- .../quorum/EphemeralNodeDeletionTest.java | 219 ++++++++++++++++++ .../server/quorum/QuorumPeerTestBase.java | 6 +- 5 files changed, 254 insertions(+), 18 deletions(-) create mode 100644 src/java/test/org/apache/zookeeper/server/quorum/EphemeralNodeDeletionTest.java diff --git a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java index c54f6e6b797..1645cda4a98 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/Learner.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/Learner.java @@ -333,7 +333,7 @@ protected void syncWithLeader(long newLeaderZxid) throws IOException, Interrupte snapshotNeeded = false; } else if (qp.getType() == Leader.SNAP) { - LOG.info("Getting a snapshot from leader"); + LOG.info("Getting a snapshot from leader 0x" + Long.toHexString(qp.getZxid())); // The leader is going to dump the database // clear our own database and read zk.getZKDatabase().clear(); @@ -343,6 +343,7 @@ else if (qp.getType() == Leader.SNAP) { LOG.error("Missing signature. Got " + signature); throw new IOException("Missing signature"); } + zk.getZKDatabase().setlastProcessedZxid(qp.getZxid()); } else if (qp.getType() == Leader.TRUNC) { //we need to truncate the log to the lastzxid of the leader LOG.warn("Truncating log to get in sync with the leader 0x" @@ -354,7 +355,7 @@ else if (qp.getType() == Leader.SNAP) { + Long.toHexString(qp.getZxid())); System.exit(13); } - + zk.getZKDatabase().setlastProcessedZxid(qp.getZxid()); } else { LOG.error("Got unexpected packet from leader " @@ -362,8 +363,7 @@ else if (qp.getType() == Leader.SNAP) { System.exit(13); } - zk.getZKDatabase().setlastProcessedZxid(qp.getZxid()); - zk.createSessionTracker(); + zk.createSessionTracker(); long lastQueued = 0; 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 53f607750f5..267f89ed22c 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java @@ -566,7 +566,7 @@ public static QuorumPeer testingQuorumPeer() throws SaslException { return new QuorumPeer(); } - private QuorumPeer() throws SaslException { + protected QuorumPeer() throws SaslException { super("QuorumPeer"); quorumStats = new QuorumStats(this); initialize(); @@ -1333,6 +1333,10 @@ public void setZKDatabase(ZKDatabase database) { this.zkDb = database; } + protected ZKDatabase getZkDb() { + return zkDb; + } + public void setRunning(boolean running) { this.running = running; } 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 9644ced1faf..da0df52140e 100644 --- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java +++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeerMain.java @@ -21,6 +21,7 @@ import java.io.IOException; import javax.management.JMException; +import javax.security.sasl.SaslException; import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; @@ -131,18 +132,21 @@ public void runFromConfig(QuorumPeerConfig config) throws IOException { ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns()); - - quorumPeer = new QuorumPeer(config.getServers(), - new File(config.getDataDir()), - new File(config.getDataLogDir()), - config.getElectionAlg(), - config.getServerId(), - config.getTickTime(), - config.getInitLimit(), - config.getSyncLimit(), - config.getQuorumListenOnAllIPs(), - cnxnFactory, - config.getQuorumVerifier()); + + quorumPeer = getQuorumPeer(); + + quorumPeer.setQuorumPeers(config.getServers()); + quorumPeer.setTxnFactory(new FileTxnSnapLog( + new File(config.getDataDir()), + new File(config.getDataLogDir()))); + quorumPeer.setElectionType(config.getElectionAlg()); + quorumPeer.setMyid(config.getServerId()); + quorumPeer.setTickTime(config.getTickTime()); + quorumPeer.setInitLimit(config.getInitLimit()); + quorumPeer.setSyncLimit(config.getSyncLimit()); + quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs()); + quorumPeer.setCnxnFactory(cnxnFactory); + quorumPeer.setQuorumVerifier(config.getQuorumVerifier()); quorumPeer.setClientPortAddress(config.getClientPortAddress()); quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout()); quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout()); @@ -170,4 +174,9 @@ public void runFromConfig(QuorumPeerConfig config) throws IOException { LOG.warn("Quorum Peer interrupted", e); } } + + // @VisibleForTesting + protected QuorumPeer getQuorumPeer() throws SaslException { + return new QuorumPeer(); + } } diff --git a/src/java/test/org/apache/zookeeper/server/quorum/EphemeralNodeDeletionTest.java b/src/java/test/org/apache/zookeeper/server/quorum/EphemeralNodeDeletionTest.java new file mode 100644 index 00000000000..c34f243c2a5 --- /dev/null +++ b/src/java/test/org/apache/zookeeper/server/quorum/EphemeralNodeDeletionTest.java @@ -0,0 +1,219 @@ +/** + * 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.server.quorum; + +import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.net.SocketTimeoutException; + +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.PortAssignment; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.persistence.FileTxnSnapLog; +import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; +import org.apache.zookeeper.test.ClientBase; +import org.apache.zookeeper.test.ClientBase.CountdownWatcher; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import javax.security.sasl.SaslException; + +public class EphemeralNodeDeletionTest extends QuorumPeerTestBase { + private static int SERVER_COUNT = 3; + private MainThread[] mt = new MainThread[SERVER_COUNT]; + + /** + * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2355. + * ZooKeeper ephemeral node is never deleted if follower fail while reading + * the proposal packet. + */ + + @Test(timeout = 120000) + public void testEphemeralNodeDeletion() throws Exception { + 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(); + sb.append(server + "\n"); + } + String currentQuorumCfgSection = sb.toString(); + System.out.println(currentQuorumCfgSection); + // start all the servers + for (int i = 0; i < SERVER_COUNT; i++) { + mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection) { + @Override + public TestQPMain getTestQPMain() { + return new MockTestQPMain(); + } + }; + mt[i].start(); + } + + // ensure all servers 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 watch = new CountdownWatcher(); + ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[1], + ClientBase.CONNECTION_TIMEOUT, watch); + watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + + /** + * now the problem scenario starts + */ + + // 1: create ephemeral node + String nodePath = "/e1"; + zk.create(nodePath, "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); + + // 2: inject network problem in one of the follower + CustomQuorumPeer follower = (CustomQuorumPeer) getByServerState(mt, + ServerState.FOLLOWING); + follower.setInjectError(true); + + // 3: close the session so that ephemeral node is deleted + zk.close(); + + // remove the error + follower.setInjectError(false); + + Assert.assertTrue("Faulted Follower should have joined quorum by now", + ClientBase.waitForServerUp( + "127.0.0.1:" + follower.getClientPort(), + CONNECTION_TIMEOUT)); + + QuorumPeer leader = getByServerState(mt, ServerState.LEADING); + assertNotNull("Leader should not be null", leader); + Assert.assertTrue("Leader must be running", ClientBase.waitForServerUp( + "127.0.0.1:" + leader.getClientPort(), CONNECTION_TIMEOUT)); + + watch = new CountdownWatcher(); + zk = new ZooKeeper("127.0.0.1:" + leader.getClientPort(), + ClientBase.CONNECTION_TIMEOUT, watch); + watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + + Stat exists = zk.exists(nodePath, false); + assertNull("Node must have been deleted from leader", exists); + + CountdownWatcher followerWatch = new CountdownWatcher(); + ZooKeeper followerZK = new ZooKeeper( + "127.0.0.1:" + follower.getClientPort(), + ClientBase.CONNECTION_TIMEOUT, followerWatch); + followerWatch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); + Stat nodeAtFollower = followerZK.exists(nodePath, false); + + // Problem 1: Follower had one extra ephemeral node /e1 + assertNull("ephemeral node must not exist", nodeAtFollower); + + // Create the node with another session + zk.create(nodePath, "2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); + + // close the session and newly created ephemeral node should be deleted + zk.close(); + + nodeAtFollower = followerZK.exists(nodePath, false); + + // Problem 2: Before fix, after session close the ephemeral node + // was not getting deleted. But now after the fix after session close + // ephemeral node is getting deleted. + assertNull("After session close ephemeral node must be deleted", + nodeAtFollower); + followerZK.close(); + } + + @After + public void tearDown() { + // stop all severs + for (int i = 0; i < mt.length; i++) { + try { + mt[i].shutdown(); + } catch (InterruptedException e) { + LOG.warn("Quorum Peer interrupted while shutting it down", e); + } + } + } + + private QuorumPeer getByServerState(MainThread[] mt, ServerState state) { + for (int i = mt.length - 1; i >= 0; i--) { + QuorumPeer quorumPeer = mt[i].getQuorumPeer(); + if (null != quorumPeer && state == quorumPeer.getPeerState()) { + return quorumPeer; + } + } + return null; + } + + static class CustomQuorumPeer extends QuorumPeer { + private boolean injectError = false; + + public CustomQuorumPeer() throws SaslException { + } + + @Override + protected Follower makeFollower(FileTxnSnapLog logFactory) + throws IOException { + return new Follower(this, new FollowerZooKeeperServer(logFactory, + this, null /*DataTreeBuilder is never used*/, + this.getZkDb())) { + + @Override + void readPacket(QuorumPacket pp) throws IOException { + /** + * In real scenario got SocketTimeoutException while reading + * the packet from leader because of network problem, but + * here throwing SocketTimeoutException based on whether + * error is injected or not + */ + super.readPacket(pp); + if (injectError && pp.getType() == Leader.PROPOSAL) { + String type = LearnerHandler.packetToString(pp); + throw new SocketTimeoutException( + "Socket timeout while reading the packet for operation " + + type); + } + } + + }; + } + + public void setInjectError(boolean injectError) { + this.injectError = injectError; + } + + } + + static class MockTestQPMain extends TestQPMain { + @Override + protected QuorumPeer getQuorumPeer() throws SaslException { + return new CustomQuorumPeer(); + } + } +} \ No newline at end of file diff --git a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java index 6d5eb47c45c..c19963c913c 100644 --- a/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java +++ b/src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java @@ -129,7 +129,7 @@ public MainThread(int myid, int clientPort, String quorumCfgSection) Thread currentThread; synchronized public void start() { - main = new TestQPMain(); + main = getTestQPMain(); currentThread = new Thread(this); currentThread.start(); mainFailed = new CountDownLatch(1); @@ -202,5 +202,9 @@ public Map getOtherConfigs() { public File getConfFile() { return confFile; } + + public TestQPMain getTestQPMain() { + return new TestQPMain(); + } } } From 7294f8b1b260c76fc6cdd5d3f6e5125c4e9577b3 Mon Sep 17 00:00:00 2001 From: Sun Qi Date: Thu, 3 Aug 2017 09:22:54 -0700 Subject: [PATCH 436/444] ZOOKEEPER-1669: Operations to server will be timed-out while thousands of sessions expired same time The issue is raised while tens thousands of clients try to reconnect ZooKeeper service. Actually, we came across the issue during maintaining our HBase cluster, which used a 5-server ZooKeeper cluster. The HBase cluster was composed of many many regionservers (in thousand order of magnitude), and connected by tens thousands of clients to do massive reads/writes. Because the r/w throughput is very high, ZooKeeper zxid increased quickly as well. Basically, each two or three weeks, Zookeeper would make leader relection triggered by the zxid roll over. The leader relection will cause the clients(HBase regionservers and HBase clients) disconnected and reconnected with Zookeeper servers in the mean time, and try to renew the sessions. In current implementation of session renew, NIOServerCnxnFactory will clone all the connections at first in order to avoid race condition in multi-threads and go iterate the cloned connection set one by one to find the related session to renew. It's very time consuming. In our case (described above), it caused many region servers can't successfully renew session before session timeout, and eventually the HBase cluster lose these region servers and affect the HBase stability. The change is to make refactoring to the close session logic and introduce a ConcurrentHashMap to store session id and connection map relation, which is a thread-safe data structure and eliminate the necessary to clone the connection set at first. Author: Sun Qi Reviewers: Edward Ribeiro , Michael Han Closes #312 from CheneySun/branch-3.4 --- .../zookeeper/server/NIOServerCnxn.java | 49 +++++++++---------- .../server/NIOServerCnxnFactory.java | 42 +++++++++++----- .../zookeeper/server/NettyServerCnxn.java | 32 +++++------- .../server/NettyServerCnxnFactory.java | 41 +++++++++++----- .../apache/zookeeper/server/ServerCnxn.java | 6 +++ .../zookeeper/server/ServerCnxnFactory.java | 9 ++++ 6 files changed, 108 insertions(+), 71 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java index 456d4c2f14b..52d0626853f 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java @@ -222,6 +222,15 @@ protected boolean isSocketOpen() { return sock.isOpen(); } + @Override + public InetAddress getSocketAddress() { + if (sock == null) { + return null; + } + + return sock.socket().getInetAddress(); + } + /** * Handles read/write IO on connection. */ @@ -453,7 +462,7 @@ private void cleanupWriterSocket(PrintWriter pwriter) { } } } - + /** * This class wraps the sendBuffer method of NIOServerCnxn. It is * responsible for chunking up the response to a client. Rather @@ -1000,34 +1009,21 @@ public String toString() { */ @Override public void close() { - synchronized(factory.cnxns){ - // if this is not in cnxns then it's already closed - if (!factory.cnxns.remove(this)) { - return; - } + factory.removeCnxn(this); - synchronized (factory.ipMap) { - Set s = - factory.ipMap.get(sock.socket().getInetAddress()); - s.remove(this); - } + if (zkServer != null) { + zkServer.removeCnxn(this); + } - factory.unregisterConnection(this); + closeSock(); - if (zkServer != null) { - zkServer.removeCnxn(this); - } - - closeSock(); - - if (sk != null) { - try { - // need to cancel this selection key from the selector - sk.cancel(); - } catch (Exception e) { - if (LOG.isDebugEnabled()) { - LOG.debug("ignoring exception during selectionkey cancel", e); - } + if (sk != null) { + try { + // need to cancel this selection key from the selector + sk.cancel(); + } catch (Exception e) { + if (LOG.isDebugEnabled()) { + LOG.debug("ignoring exception during selectionkey cancel", e); } } } @@ -1168,6 +1164,7 @@ public long getSessionId() { @Override public void setSessionId(long sessionId) { this.sessionId = sessionId; + this.factory.addSession(sessionId, this); } @Override diff --git a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java index bd86cdd2df3..d7581a4cf7d 100644 --- a/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java @@ -151,6 +151,29 @@ private void addCnxn(NIOServerCnxn cnxn) { } } + public void removeCnxn(NIOServerCnxn cnxn) { + synchronized(cnxns) { + // Remove the related session from the sessionMap. + long sessionId = cnxn.getSessionId(); + if (sessionId != 0) { + sessionMap.remove(sessionId); + } + + // if this is not in cnxns then it's already closed + if (!cnxns.remove(cnxn)) { + return; + } + + synchronized (ipMap) { + Set s = + ipMap.get(cnxn.getSocketAddress()); + s.remove(cnxn); + } + + unregisterConnection(cnxn); + } + } + protected NIOServerCnxn createConnection(SocketChannel sock, SelectionKey sk) throws IOException { return new NIOServerCnxn(zkServer, sock, sk, this); @@ -275,19 +298,12 @@ public synchronized void closeSession(long sessionId) { @SuppressWarnings("unchecked") private void closeSessionWithoutWakeup(long sessionId) { - HashSet cnxns; - synchronized (this.cnxns) { - cnxns = (HashSet)this.cnxns.clone(); - } - - for (NIOServerCnxn cnxn : cnxns) { - if (cnxn.getSessionId() == sessionId) { - try { - cnxn.close(); - } catch (Exception e) { - LOG.warn("exception during session close", e); - } - break; + NIOServerCnxn cnxn = (NIOServerCnxn) sessionMap.remove(sessionId); + if (cnxn != null) { + try { + cnxn.close(); + } catch (Exception e) { + LOG.warn("exception during session close", e); } } } diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java index 99058d1fc39..a1ca54b6cf8 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; @@ -95,26 +96,7 @@ public void close() { // connection bean leak under certain race conditions. factory.unregisterConnection(this); - synchronized(factory.cnxns){ - // if this is not in cnxns then it's already closed - if (!factory.cnxns.remove(this)) { - if (LOG.isDebugEnabled()) { - LOG.debug("cnxns size:" + factory.cnxns.size()); - } - return; - } - if (LOG.isDebugEnabled()) { - LOG.debug("close in progress for sessionid:0x" - + Long.toHexString(sessionId)); - } - - synchronized (factory.ipMap) { - Set s = - factory.ipMap.get(((InetSocketAddress)channel - .getRemoteAddress()).getAddress()); - s.remove(this); - } - } + factory.removeCnxn(this); if (channel.isOpen()) { channel.close(); @@ -204,6 +186,7 @@ public void sendResponse(ReplyHeader h, Record r, String tag) @Override public void setSessionId(long sessionId) { this.sessionId = sessionId; + factory.addSession(sessionId, this); } @Override @@ -227,6 +210,15 @@ public void sendBuffer(ByteBuffer sendBuffer) { packetSent(); } + @Override + public InetAddress getSocketAddress() { + if (channel == null) { + return null; + } + + return ((InetSocketAddress)(channel.getRemoteAddress())).getAddress(); + } + /** * clean up the socket related to a command and also make sure we flush the * data before we do that diff --git a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java index 2d3c93fd9a2..a34a398b2d4 100644 --- a/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java @@ -287,18 +287,13 @@ public void closeSession(long sessionId) { if (LOG.isDebugEnabled()) { LOG.debug("closeSession sessionid:0x" + sessionId); } - NettyServerCnxn[] allCnxns = null; - synchronized (cnxns) { - allCnxns = cnxns.toArray(new NettyServerCnxn[cnxns.size()]); - } - for (NettyServerCnxn cnxn : allCnxns) { - if (cnxn.getSessionId() == sessionId) { - try { - cnxn.close(); - } catch (Exception e) { - LOG.warn("exception during session close", e); - } - break; + + NettyServerCnxn cnxn = (NettyServerCnxn) sessionMap.remove(sessionId); + if (cnxn != null) { + try { + cnxn.close(); + } catch (Exception e) { + LOG.warn("exception during session close", e); } } } @@ -402,4 +397,26 @@ private void addCnxn(NettyServerCnxn cnxn) { } } + public void removeCnxn(ServerCnxn cnxn) { + synchronized(cnxns){ + // if this is not in cnxns then it's already closed + if (!cnxns.remove(cnxn)) { + if (LOG.isDebugEnabled()) { + LOG.debug("cnxns size:" + cnxns.size()); + } + return; + } + if (LOG.isDebugEnabled()) { + LOG.debug("close in progress for sessionid:0x" + + Long.toHexString(cnxn.getSessionId())); + } + + synchronized (ipMap) { + Set s = + ipMap.get(cnxn.getSocketAddress()); + s.remove(cnxn); + } + } + } + } diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java index ad259e3fe5d..f3dc8e2ff1a 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxn.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxn.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -101,6 +102,11 @@ public boolean removeAuthInfo(Id id) { abstract void setSessionTimeout(int sessionTimeout); + /** + * Wrapper method to return the socket address + */ + public abstract InetAddress getSocketAddress(); + protected ZooKeeperSaslServer zooKeeperSaslServer = null; protected static class CloseRequestException extends IOException { diff --git a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java index 2a67ec56ee4..c37d83be975 100644 --- a/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java +++ b/src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; @@ -48,6 +49,10 @@ public interface PacketProcessor { private static final Logger LOG = LoggerFactory.getLogger(ServerCnxnFactory.class); + // sessionMap is used to speed up closeSession() + protected final ConcurrentMap sessionMap = + new ConcurrentHashMap(); + /** * The buffer will cause the connection to be close when we do a send. */ @@ -158,6 +163,10 @@ public void registerConnection(ServerCnxn serverCnxn) { } + public void addSession(long sessionId, ServerCnxn cnxn) { + sessionMap.put(sessionId, cnxn); + } + /** * Initialize the server SASL if specified. * From e4303a37a813c9f1bd4cdefd9c754267b12c32b4 Mon Sep 17 00:00:00 2001 From: Michael Han Date: Thu, 3 Aug 2017 14:02:29 -0700 Subject: [PATCH 437/444] ZOOKEEPER-2853: The lastZxidSeen in FileTxnLog.java is never being assigned. This is a port of the same patch committed to master and branch-3.5, after resolving merge conflicts. --- .../server/persistence/FileTxnLog.java | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java index 690cfca6aaa..724b8551874 100644 --- a/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java +++ b/src/java/main/org/apache/zookeeper/server/persistence/FileTxnLog.java @@ -192,44 +192,48 @@ public synchronized void close() throws IOException { public synchronized boolean append(TxnHeader hdr, Record txn) throws IOException { - if (hdr != null) { - if (hdr.getZxid() <= lastZxidSeen) { - LOG.warn("Current zxid " + hdr.getZxid() - + " is <= " + lastZxidSeen + " for " - + hdr.getType()); - } - if (logStream==null) { - if(LOG.isInfoEnabled()){ - LOG.info("Creating new log file: log." + - Long.toHexString(hdr.getZxid())); - } - - logFileWrite = new File(logDir, ("log." + - Long.toHexString(hdr.getZxid()))); - fos = new FileOutputStream(logFileWrite); - logStream=new BufferedOutputStream(fos); - oa = BinaryOutputArchive.getArchive(logStream); - FileHeader fhdr = new FileHeader(TXNLOG_MAGIC,VERSION, dbId); - fhdr.serialize(oa, "fileheader"); - // Make sure that the magic number is written before padding. - logStream.flush(); - currentSize = fos.getChannel().position(); - streamsToFlush.add(fos); - } - padFile(fos); - byte[] buf = Util.marshallTxnEntry(hdr, txn); - if (buf == null || buf.length == 0) { - throw new IOException("Faulty serialization for header " + - "and txn"); + if (hdr == null) { + return false; + } + + if (hdr.getZxid() <= lastZxidSeen) { + LOG.warn("Current zxid " + hdr.getZxid() + + " is <= " + lastZxidSeen + " for " + + hdr.getType()); + } else { + lastZxidSeen = hdr.getZxid(); + } + + if (logStream==null) { + if(LOG.isInfoEnabled()){ + LOG.info("Creating new log file: log." + + Long.toHexString(hdr.getZxid())); } - Checksum crc = makeChecksumAlgorithm(); - crc.update(buf, 0, buf.length); - oa.writeLong(crc.getValue(), "txnEntryCRC"); - Util.writeTxnBytes(oa, buf); - - return true; + + logFileWrite = new File(logDir, ("log." + + Long.toHexString(hdr.getZxid()))); + fos = new FileOutputStream(logFileWrite); + logStream=new BufferedOutputStream(fos); + oa = BinaryOutputArchive.getArchive(logStream); + FileHeader fhdr = new FileHeader(TXNLOG_MAGIC,VERSION, dbId); + fhdr.serialize(oa, "fileheader"); + // Make sure that the magic number is written before padding. + logStream.flush(); + currentSize = fos.getChannel().position(); + streamsToFlush.add(fos); } - return false; + padFile(fos); + byte[] buf = Util.marshallTxnEntry(hdr, txn); + if (buf == null || buf.length == 0) { + throw new IOException("Faulty serialization for header " + + "and txn"); + } + Checksum crc = makeChecksumAlgorithm(); + crc.update(buf, 0, buf.length); + oa.writeLong(crc.getValue(), "txnEntryCRC"); + Util.writeTxnBytes(oa, buf); + + return true; } /** From 4beaa69b59514ab11a559876edbacf79b8af73f2 Mon Sep 17 00:00:00 2001 From: Abraham Fine Date: Thu, 10 Aug 2017 13:04:35 -0700 Subject: [PATCH 438/444] ZOOKEEPER-2864: Add script to run a java api compatibility tool For example: `python check_compatibility.py branch-3.5..branch-3.4 ` Author: Abraham Fine Reviewers: Michael Han Closes #329 from afine/ZOOKEEPER-2864 (cherry picked from commit 82be7f63c69b8aeecf80106c5e637bd80b64657e) Signed-off-by: Michael Han --- src/java/test/bin/check_compatibility.py | 204 +++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/java/test/bin/check_compatibility.py diff --git a/src/java/test/bin/check_compatibility.py b/src/java/test/bin/check_compatibility.py new file mode 100644 index 00000000000..cad8195942f --- /dev/null +++ b/src/java/test/bin/check_compatibility.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# 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. + +# Script which checks Java API compatibility between two revisions of the +# Java client. +# +# Based on the compatibility checker from the HBase project, but ported to +# Python for better readability. + +# Lifted from Kudu: https://github.com/apache/kudu/blob/master/build-support/check_compatibility.py + +import logging +import optparse +import os +import shutil +import subprocess +import sys + +JAVA_ACC_GIT_URL = "https://github.com/lvc/japi-compliance-checker.git" + +# The annotations for what we consider our public API. +PUBLIC_ANNOTATIONS = ["org.apache.yetus.audience.InterfaceAudience.LimitedPrivate", + "org.apache.yetus.audience.InterfaceAudience.Public"] + +# Various relative paths +PATH_TO_REPO_DIR = "../../../../" +PATH_TO_BUILD_DIR = PATH_TO_REPO_DIR + "build/compat-check" +PATH_TO_JACC_DIR = PATH_TO_REPO_DIR + "build/jacc" + +def check_output(*popenargs, **kwargs): + # r"""Run command with arguments and return its output as a byte string. + # Backported from Python 2.7 as it's implemented as pure python on stdlib. + # >>> check_output(['/usr/bin/python', '--version']) + # Python 2.6.2 + # """ + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + error = subprocess.CalledProcessError(retcode, cmd) + error.output = output + raise error + return output + +def get_repo_dir(): + """ Return the path to the top of the repo. """ + dirname, _ = os.path.split(os.path.abspath(__file__)) + return os.path.abspath(os.path.join(dirname, PATH_TO_REPO_DIR)) + + +def get_scratch_dir(): + """ Return the path to the scratch dir that we build within. """ + dirname, _ = os.path.split(os.path.abspath(__file__)) + return os.path.abspath(os.path.join(dirname, PATH_TO_BUILD_DIR)) + + +def get_java_acc_dir(): + dirname, _ = os.path.split(os.path.abspath(__file__)) + return os.path.abspath(os.path.join(dirname, PATH_TO_JACC_DIR)) + + +def clean_scratch_dir(scratch_dir): + """ Clean up and re-create the scratch directory. """ + if os.path.exists(scratch_dir): + logging.info("Removing scratch dir %s...", scratch_dir) + shutil.rmtree(scratch_dir) + logging.info("Creating empty scratch dir %s...", scratch_dir) + os.makedirs(scratch_dir) + + +def checkout_tree(rev, path): + """ Check out the Java source tree for the given revision into the given path. """ + logging.info("Checking out %s in %s", rev, path) + os.makedirs(path) + # Extract java source + subprocess.check_call(["bash", '-o', 'pipefail', "-c", + ("git archive --format=tar %s | " + + "tar -C \"%s\" -xf -") % (rev, path)], + cwd=get_repo_dir()) + + +def get_git_hash(revname): + """ Convert 'revname' to its SHA-1 hash. """ + return check_output(["git", "rev-parse", revname], + cwd=get_repo_dir()).strip() + + +def build_tree(path): + """ Run the Java build within 'path'. """ + logging.info("Building in %s...", path) + subprocess.check_call(["ant", "jar"], cwd=path) + + +def checkout_java_acc(force): + """ + Check out the Java API Compliance Checker. If 'force' is true, will re-download even if the + directory exists. + """ + acc_dir = get_java_acc_dir() + if os.path.exists(acc_dir): + logging.info("Java JAVA_ACC is already downloaded.") + if not force: + return + logging.info("Forcing re-download.") + shutil.rmtree(acc_dir) + logging.info("Checking out Java JAVA_ACC...") + subprocess.check_call(["git", "clone", "-b", "2.1", "--single-branch", "--depth=1", JAVA_ACC_GIT_URL, acc_dir]) + + +def find_client_jars(path): + """ Return a list of jars within 'path' to be checked for compatibility. """ + return check_output(["find", path, "-name", "zookeeper*.jar"]).rstrip('\n') + + +def run_java_acc(src_name, src, dst_name, dst): + """ Run the compliance checker to compare 'src' and 'dst'. """ + src_jar = find_client_jars(src) + dst_jar = find_client_jars(dst) + logging.info("Will check compatibility between original jars:\n%s\n" + + "and new jars:\n%s", + src_jar, dst_jar) + + annotations_path = os.path.join(get_scratch_dir(), "annotations.txt") + with file(annotations_path, "w") as f: + for ann in PUBLIC_ANNOTATIONS: + print >>f, ann + + java_acc_path = os.path.join(get_java_acc_dir(), "japi-compliance-checker.pl") + + out_path = os.path.join(get_scratch_dir(), "report.html") + subprocess.check_call(["perl", java_acc_path, + "-lib", "ZooKeeper", + "-v1", src_name, + "-v2", dst_name, + "-d1", src_jar, + "-d2", dst_jar, + "-annotations-list", annotations_path, + "-report-path", out_path]) + + +def main(argv): + logging.basicConfig(level=logging.INFO) + parser = optparse.OptionParser( + usage="usage: %prog SRC..[DST]") + parser.add_option("-f", "--force-download", dest="force_download_deps", + help=("Download dependencies (i.e. Java JAVA_ACC) even if they are " + + "already present")) + opts, args = parser.parse_args() + + if len(args) != 1: + parser.error("no src/dst revision specified") + sys.exit(1) + + src_rev, dst_rev = args[0].split("..", 1) + if dst_rev == "": + dst_rev = "HEAD" + src_rev = get_git_hash(src_rev) + dst_rev = get_git_hash(dst_rev) + + logging.info("Source revision: %s", src_rev) + logging.info("Destination revision: %s", dst_rev) + + # Download deps. + checkout_java_acc(opts.force_download_deps) + + # Set up the build. + scratch_dir = get_scratch_dir() + clean_scratch_dir(scratch_dir) + + # Check out the src and dst source trees. + src_dir = os.path.join(scratch_dir, "src") + dst_dir = os.path.join(scratch_dir, "dst") + checkout_tree(src_rev, src_dir) + checkout_tree(dst_rev, dst_dir) + + # Run the build in each. + build_tree(src_dir) + build_tree(dst_dir) + + run_java_acc(src_rev, src_dir + "/build", + dst_rev, dst_dir + "/build") + + +if __name__ == "__main__": + main(sys.argv) \ No newline at end of file From 9959b0e4387c02e82fadb0aee8d59f6212a2757e Mon Sep 17 00:00:00 2001 From: Fangmin Lyu Date: Thu, 10 Aug 2017 13:14:22 -0700 Subject: [PATCH 439/444] ZOOKEEPER-2870: Improve the efficiency of AtomicFileOutputStream Author: Fangmin Lyu Reviewers: Michael Han Closes #331 from lvfangmin/ZOOKEEPER-2870 (cherry picked from commit 0c5b320060bdda854b530dc8a22993ba8cbbd655) Signed-off-by: Michael Han --- .../zookeeper/common/AtomicFileOutputStream.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/java/main/org/apache/zookeeper/common/AtomicFileOutputStream.java b/src/java/main/org/apache/zookeeper/common/AtomicFileOutputStream.java index ecfcad7c4b5..2584d3f873a 100644 --- a/src/java/main/org/apache/zookeeper/common/AtomicFileOutputStream.java +++ b/src/java/main/org/apache/zookeeper/common/AtomicFileOutputStream.java @@ -35,10 +35,10 @@ * A FileOutputStream that has the property that it will only show up at its * destination once it has been entirely written and flushed to disk. While * being written, it will use a .tmp suffix. - * + * * When the output stream is closed, it is flushed, fsynced, and will be moved * into place, overwriting any file that already exists at that location. - * + * * NOTE: on Windows platforms, it will not atomically replace the target * file - instead the target file is deleted before this one is moved into * place. @@ -63,6 +63,17 @@ public AtomicFileOutputStream(File f) throws FileNotFoundException { .getAbsoluteFile(); } + /** + * The default write method in FilterOutputStream does not call the write + * method of its underlying input stream with the same arguments. Instead + * it writes the data byte by byte, override it here to make it more + * efficient. + */ + @Override + public void write(byte b[], int off, int len) throws IOException { + out.write(b, off, len); + } + @Override public void close() throws IOException { boolean triedToClose = false, success = false; From 1f811a6281090e1b24152dc51507aa6a2bdeafe3 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Sat, 12 Aug 2017 22:21:25 -0700 Subject: [PATCH 440/444] ZOOKEEPER-2859: Fix CMake build on OS X. The libraries `libm` and `librt` (providing `math.h` and `clock_gettime` respectively) cannot be linked on OS X. Instead, this functionality is "free" with `libSystem`. Note that the `hashtable` library has the dependency on `libm`, not the `zookeeper` library. This was an error carried over from the Autotools build. Also finishes a TODO for `pthread` by importing it using the `FindThreads` CMake package. Author: Andrew Schwartzmeyer Reviewers: Michael Han Closes #319 from andschwa/macos (cherry picked from commit 5bcffe9bc242d825ad97ff4f9d2dea6476983a9a) Signed-off-by: Michael Han --- src/c/CMakeLists.txt | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/c/CMakeLists.txt b/src/c/CMakeLists.txt index 50f50fc46c8..a69cd7797ea 100644 --- a/src/c/CMakeLists.txt +++ b/src/c/CMakeLists.txt @@ -45,10 +45,11 @@ if(WANT_SYNCAPI) endif() # CppUnit option -if(WIN32) - # The tests do not yet compile on Windows, so we set this to off by default. +if(WIN32 OR APPLE) + # The tests do not yet compile on Windows or macOS, + # so we set this to off by default. # - # Note that CMake can does not have expressions except in conditionals, + # Note that CMake does not have expressions except in conditionals, # so we're left with this if/else/endif pattern. set(DEFAULT_WANT_CPPUNIT OFF) else() @@ -147,6 +148,7 @@ configure_file(cmake_config.h.in ${CMAKE_SOURCE_DIR}/include/config.h) # hashtable library set(hashtable_sources src/hashtable/hashtable_itr.c src/hashtable/hashtable.c) add_library(hashtable STATIC ${hashtable_sources}) +target_link_libraries(hashtable PUBLIC $<$:m>) # zookeeper library set(zookeeper_sources @@ -167,17 +169,14 @@ if(WIN32) endif() add_library(zookeeper STATIC ${zookeeper_sources}) -target_link_libraries(zookeeper PUBLIC hashtable) -if(UNIX) - target_link_libraries(zookeeper PUBLIC m rt) -elseif(WIN32) - # Link to Winsock 2.0 - target_link_libraries(zookeeper PUBLIC ws2_32) -endif() +target_link_libraries(zookeeper PUBLIC + hashtable + $<$:rt> # clock_gettime + $<$:ws2_32>) # Winsock 2.0 if(WANT_SYNCAPI AND NOT WIN32) - # TODO: Use `find_library()` or `FindThreads()` for `pthread`. - target_link_libraries(zookeeper PUBLIC pthread) + find_package(Threads REQUIRED) + target_link_libraries(zookeeper PUBLIC Threads::Threads) endif() # cli executable From b903a07c4944cb0a90045e686b7c3f153aee6153 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 17 Aug 2017 21:20:49 -0700 Subject: [PATCH 441/444] ZOOKEEPER-2874: Windows Debug builds don't link with `/MTd` When building in Debug configuration, this logic ensures that `/MTd` is used instead of just `/MT`, which on Windows means to link to the multi-threaded (debug) version of the standard library. When the user does not add `/MT` as a compile option manually, CMake would otherwise link to the correct one. Because we are overriding it for threaded compilations, we also must ensure that Debug configurations are specially handled. Furthermore, this must be done using a generator expression over configuration time logic because the Visual Studio CMake generators are "multi-configuration generators", that is, the configuration is chosen at build time instead of compile time. The generator expression handles this scenario, but checking `CMAKE_BUILD_TYPE` would not. Author: Andrew Schwartzmeyer Reviewers: Michael Han Closes #335 from andschwa/ZOOKEEPER-2874 (cherry picked from commit ab182d4561f1c6725af0e89e0b76d92186732195) Signed-off-by: Michael Han --- src/c/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/c/CMakeLists.txt b/src/c/CMakeLists.txt index a69cd7797ea..6219fc4cc75 100644 --- a/src/c/CMakeLists.txt +++ b/src/c/CMakeLists.txt @@ -40,7 +40,9 @@ option(WANT_SYNCAPI "Enables Sync API support" ON) if(WANT_SYNCAPI) add_definitions(-DTHREADED) if(WIN32) - add_compile_options(/MT) + # Note that the generator expression ensures that `/MTd` is used when Debug + # configurations are built. + add_compile_options(/MT$<$:d>) endif() endif() From 955611e294151fee77b5b95829ebf117c65317ed Mon Sep 17 00:00:00 2001 From: "yaniv.kunda" Date: Fri, 18 Aug 2017 15:30:06 -0700 Subject: [PATCH 442/444] ZOOKEEPER-2861: Main-Class JAR manifest attribute is incorrect Author: yaniv.kunda Reviewers: Abe Fine , Michael Han Closes #323 from ykunda/master (cherry picked from commit 09742f2d050105689592ffb5f5240ebde6f79dee) Signed-off-by: Michael Han --- build.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.xml b/build.xml index 0c3e09285ef..233bfa15d0b 100644 --- a/build.xml +++ b/build.xml @@ -552,7 +552,7 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> - + @@ -593,7 +593,7 @@ xmlns:cs="antlib:com.puppycrawl.tools.checkstyle"> - + From 70797397f12c8a9cc04895d7ca3459f7c7134f7d Mon Sep 17 00:00:00 2001 From: Manoj Mallela Date: Wed, 23 Aug 2017 17:11:04 -0700 Subject: [PATCH 443/444] ZOOKEEPER-2880: Rename README.txt to README.md - Change format of README.txt to markdown. Author: Manoj Mallela Reviewers: Michael Han Closes #341 from manojmallela/master (cherry picked from commit 86577c9d81bd40067bc084e49a7172884ac9ae49) Signed-off-by: Michael Han --- README.txt => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README.txt => README.md (100%) diff --git a/README.txt b/README.md similarity index 100% rename from README.txt rename to README.md From 2dc3664e52a0f1ae82c5c4fdc800921548bfc087 Mon Sep 17 00:00:00 2001 From: xoiss Date: Tue, 5 Sep 2017 14:28:36 +0300 Subject: [PATCH 444/444] ZOOKEEPER-2890 - fix freing by uninitialized address. --- src/c/src/zookeeper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c index a8fef67a61b..f15e9e7c7ac 100644 --- a/src/c/src/zookeeper.c +++ b/src/c/src/zookeeper.c @@ -2107,6 +2107,7 @@ static void deserialize_response(int type, int xid, int failed, int rc, completi cptr->c.string_result(rc, 0, cptr->data); } else { struct CreateResponse res; + memset(&res, 0, sizeof(res)); deserialize_CreateResponse(ia, "reply", &res); cptr->c.string_result(rc, res.path, cptr->data); deallocate_CreateResponse(&res);