Skip to content

Commit 82f2e53

Browse files
authored
CURATOR-704. Add server compatibility check support (#497)
Add new interface ZookeeperCompatibility to represent server compatibility and the existing Compatibility class (which represents client compatibility). Enhance CuratorFramework to accept ZookeeperCompatibility instance, allowing users to specify which server version to target (default is LATEST).
1 parent 972fffa commit 82f2e53

File tree

8 files changed

+119
-8
lines changed

8 files changed

+119
-8
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.curator.utils;
21+
22+
/**
23+
* Describe feature supports based on server compatibility (as opposed to
24+
* {@code Compatibility} which represents client compatibility.
25+
*/
26+
public class ZookeeperCompatibility {
27+
/**
28+
* Represent latest version with all features enabled
29+
*/
30+
public static final ZookeeperCompatibility LATEST =
31+
builder().hasPersistentWatchers(true).build();
32+
33+
public static Builder builder() {
34+
return new Builder();
35+
}
36+
37+
public static class Builder {
38+
// List of features introduced by Zookeeper over time.
39+
// All values are set to false by default for backward compatibility
40+
private boolean hasPersistentWatchers = false;
41+
42+
public Builder hasPersistentWatchers(boolean value) {
43+
this.hasPersistentWatchers = value;
44+
return this;
45+
}
46+
47+
public boolean hasPersistentWatchers() {
48+
return this.hasPersistentWatchers;
49+
}
50+
51+
public ZookeeperCompatibility build() {
52+
return new ZookeeperCompatibility(this);
53+
}
54+
}
55+
56+
private final boolean hasPersistentWatchers;
57+
58+
private ZookeeperCompatibility(Builder builder) {
59+
this.hasPersistentWatchers = builder.hasPersistentWatchers;
60+
}
61+
62+
/**
63+
* Check if both client and server support persistent watchers
64+
*
65+
* @return {@code true} if both the client library and the server version
66+
* support persistent watchers
67+
*/
68+
public boolean hasPersistentWatchers() {
69+
return this.hasPersistentWatchers && Compatibility.hasPersistentWatchers();
70+
}
71+
}

curator-framework/src/main/java/org/apache/curator/framework/CuratorFramework.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.apache.curator.framework.state.ConnectionStateErrorPolicy;
3535
import org.apache.curator.framework.state.ConnectionStateListener;
3636
import org.apache.curator.utils.EnsurePath;
37+
import org.apache.curator.utils.ZookeeperCompatibility;
3738
import org.apache.zookeeper.Watcher;
3839
import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
3940

@@ -262,6 +263,13 @@ public interface CuratorFramework extends Closeable {
262263
*/
263264
public CuratorZookeeperClient getZookeeperClient();
264265

266+
/**
267+
* Return zookeeper server compatibility
268+
*
269+
* @return compatibility
270+
*/
271+
public ZookeeperCompatibility getZookeeperCompatibility();
272+
265273
/**
266274
* Allocates an ensure path instance that is namespace aware
267275
*

curator-framework/src/main/java/org/apache/curator/framework/CuratorFrameworkFactory.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.apache.curator.framework.state.ConnectionStateListenerManagerFactory;
4747
import org.apache.curator.framework.state.StandardConnectionStateErrorPolicy;
4848
import org.apache.curator.utils.DefaultZookeeperFactory;
49+
import org.apache.curator.utils.ZookeeperCompatibility;
4950
import org.apache.curator.utils.ZookeeperFactory;
5051
import org.apache.zookeeper.CreateMode;
5152
import org.apache.zookeeper.Watcher;
@@ -174,6 +175,7 @@ public static class Builder {
174175
ConnectionStateListenerManagerFactory.standard;
175176
private int simulatedSessionExpirationPercent = 100;
176177
private ZKClientConfig zkClientConfig;
178+
private ZookeeperCompatibility zookeeperCompatibility = ZookeeperCompatibility.LATEST;
177179

178180
/**
179181
* Apply the current values and build a new CuratorFramework
@@ -519,6 +521,11 @@ public Builder connectionStateListenerManagerFactory(
519521
return this;
520522
}
521523

524+
public Builder zookeeperCompatibility(ZookeeperCompatibility zookeeperCompatibility) {
525+
this.zookeeperCompatibility = zookeeperCompatibility;
526+
return this;
527+
}
528+
522529
public Executor getRunSafeService() {
523530
return runSafeService;
524531
}
@@ -640,6 +647,10 @@ public ConnectionStateListenerManagerFactory getConnectionStateListenerManagerFa
640647
return connectionStateListenerManagerFactory;
641648
}
642649

650+
public ZookeeperCompatibility getZookeeperCompatibility() {
651+
return zookeeperCompatibility;
652+
}
653+
643654
private Builder() {}
644655
}
645656

curator-framework/src/main/java/org/apache/curator/framework/imps/CuratorFrameworkImpl.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@
7777
import org.apache.curator.framework.state.ConnectionStateErrorPolicy;
7878
import org.apache.curator.framework.state.ConnectionStateListener;
7979
import org.apache.curator.framework.state.ConnectionStateManager;
80-
import org.apache.curator.utils.Compatibility;
8180
import org.apache.curator.utils.DebugUtils;
8281
import org.apache.curator.utils.EnsurePath;
8382
import org.apache.curator.utils.ThreadUtils;
8483
import org.apache.curator.utils.ZKPaths;
84+
import org.apache.curator.utils.ZookeeperCompatibility;
8585
import org.apache.curator.utils.ZookeeperFactory;
8686
import org.apache.zookeeper.KeeperException;
8787
import org.apache.zookeeper.WatchedEvent;
@@ -117,6 +117,7 @@ public class CuratorFrameworkImpl implements CuratorFramework {
117117
private final EnsembleTracker ensembleTracker;
118118
private final SchemaSet schemaSet;
119119
private final Executor runSafeService;
120+
private final ZookeeperCompatibility zookeeperCompatibility;
120121

121122
private volatile ExecutorService executorService;
122123
private final AtomicBoolean logAsErrorConnectionErrors = new AtomicBoolean(false);
@@ -204,6 +205,7 @@ public void process(WatchedEvent watchedEvent) {
204205
builder.withEnsembleTracker() ? new EnsembleTracker(this, builder.getEnsembleProvider()) : null;
205206

206207
runSafeService = makeRunSafeService(builder);
208+
zookeeperCompatibility = builder.getZookeeperCompatibility();
207209
}
208210

209211
private Executor makeRunSafeService(CuratorFrameworkFactory.Builder builder) {
@@ -292,6 +294,7 @@ protected CuratorFrameworkImpl(CuratorFrameworkImpl parent) {
292294
schemaSet = parent.schemaSet;
293295
ensembleTracker = parent.ensembleTracker;
294296
runSafeService = parent.runSafeService;
297+
zookeeperCompatibility = parent.zookeeperCompatibility;
295298
}
296299

297300
@Override
@@ -585,8 +588,8 @@ public RemoveWatchesBuilder watches() {
585588
@Override
586589
public WatchesBuilder watchers() {
587590
Preconditions.checkState(
588-
Compatibility.hasPersistentWatchers(),
589-
"watchers() is not supported in the ZooKeeper library being used. Use watches() instead.");
591+
zookeeperCompatibility.hasPersistentWatchers(),
592+
"watchers() is not supported in the ZooKeeper library and/or server being used. Use watches() instead.");
590593
return new WatchesBuilderImpl(this);
591594
}
592595

@@ -600,6 +603,11 @@ public CuratorZookeeperClient getZookeeperClient() {
600603
return client;
601604
}
602605

606+
@Override
607+
public ZookeeperCompatibility getZookeeperCompatibility() {
608+
return zookeeperCompatibility;
609+
}
610+
603611
@Override
604612
public EnsurePath newNamespaceAwareEnsurePath(String path) {
605613
return namespace.newNamespaceAwareEnsurePath(path);

curator-recipes/src/main/java/org/apache/curator/framework/recipes/cache/CuratorCacheBridgeBuilderImpl.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import java.util.concurrent.ExecutorService;
2323
import org.apache.curator.framework.CuratorFramework;
24-
import org.apache.curator.utils.Compatibility;
2524
import org.slf4j.LoggerFactory;
2625

2726
class CuratorCacheBridgeBuilderImpl implements CuratorCacheBridgeBuilder {
@@ -57,7 +56,7 @@ public CuratorCacheBridgeBuilder withExecutorService(ExecutorService executorSer
5756

5857
@Override
5958
public CuratorCacheBridge build() {
60-
if (!forceTreeCache && Compatibility.hasPersistentWatchers()) {
59+
if (!forceTreeCache && client.getZookeeperCompatibility().hasPersistentWatchers()) {
6160
if (executorService != null) {
6261
LoggerFactory.getLogger(getClass()).warn("CuratorCache does not support custom ExecutorService");
6362
}

curator-test-zk35/src/test/java/org/apache/curator/zk35/TestIs35.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static org.junit.jupiter.api.Assertions.assertTrue;
2424
import org.apache.curator.test.compatibility.CuratorTestBase;
2525
import org.apache.curator.utils.Compatibility;
26+
import org.apache.curator.utils.ZookeeperCompatibility;
2627
import org.junit.jupiter.api.Tag;
2728
import org.junit.jupiter.api.Test;
2829

@@ -33,6 +34,12 @@ public void testIsZk35() {
3334
assertFalse(Compatibility.hasGetReachableOrOneMethod());
3435
assertTrue(Compatibility.hasAddrField());
3536
assertFalse(Compatibility.hasPersistentWatchers());
37+
assertFalse(ZookeeperCompatibility.LATEST.hasPersistentWatchers());
38+
assertFalse(ZookeeperCompatibility.builder().build().hasPersistentWatchers());
39+
assertFalse(ZookeeperCompatibility.builder()
40+
.hasPersistentWatchers(false)
41+
.build()
42+
.hasPersistentWatchers());
3643
}
3744

3845
@Override

curator-test-zk36/src/test/java/org/apache/curator/zk36/TestIs36.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919

2020
package org.apache.curator.zk36;
2121

22+
import static org.junit.jupiter.api.Assertions.assertFalse;
2223
import static org.junit.jupiter.api.Assertions.assertTrue;
2324
import static org.junit.jupiter.api.Assertions.fail;
2425
import org.apache.curator.test.compatibility.CuratorTestBase;
2526
import org.apache.curator.utils.Compatibility;
27+
import org.apache.curator.utils.ZookeeperCompatibility;
2628
import org.junit.jupiter.api.Tag;
2729
import org.junit.jupiter.api.Test;
2830

@@ -33,6 +35,12 @@ public void testIsZk36() {
3335
assertTrue(Compatibility.hasGetReachableOrOneMethod());
3436
assertTrue(Compatibility.hasAddrField());
3537
assertTrue(Compatibility.hasPersistentWatchers());
38+
assertTrue(ZookeeperCompatibility.LATEST.hasPersistentWatchers());
39+
assertFalse(ZookeeperCompatibility.builder().build().hasPersistentWatchers());
40+
assertFalse(ZookeeperCompatibility.builder()
41+
.hasPersistentWatchers(false)
42+
.build()
43+
.hasPersistentWatchers());
3644
try {
3745
Class.forName("org.apache.zookeeper.proto.WhoAmIResponse");
3846
fail("WhoAmIResponse is introduced after ZooKeeper 3.7");

curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncCuratorFrameworkImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.apache.curator.framework.imps.CuratorMultiTransactionImpl;
3333
import org.apache.curator.framework.imps.GetACLBuilderImpl;
3434
import org.apache.curator.framework.imps.SyncBuilderImpl;
35-
import org.apache.curator.utils.Compatibility;
3635
import org.apache.curator.x.async.AsyncCuratorFramework;
3736
import org.apache.curator.x.async.AsyncStage;
3837
import org.apache.curator.x.async.WatchMode;
@@ -140,8 +139,8 @@ public AsyncRemoveWatchesBuilder removeWatches() {
140139
@Override
141140
public AsyncWatchBuilder addWatch() {
142141
Preconditions.checkState(
143-
Compatibility.hasPersistentWatchers(),
144-
"addWatch() is not supported in the ZooKeeper library being used.");
142+
client.getZookeeperCompatibility().hasPersistentWatchers(),
143+
"addWatch() is not supported in the ZooKeeper library and/or server being used.");
145144
return new AsyncWatchBuilderImpl(client, filters);
146145
}
147146

0 commit comments

Comments
 (0)