├── settings.gradle ├── lib ├── junit-4.12.jar ├── log4j-1.2.15.jar ├── objenesis-1.0.jar ├── commons-io-1.4.jar ├── slf4j-api-1.6.1.jar ├── zookeeper-3.4.13.jar ├── assertj-core-2.0.0.jar ├── hamcrest-core-1.1.jar ├── mockito-core-1.8.0.jar └── slf4j-log4j12-1.6.1.jar ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── src ├── test │ ├── resources │ │ └── log4j.properties │ └── java │ │ └── org │ │ └── I0Itec │ │ └── zkclient │ │ ├── InMemoryConnectionTest.java │ │ ├── DeferredGatewayStarter.java │ │ ├── ZkConnectionTest.java │ │ ├── ZkConnectionResolveHostsTest.java │ │ ├── MemoryZkClientTest.java │ │ ├── InMemoryAuthTest.java │ │ ├── ZkClientSerializationTest.java │ │ ├── ZkAuthTest.java │ │ ├── AbstractConnectionTest.java │ │ ├── AbstractAuthTest.java │ │ ├── util │ │ └── ZkPathUtilTest.java │ │ ├── testutil │ │ └── ZkTestSystem.java │ │ ├── DistributedQueueTest.java │ │ ├── TestUtil.java │ │ ├── ContentWatcherTest.java │ │ ├── SaslAuthenticatedTest.java │ │ ├── ZkStateChangeTest.java │ │ ├── ServerZkClientTest.java │ │ └── AbstractBaseZkClientTest.java └── main │ └── java │ └── org │ └── I0Itec │ └── zkclient │ ├── IDefaultNameSpace.java │ ├── exception │ ├── ZkInterruptedException.java │ ├── ZkMarshallingError.java │ ├── ZkTimeoutException.java │ ├── ZkAuthFailedException.java │ ├── ZkNoNodeException.java │ ├── ZkBadVersionException.java │ ├── ZkNodeExistsException.java │ └── ZkException.java │ ├── Holder.java │ ├── DataUpdater.java │ ├── serialize │ ├── ZkSerializer.java │ ├── BytesPushThroughSerializer.java │ ├── SerializableSerializer.java │ └── TcclAwareObjectIputStream.java │ ├── IZkDataListener.java │ ├── Gateway.java │ ├── IZkChildListener.java │ ├── IZkStateListener.java │ ├── ExceptionUtil.java │ ├── ZkLock.java │ ├── IZkConnection.java │ ├── util │ └── ZkPathUtil.java │ ├── ContentWatcher.java │ ├── ZkEventThread.java │ ├── DistributedQueue.java │ ├── NetworkUtil.java │ ├── ZkConnection.java │ ├── GatewayThread.java │ ├── ZkServer.java │ └── InMemoryConnection.java ├── CHANGELOG.markdown ├── README.markdown ├── gradlew.bat ├── gradlew └── LICENSE /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name="zkclient" 2 | -------------------------------------------------------------------------------- /lib/junit-4.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/junit-4.12.jar -------------------------------------------------------------------------------- /lib/log4j-1.2.15.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/log4j-1.2.15.jar -------------------------------------------------------------------------------- /lib/objenesis-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/objenesis-1.0.jar -------------------------------------------------------------------------------- /lib/commons-io-1.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/commons-io-1.4.jar -------------------------------------------------------------------------------- /lib/slf4j-api-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/slf4j-api-1.6.1.jar -------------------------------------------------------------------------------- /lib/zookeeper-3.4.13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/zookeeper-3.4.13.jar -------------------------------------------------------------------------------- /lib/assertj-core-2.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/assertj-core-2.0.0.jar -------------------------------------------------------------------------------- /lib/hamcrest-core-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/hamcrest-core-1.1.jar -------------------------------------------------------------------------------- /lib/mockito-core-1.8.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/mockito-core-1.8.0.jar -------------------------------------------------------------------------------- /lib/slf4j-log4j12-1.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/lib/slf4j-log4j12-1.6.1.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgroschupf/zkclient/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .classpath 3 | .project 4 | .settings 5 | .gradle 6 | build-eclipse 7 | build 8 | zkclient.iml 9 | zkclient.ipr 10 | zkclient.iws 11 | .idea 12 | 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 23 17:30:58 CET 2012 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.3-bin.zip 7 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=info, console 2 | log4j.logger.org.apache.zookeeper=error 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.target=System.out 6 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %5p [%t] (%F:%L) - %m%n 8 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/InMemoryConnectionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | 19 | public class InMemoryConnectionTest extends AbstractConnectionTest { 20 | 21 | public InMemoryConnectionTest() { 22 | super(new InMemoryConnection()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/IDefaultNameSpace.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | public interface IDefaultNameSpace { 19 | 20 | /** 21 | * Creates a set of default folder structure within a zookeeper . 22 | * 23 | * @param zkClient 24 | * The zkclient. 25 | */ 26 | public void createDefaultNameSpace(ZkClient zkClient); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkInterruptedException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | public class ZkInterruptedException extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkInterruptedException(InterruptedException e) { 23 | super(e); 24 | Thread.currentThread().interrupt(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/Holder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | public class Holder { 19 | 20 | private T _value; 21 | 22 | public Holder() { 23 | // do nothing 24 | } 25 | 26 | public Holder(T value) { 27 | _value = value; 28 | } 29 | 30 | public T get() { 31 | return _value; 32 | } 33 | 34 | public void set(T value) { 35 | _value = value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/DataUpdater.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | /** 19 | * Updates the data of a znode. This is used together with {@link ZkClient#updateDataSerialized(String, DataUpdater)}. 20 | * 21 | * @param 22 | */ 23 | public interface DataUpdater { 24 | 25 | /** 26 | * Updates the current data of a znode. 27 | * 28 | * @param currentData 29 | * The current contents. 30 | * @return the new data that should be written back to ZooKeeper. 31 | */ 32 | public T update(T currentData); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/DeferredGatewayStarter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | public class DeferredGatewayStarter extends Thread { 19 | 20 | private final Gateway _zkServer; 21 | private int _delay; 22 | 23 | public DeferredGatewayStarter(Gateway gateway, int delay) { 24 | _zkServer = gateway; 25 | _delay = delay; 26 | } 27 | 28 | @Override 29 | public void run() { 30 | try { 31 | Thread.sleep(_delay); 32 | _zkServer.start(); 33 | } catch (Exception e) { 34 | // ignore 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkMarshallingError.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | public class ZkMarshallingError extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkMarshallingError() { 23 | super(); 24 | } 25 | 26 | public ZkMarshallingError(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | public ZkMarshallingError(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | 34 | public ZkMarshallingError(String message) { 35 | super(message); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkTimeoutException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | public class ZkTimeoutException extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkTimeoutException() { 23 | super(); 24 | } 25 | 26 | public ZkTimeoutException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | public ZkTimeoutException(String message) { 31 | super(message); 32 | } 33 | 34 | public ZkTimeoutException(Throwable cause) { 35 | super(cause); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/serialize/ZkSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.serialize; 17 | 18 | import org.I0Itec.zkclient.exception.ZkMarshallingError; 19 | 20 | /** 21 | * Zookeeper is able to store data in form of byte arrays. This interfacte is a bridge between those byte-array format 22 | * and higher level objects. 23 | * 24 | * @see BytesPushThroughSerializer 25 | * @see SerializableSerializer 26 | */ 27 | public interface ZkSerializer { 28 | 29 | public byte[] serialize(Object data) throws ZkMarshallingError; 30 | 31 | public Object deserialize(byte[] bytes) throws ZkMarshallingError; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkAuthFailedException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | public class ZkAuthFailedException extends ZkException { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | public ZkAuthFailedException() { 23 | super(); 24 | } 25 | 26 | public ZkAuthFailedException(String message, Throwable cause) { 27 | super(message, cause); 28 | } 29 | 30 | public ZkAuthFailedException(String message) { 31 | super(message); 32 | } 33 | 34 | public ZkAuthFailedException(Throwable cause) { 35 | super(cause); 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/serialize/BytesPushThroughSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.serialize; 17 | 18 | import org.I0Itec.zkclient.exception.ZkMarshallingError; 19 | 20 | /** 21 | * A {@link ZkSerializer} which simply passes byte arrays to zk and back. 22 | */ 23 | public class BytesPushThroughSerializer implements ZkSerializer { 24 | 25 | @Override 26 | public Object deserialize(byte[] bytes) throws ZkMarshallingError { 27 | return bytes; 28 | } 29 | 30 | @Override 31 | public byte[] serialize(Object bytes) throws ZkMarshallingError { 32 | return (byte[]) bytes; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkNoNodeException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkNoNodeException extends ZkException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkNoNodeException() { 25 | super(); 26 | } 27 | 28 | public ZkNoNodeException(KeeperException cause) { 29 | super(cause); 30 | } 31 | 32 | public ZkNoNodeException(String message, KeeperException cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ZkNoNodeException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkBadVersionException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkBadVersionException extends ZkException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkBadVersionException() { 25 | super(); 26 | } 27 | 28 | public ZkBadVersionException(KeeperException cause) { 29 | super(cause); 30 | } 31 | 32 | public ZkBadVersionException(String message, KeeperException cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ZkBadVersionException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkNodeExistsException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkNodeExistsException extends ZkException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkNodeExistsException() { 25 | super(); 26 | } 27 | 28 | public ZkNodeExistsException(KeeperException cause) { 29 | super(cause); 30 | } 31 | 32 | public ZkNodeExistsException(String message, KeeperException cause) { 33 | super(message, cause); 34 | } 35 | 36 | public ZkNodeExistsException(String message) { 37 | super(message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkConnectionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 19 | import org.junit.Rule; 20 | 21 | public class ZkConnectionTest extends AbstractConnectionTest { 22 | 23 | @Rule 24 | public ZkTestSystem _zk = ZkTestSystem.getInstance(); 25 | 26 | public ZkConnectionTest() { 27 | super(establishConnection()); 28 | } 29 | 30 | private static ZkConnection establishConnection() { 31 | ZkConnection zkConnection = new ZkConnection("localhost:" + ZkTestSystem.getInstance().getZkServer().getPort()); 32 | new ZkClient(zkConnection);// connect 33 | return zkConnection; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkConnectionResolveHostsTest.java: -------------------------------------------------------------------------------- 1 | package org.I0Itec.zkclient; 2 | 3 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 4 | import org.junit.Assert; 5 | import org.junit.Rule; 6 | import org.junit.Test; 7 | 8 | public class ZkConnectionResolveHostsTest { 9 | @Rule 10 | public ZkTestSystem _zk = ZkTestSystem.getInstance(); 11 | 12 | @Test 13 | public void ZkConnectionResolveHosts() { 14 | String connectionString = "host-unknown,localhost:" + ZkTestSystem.getInstance().getZkServer().getPort(); 15 | IZkConnection connection = new ZkConnection(connectionString); 16 | new ZkClient(connection); 17 | 18 | String connectionStringWithZkRoot = "host-unknown:5070,localhost:" + ZkTestSystem.getInstance().getZkServer().getPort() + "/zkroot"; 19 | IZkConnection connectionWithZkRoot = new ZkConnection(connectionStringWithZkRoot); 20 | new ZkClient(connectionWithZkRoot); 21 | 22 | String connectionStringWithSingleHost = "localhost:" + ZkTestSystem.getInstance().getZkServer().getPort(); 23 | IZkConnection connectionWithSingleHost = new ZkConnection(connectionStringWithSingleHost); 24 | new ZkClient(connectionWithSingleHost); 25 | Assert.assertEquals("localhost:" + ZkTestSystem.getInstance().getZkServer().getPort(), connectionWithSingleHost.getServers()); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/IZkDataListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | /** 19 | * An {@link IZkDataListener} can be registered at a {@link ZkClient} for listening on zk data changes for a given path. 20 | * 21 | * Node: Also this listener re-subscribes it watch for the path on each zk event (zk watches are one-timers) is is not 22 | * guaranteed that events on the path are missing (see http://zookeeper.wiki.sourceforge.net/ZooKeeperWatches). An 23 | * implementation of this class should take that into account. 24 | */ 25 | public interface IZkDataListener { 26 | 27 | public void handleDataChange(String dataPath, Object data) throws Exception; 28 | 29 | public void handleDataDeleted(String dataPath) throws Exception; 30 | } 31 | -------------------------------------------------------------------------------- /CHANGELOG.markdown: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ===== 3 | 4 | ZkClient 0.12 (???) 5 | --------------- 6 | - ... 7 | 8 | ZkClient 0.11 (Okt 2018) 9 | --------------- 10 | - Upgrade to Zookeeper 3.4.13 (from 3.4.8) 11 | - Fix UnknownHostException if one of the server hosts is currently not available 12 | 13 | 14 | ZkClient 0.10 (Nov 2016) 15 | --------------- 16 | - #56: Switch from Log4j to Slf4j 17 | - #41: Provide more information about zookeeper server in case of connection errors 18 | 19 | 20 | ZkClient 0.9 (June 2016) 21 | --------------- 22 | - Upgrade to Zookeeper 3.4.8 (from 3.4.6) 23 | 24 | 25 | ZkClient 0.8 (Mar 2016) 26 | --------------- 27 | - #45: Support for conditional deletes 28 | - Adding ZkAuthFailedException 29 | - exclude org.apache.jute and zookeeper.proto from imported packages of MANIFEST.MF 30 | 31 | 32 | ZkClient 0.7 (Nov 2015) 33 | --------------- 34 | - #38: wait on SaslAuthenticated event when SASL is enabled 35 | 36 | 37 | ZkClient 0.6 (Aug 2015) 38 | --------------- 39 | - Adding setAcl and getAcl methods to zkClient so users can setAcls not just during creation but after creation of node as well. 40 | - Upgrade to Zookeeper 3.4.6 (from 3.4.3) 41 | 42 | 43 | ZkClient 0.5 (Apr, 2015) 44 | --------------- 45 | - Upgrade to zookeeper 3.4.3 (from 3.3.1) 46 | - Added support for multiops support 47 | - Add an option to ZkClient to specify operation retry timeout 48 | - Support for ACLs 49 | - #25: fail retryUntilConnected actions with clear exception in case client gets closed 50 | 51 | 52 | ZkClient 0.4 (Oct, 2013) 53 | --------------- 54 | - Support for handling SessionEstablishmentErrors -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/Gateway.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | public class Gateway { 19 | 20 | private GatewayThread _thread; 21 | private final int _port; 22 | private final int _destinationPort; 23 | 24 | public Gateway(int port, int destinationPort) { 25 | _port = port; 26 | _destinationPort = destinationPort; 27 | } 28 | 29 | public synchronized void start() { 30 | if (_thread != null) { 31 | throw new IllegalStateException("Gateway already running"); 32 | } 33 | _thread = new GatewayThread(_port, _destinationPort); 34 | _thread.start(); 35 | _thread.awaitUp(); 36 | } 37 | 38 | public synchronized void stop() { 39 | if (_thread != null) { 40 | try { 41 | _thread.interruptAndJoin(); 42 | } catch (InterruptedException e) { 43 | Thread.currentThread().interrupt(); 44 | } 45 | _thread = null; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/IZkChildListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * An {@link IZkChildListener} can be registered at a {@link ZkClient} for listening on zk child changes for a given 22 | * path. 23 | * 24 | * Node: Also this listener re-subscribes it watch for the path on each zk event (zk watches are one-timers) is is not 25 | * guaranteed that events on the path are missing (see http://zookeeper.wiki.sourceforge.net/ZooKeeperWatches). An 26 | * implementation of this class should take that into account. 27 | * 28 | */ 29 | public interface IZkChildListener { 30 | 31 | /** 32 | * Called when the children of the given path changed. 33 | * 34 | * @param parentPath 35 | * The parent path 36 | * @param currentChilds 37 | * The children or null if the root node (parent path) was deleted. 38 | * @throws Exception 39 | */ 40 | public void handleChildChange(String parentPath, List currentChilds) throws Exception; 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/MemoryZkClientTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import org.apache.zookeeper.CreateMode; 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | 22 | public class MemoryZkClientTest extends AbstractBaseZkClientTest { 23 | 24 | @Override 25 | public void setUp() throws Exception { 26 | super.setUp(); 27 | _client = new ZkClient(new InMemoryConnection()); 28 | } 29 | 30 | @Override 31 | public void tearDown() throws Exception { 32 | super.tearDown(); 33 | _client.close(); 34 | } 35 | 36 | @Test 37 | public void testGetChildren() throws Exception { 38 | String path1 = "/a"; 39 | String path2 = "/a/a"; 40 | String path3 = "/a/a/a"; 41 | 42 | _client.create(path1, null, CreateMode.PERSISTENT); 43 | _client.create(path2, null, CreateMode.PERSISTENT); 44 | _client.create(path3, null, CreateMode.PERSISTENT); 45 | Assert.assertEquals(1, _client.getChildren(path1).size()); 46 | Assert.assertEquals(1, _client.getChildren(path2).size()); 47 | Assert.assertEquals(0, _client.getChildren(path3).size()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ZkClient: a zookeeper client, that makes life a little easier. 2 | ===== 3 | 4 | + Website: https://github.com/sgroschupf/zkclient 5 | + Apache 2.0 License 6 | 7 | ==> see [CHANGELOG][] for recent work 8 | 9 | 10 | Build ZkClient from sources: 11 | --------------- 12 | 13 | + git clone https://github.com/sgroschupf/zkclient.git 14 | + ./gradlew test _(run the test suite)_ 15 | + ./gradlew jars _(build the jars)_ 16 | + (see available build targets by executing './gradlew tasks' ) 17 | 18 | 19 | Howto release ZkClient as maven artifact 20 | --------------- 21 | - sonatype repository is already configured: https://issues.sonatype.org/browse/OSSRH-4783 22 | - generate gpg key and publish it to _hkp://pool.sks-keyservers.net_ (https://docs.sonatype.org/display/Repository/How+To+Generate+PGP+Signatures+With+Maven may be of help) 23 | - tell gradle about the gpg key and sonatype credentials, e.g. through ~/.gradle/gradle.properties: 24 | - sonatypeUsername=$yourSonatypeUser 25 | - sonatypePassword=$yourSonatypePassword 26 | - signing.keyId=$yourKeyId 27 | - signing.password=$yourKeyPassphrase 28 | - signing.secretKeyRingFile=/Users/$username/.gnupg/secring.gpg 29 | - set version in build.gradle to the release version (e.g. 0.5-dev to 0.5) and commit 30 | - update CHANGELOG.markdown 31 | - upload the signed artifacts to the Sonatype repository 32 | - _gradle clean uploadArchives_ 33 | - go to https://oss.sonatype.org/index.html#stagingRepositories and close the repository 34 | - check the artifacts and if everything is ok, release the repository (on the same page) 35 | - syncing to central maven repository will then be activated (might take around 2h) 36 | - tag with 37 | - _git tag -a $releaseVersion -m "Tag for $releaseVersion release"_ 38 | - _git push --tags_ 39 | - set version in build.gradle to the next dev version (e.g 0.5 to 0.6-dev) and commit 40 | 41 | 42 | [CHANGELOG]: ./CHANGELOG.markdown 43 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/InMemoryAuthTest.java: -------------------------------------------------------------------------------- 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.I0Itec.zkclient; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import org.apache.zookeeper.CreateMode; 24 | import org.apache.zookeeper.ZooDefs; 25 | import org.apache.zookeeper.data.ACL; 26 | import org.apache.zookeeper.data.Id; 27 | import org.junit.Test; 28 | 29 | public class InMemoryAuthTest extends AbstractAuthTest { 30 | 31 | @Override 32 | public void setUp() throws Exception { 33 | super.setUp(); 34 | _client = new ZkClient(new InMemoryConnection()); 35 | } 36 | 37 | @Override 38 | public void tearDown() throws Exception { 39 | super.tearDown(); 40 | _client.close(); 41 | } 42 | 43 | @Test 44 | public void testAuthorized() { 45 | List acl = new ArrayList(); 46 | acl.add(new ACL(ZooDefs.Perms.ALL, new Id("digest", "pat:pass"))); 47 | _client.addAuthInfo("digest", "pat:pass".getBytes()); 48 | _client.create("/path1", null, acl, CreateMode.PERSISTENT); 49 | _client.readData("/path1"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/IZkStateListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import org.apache.zookeeper.Watcher.Event.KeeperState; 19 | 20 | public interface IZkStateListener { 21 | 22 | /** 23 | * Called when the zookeeper connection state has changed. 24 | * 25 | * @param state 26 | * The new state. 27 | * @throws Exception 28 | * On any error. 29 | */ 30 | public void handleStateChanged(KeeperState state) throws Exception; 31 | 32 | /** 33 | * Called after the zookeeper session has expired and a new session has been created. You would have to re-create 34 | * any ephemeral nodes here. 35 | * 36 | * @throws Exception 37 | * On any error. 38 | */ 39 | public void handleNewSession() throws Exception; 40 | 41 | /** 42 | * Called when a session cannot be re-established. This should be used to implement connection 43 | * failure handling e.g. retry to connect or pass the error up 44 | * 45 | * @param error 46 | * The error that prevents a session from being established 47 | * @throws Exception 48 | * On any error. 49 | */ 50 | public void handleSessionEstablishmentError(final Throwable error) throws Exception; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/ExceptionUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 19 | 20 | public class ExceptionUtil { 21 | 22 | public static RuntimeException convertToRuntimeException(Throwable e) { 23 | if (e instanceof RuntimeException) { 24 | return (RuntimeException) e; 25 | } 26 | retainInterruptFlag(e); 27 | return new RuntimeException(e); 28 | } 29 | 30 | /** 31 | * This sets the interrupt flag if the catched exception was an {@link InterruptedException}. Catching such an 32 | * exception always clears the interrupt flag. 33 | * 34 | * @param catchedException 35 | * The catched exception. 36 | */ 37 | public static void retainInterruptFlag(Throwable catchedException) { 38 | if (catchedException instanceof InterruptedException) { 39 | Thread.currentThread().interrupt(); 40 | } 41 | } 42 | 43 | public static void rethrowInterruptedException(Throwable e) throws InterruptedException { 44 | if (e instanceof InterruptedException) { 45 | throw (InterruptedException) e; 46 | } 47 | if (e instanceof ZkInterruptedException) { 48 | throw (ZkInterruptedException) e; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/ZkLock.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.concurrent.locks.Condition; 19 | import java.util.concurrent.locks.ReentrantLock; 20 | 21 | public class ZkLock extends ReentrantLock { 22 | 23 | private static final long serialVersionUID = 1L; 24 | 25 | private Condition _dataChangedCondition = newCondition(); 26 | private Condition _stateChangedCondition = newCondition(); 27 | private Condition _zNodeEventCondition = newCondition(); 28 | 29 | /** 30 | * This condition will be signaled if a zookeeper event was processed and the event contains a data/child change. 31 | * 32 | * @return the condition. 33 | */ 34 | public Condition getDataChangedCondition() { 35 | return _dataChangedCondition; 36 | } 37 | 38 | /** 39 | * This condition will be signaled if a zookeeper event was processed and the event contains a state change 40 | * (connected, disconnected, session expired, etc ...). 41 | * 42 | * @return the condition. 43 | */ 44 | public Condition getStateChangedCondition() { 45 | return _stateChangedCondition; 46 | } 47 | 48 | /** 49 | * This condition will be signaled if any znode related zookeeper event was received. 50 | * 51 | * @return the condition. 52 | */ 53 | public Condition getZNodeEventCondition() { 54 | return _zNodeEventCondition; 55 | } 56 | } -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkClientSerializationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.junit.Assert.assertArrayEquals; 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.Random; 22 | 23 | import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer; 24 | import org.I0Itec.zkclient.serialize.SerializableSerializer; 25 | import org.I0Itec.zkclient.testutil.ZkTestSystem; 26 | import org.junit.Rule; 27 | import org.junit.Test; 28 | 29 | public class ZkClientSerializationTest { 30 | 31 | @Rule 32 | public ZkTestSystem _zk = ZkTestSystem.getInstance(); 33 | 34 | @Test 35 | public void testBytes() throws Exception { 36 | ZkClient zkClient = new ZkClient(_zk.getZkServerAddress(), 2000, 30000, new BytesPushThroughSerializer()); 37 | byte[] bytes = new byte[100]; 38 | new Random().nextBytes(bytes); 39 | zkClient.createPersistent("/a", bytes); 40 | byte[] readBytes = zkClient.readData("/a"); 41 | assertArrayEquals(bytes, readBytes); 42 | } 43 | 44 | @Test 45 | public void testSerializables() throws Exception { 46 | ZkClient zkClient = new ZkClient(_zk.getZkServerAddress(), 2000, 30000, new SerializableSerializer()); 47 | String data = "hello world"; 48 | zkClient.createPersistent("/a", data); 49 | String readData = zkClient.readData("/a"); 50 | assertEquals(data, readData); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/serialize/SerializableSerializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.serialize; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.ObjectInputStream; 22 | import java.io.ObjectOutputStream; 23 | 24 | import org.I0Itec.zkclient.exception.ZkMarshallingError; 25 | 26 | public class SerializableSerializer implements ZkSerializer { 27 | 28 | @Override 29 | public Object deserialize(byte[] bytes) throws ZkMarshallingError { 30 | try { 31 | ObjectInputStream inputStream = new TcclAwareObjectIputStream(new ByteArrayInputStream(bytes)); 32 | Object object = inputStream.readObject(); 33 | return object; 34 | } catch (ClassNotFoundException e) { 35 | throw new ZkMarshallingError("Unable to find object class.", e); 36 | } catch (IOException e) { 37 | throw new ZkMarshallingError(e); 38 | } 39 | } 40 | 41 | @Override 42 | public byte[] serialize(Object serializable) throws ZkMarshallingError { 43 | try { 44 | ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); 45 | ObjectOutputStream stream = new ObjectOutputStream(byteArrayOS); 46 | stream.writeObject(serializable); 47 | stream.close(); 48 | return byteArrayOS.toByteArray(); 49 | } catch (IOException e) { 50 | throw new ZkMarshallingError(e); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/exception/ZkException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.exception; 17 | 18 | import org.apache.zookeeper.KeeperException; 19 | 20 | public class ZkException extends RuntimeException { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | public ZkException() { 25 | super(); 26 | } 27 | 28 | public ZkException(String message, Throwable cause) { 29 | super(message, cause); 30 | } 31 | 32 | public ZkException(String message) { 33 | super(message); 34 | } 35 | 36 | public ZkException(Throwable cause) { 37 | super(cause); 38 | } 39 | 40 | public static ZkException create(KeeperException e) { 41 | switch (e.code()) { 42 | // case DATAINCONSISTENCY: 43 | // return new DataInconsistencyException(); 44 | // case CONNECTIONLOSS: 45 | // return new ConnectionLossException(); 46 | case NONODE: 47 | return new ZkNoNodeException(e); 48 | // case NOAUTH: 49 | // return new ZkNoAuthException(); 50 | case BADVERSION: 51 | return new ZkBadVersionException(e); 52 | // case NOCHILDRENFOREPHEMERALS: 53 | // return new NoChildrenForEphemeralsException(); 54 | case NODEEXISTS: 55 | return new ZkNodeExistsException(e); 56 | // case INVALIDACL: 57 | // return new ZkInvalidACLException(); 58 | // case AUTHFAILED: 59 | // return new AuthFailedException(); 60 | // case NOTEMPTY: 61 | // return new NotEmptyException(); 62 | // case SESSIONEXPIRED: 63 | // return new SessionExpiredException(); 64 | // case INVALIDCALLBACK: 65 | // return new InvalidCallbackException(); 66 | 67 | default: 68 | return new ZkException(e); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkAuthTest.java: -------------------------------------------------------------------------------- 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.I0Itec.zkclient; 19 | 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | 22 | import org.apache.zookeeper.CreateMode; 23 | import org.apache.zookeeper.ZooDefs; 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | 28 | public class ZkAuthTest extends AbstractAuthTest { 29 | @Override 30 | @Before 31 | public void setUp() throws Exception { 32 | super.setUp(); 33 | _zkServer = TestUtil.startZkServer("ZkClientTest", 4711); 34 | _client = new ZkClient("localhost:4711", 25000); 35 | } 36 | 37 | @Override 38 | @After 39 | public void tearDown() throws Exception { 40 | super.tearDown(); 41 | if (_client != null) { 42 | _client.close(); 43 | } 44 | _zkServer.shutdown(); 45 | } 46 | 47 | @Test 48 | public void testAuthorized() { 49 | _client.addAuthInfo("digest", "pat:pass".getBytes()); 50 | _client.create("/path1", null, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); 51 | _client.readData("/path1"); 52 | } 53 | 54 | @Test 55 | public void testSetAndGetAcls() { 56 | _client.addAuthInfo("digest", "pat:pass".getBytes()); 57 | 58 | _client.create("/path1", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 59 | assertThat(_client.getAcl("/path1").getKey()).isEqualTo(ZooDefs.Ids.OPEN_ACL_UNSAFE); 60 | 61 | for (int i = 0; i < 100; i++) { 62 | _client.setAcl("/path1", ZooDefs.Ids.OPEN_ACL_UNSAFE); 63 | assertThat(_client.getAcl("/path1").getKey()).isEqualTo(ZooDefs.Ids.OPEN_ACL_UNSAFE); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/AbstractConnectionTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.List; 19 | 20 | import org.I0Itec.zkclient.util.ZkPathUtil; 21 | import org.apache.zookeeper.CreateMode; 22 | import org.apache.zookeeper.KeeperException; 23 | import org.junit.Test; 24 | 25 | import static org.junit.Assert.assertEquals; 26 | 27 | public abstract class AbstractConnectionTest { 28 | 29 | private final IZkConnection _connection; 30 | 31 | public AbstractConnectionTest(IZkConnection connection) { 32 | _connection = connection; 33 | } 34 | 35 | @Test 36 | public void testGetChildren_OnEmptyFileSystem() throws KeeperException, InterruptedException { 37 | InMemoryConnection connection = new InMemoryConnection(); 38 | List children = connection.getChildren("/", false); 39 | assertEquals(0, children.size()); 40 | } 41 | 42 | @Test 43 | public void testSequentials() throws KeeperException, InterruptedException { 44 | String sequentialPath = _connection.create("/a", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL); 45 | int firstSequential = Integer.parseInt(sequentialPath.substring(2)); 46 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), sequentialPath); 47 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/a", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL)); 48 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/a", new byte[0], CreateMode.PERSISTENT_SEQUENTIAL)); 49 | assertEquals("/b" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/b", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL)); 50 | assertEquals("/b" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/b", new byte[0], CreateMode.PERSISTENT_SEQUENTIAL)); 51 | assertEquals("/a" + ZkPathUtil.leadingZeros(firstSequential++, 10), _connection.create("/a", new byte[0], CreateMode.EPHEMERAL_SEQUENTIAL)); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/AbstractAuthTest.java: -------------------------------------------------------------------------------- 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.I0Itec.zkclient; 19 | 20 | import static org.junit.Assert.fail; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import org.I0Itec.zkclient.exception.ZkException; 26 | import org.apache.log4j.Logger; 27 | import org.apache.zookeeper.CreateMode; 28 | import org.apache.zookeeper.KeeperException; 29 | import org.apache.zookeeper.ZooDefs; 30 | import org.apache.zookeeper.data.ACL; 31 | import org.apache.zookeeper.data.Id; 32 | import org.junit.After; 33 | import org.junit.Before; 34 | import org.junit.Test; 35 | 36 | public abstract class AbstractAuthTest { 37 | protected ZkClient _client; 38 | protected ZkServer _zkServer; 39 | protected static final Logger LOG = Logger.getLogger(AbstractAuthTest.class); 40 | 41 | @Before 42 | public void setUp() throws Exception { 43 | LOG.info("------------ BEFORE -------------"); 44 | 45 | } 46 | 47 | @After 48 | public void tearDown() throws Exception { 49 | LOG.info("------------ AFTER -------------"); 50 | } 51 | 52 | @Test 53 | public void testBadAuth() { 54 | try { 55 | List acl = new ArrayList(); 56 | acl.add(new ACL(ZooDefs.Perms.ALL, new Id("digest", "pat:pass"))); 57 | _client.create("/path1", null, acl, CreateMode.EPHEMERAL); 58 | _client.addAuthInfo("digest", "pat:pass2".getBytes()); 59 | _client.readData("/path1"); 60 | fail("Should get auth error"); 61 | } catch (ZkException e) { 62 | if (e.getCause() instanceof KeeperException && ((KeeperException) e.getCause()).code() == KeeperException.Code.NOAUTH) { 63 | // do nothing, this is expected 64 | } else { 65 | fail("wrong exception"); 66 | } 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/util/ZkPathUtilTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.util; 17 | 18 | import junit.framework.TestCase; 19 | 20 | import org.I0Itec.zkclient.TestUtil; 21 | import org.I0Itec.zkclient.ZkClient; 22 | import org.I0Itec.zkclient.ZkServer; 23 | 24 | public class ZkPathUtilTest extends TestCase { 25 | 26 | protected ZkServer _zkServer; 27 | protected ZkClient _client; 28 | 29 | public void testToString() throws Exception { 30 | _zkServer = TestUtil.startZkServer("ZkPathUtilTest", 4711); 31 | _client = new ZkClient("localhost:4711", 30000); 32 | final String file1 = "/files/file1"; 33 | final String file2 = "/files/file2"; 34 | final String file3 = "/files/file2/file3"; 35 | _client.createPersistent(file1, true); 36 | _client.createPersistent(file2, true); 37 | _client.createPersistent(file3, true); 38 | 39 | String stringRepresentation = ZkPathUtil.toString(_client); 40 | System.out.println(stringRepresentation); 41 | System.out.println("-------------------------"); 42 | assertTrue(stringRepresentation.contains("file1")); 43 | assertTrue(stringRepresentation.contains("file2")); 44 | assertTrue(stringRepresentation.contains("file3")); 45 | 46 | // path filtering 47 | stringRepresentation = ZkPathUtil.toString(_client, "/", new ZkPathUtil.PathFilter() { 48 | @Override 49 | public boolean showChilds(String path) { 50 | return !file2.equals(path); 51 | } 52 | }); 53 | assertTrue(stringRepresentation.contains("file1")); 54 | assertTrue(stringRepresentation.contains("file2")); 55 | assertFalse(stringRepresentation.contains("file3")); 56 | 57 | // start path 58 | stringRepresentation = ZkPathUtil.toString(_client, file2, ZkPathUtil.PathFilter.ALL); 59 | assertFalse(stringRepresentation.contains("file1")); 60 | assertTrue(stringRepresentation.contains("file2")); 61 | assertTrue(stringRepresentation.contains("file3")); 62 | 63 | _zkServer.shutdown(); 64 | } 65 | 66 | public void testLeadingZeros() throws Exception { 67 | assertEquals("0000000001", ZkPathUtil.leadingZeros(1, 10)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/IZkConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | import org.apache.zookeeper.CreateMode; 22 | import org.apache.zookeeper.KeeperException; 23 | import org.apache.zookeeper.Op; 24 | import org.apache.zookeeper.OpResult; 25 | import org.apache.zookeeper.Watcher; 26 | import org.apache.zookeeper.ZooKeeper.States; 27 | import org.apache.zookeeper.data.ACL; 28 | import org.apache.zookeeper.data.Stat; 29 | 30 | public interface IZkConnection { 31 | 32 | public void connect(Watcher watcher); 33 | 34 | void close() throws InterruptedException; 35 | 36 | public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException; 37 | 38 | public String create(String path, byte[] data, List acl, CreateMode mode) throws KeeperException, InterruptedException; 39 | 40 | public void delete(String path) throws InterruptedException, KeeperException; 41 | 42 | public void delete(String path, int version) throws InterruptedException, KeeperException; 43 | 44 | boolean exists(final String path, final boolean watch) throws KeeperException, InterruptedException; 45 | 46 | List getChildren(final String path, final boolean watch) throws KeeperException, InterruptedException; 47 | 48 | public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException; 49 | 50 | public void writeData(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException; 51 | 52 | public Stat writeDataReturnStat(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException; 53 | 54 | public States getZookeeperState(); 55 | 56 | public long getCreateTime(String path) throws KeeperException, InterruptedException; 57 | 58 | public String getServers(); 59 | 60 | public List multi(Iterable ops) throws KeeperException, InterruptedException; 61 | 62 | public void addAuthInfo(String scheme, byte[] auth); 63 | 64 | public void setAcl(final String path, List acl, int version) throws KeeperException, InterruptedException; 65 | 66 | public Map.Entry, Stat> getAcl(final String path) throws KeeperException, InterruptedException; 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/util/ZkPathUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.util; 17 | 18 | import java.util.List; 19 | 20 | import org.I0Itec.zkclient.ZkClient; 21 | 22 | public class ZkPathUtil { 23 | 24 | public static String leadingZeros(long number, int numberOfLeadingZeros) { 25 | return String.format("%0" + numberOfLeadingZeros + "d", number); 26 | } 27 | 28 | public static String toString(ZkClient zkClient) { 29 | return toString(zkClient, "/", PathFilter.ALL); 30 | } 31 | 32 | public static String toString(ZkClient zkClient, String startPath, PathFilter pathFilter) { 33 | final int level = 1; 34 | final StringBuilder builder = new StringBuilder("+ (" + startPath + ")"); 35 | builder.append("\n"); 36 | addChildrenToStringBuilder(zkClient, pathFilter, level, builder, startPath); 37 | return builder.toString(); 38 | } 39 | 40 | private static void addChildrenToStringBuilder(ZkClient zkClient, PathFilter pathFilter, final int level, final StringBuilder builder, final String startPath) { 41 | final List children = zkClient.getChildren(startPath); 42 | for (final String node : children) { 43 | String nestedPath; 44 | if (startPath.endsWith("/")) { 45 | nestedPath = startPath + node; 46 | } else { 47 | nestedPath = startPath + "/" + node; 48 | } 49 | if (pathFilter.showChilds(nestedPath)) { 50 | builder.append(getSpaces(level - 1) + "'-" + "+" + node + "\n"); 51 | addChildrenToStringBuilder(zkClient, pathFilter, level + 1, builder, nestedPath); 52 | } else { 53 | builder.append(getSpaces(level - 1) + "'-" + "-" + node + " (contents hidden)\n"); 54 | } 55 | } 56 | } 57 | 58 | private static String getSpaces(final int level) { 59 | String s = ""; 60 | for (int i = 0; i < level; i++) { 61 | s += " "; 62 | } 63 | return s; 64 | } 65 | 66 | public static interface PathFilter { 67 | 68 | public static PathFilter ALL = new PathFilter() { 69 | 70 | @Override 71 | public boolean showChilds(String path) { 72 | return true; 73 | } 74 | }; 75 | 76 | boolean showChilds(String path); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/serialize/TcclAwareObjectIputStream.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.serialize; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.io.ObjectInputStream; 21 | import java.io.ObjectStreamClass; 22 | import java.lang.reflect.Proxy; 23 | 24 | /** 25 | * An ObjectInputStream that is aware of the TCCL. 26 | */ 27 | public class TcclAwareObjectIputStream extends ObjectInputStream { 28 | 29 | public TcclAwareObjectIputStream(InputStream in) throws IOException { 30 | super(in); 31 | } 32 | 33 | /** 34 | * Load the local class equivalent of the specified stream class 35 | * description. 36 | * Uses the current class {@link ClassLoader} and falls back to the {@link Thread} context {@link ClassLoader}. 37 | */ 38 | protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { 39 | try { 40 | return getClass().getClassLoader().loadClass(classDesc.getName()); 41 | } catch (ClassNotFoundException ex) { 42 | ClassLoader tccl = Thread.currentThread().getContextClassLoader(); 43 | if (tccl != null) { 44 | return tccl.loadClass(classDesc.getName()); 45 | } else { 46 | throw ex; 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Returns a proxy class that implements the interfaces named in a proxy 53 | * class descriptor; subclasses may implement this method to read custom 54 | * data from the stream along with the descriptors for dynamic proxy 55 | * classes, allowing them to use an alternate loading mechanism for the 56 | * interfaces and the proxy class. 57 | * 58 | * For each interface uses the current class {@link ClassLoader} and falls back to the {@link Thread} context {@link ClassLoader}. 59 | */ 60 | protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { 61 | ClassLoader cl = getClass().getClassLoader(); 62 | Class[] cinterfaces = new Class[interfaces.length]; 63 | 64 | for (int i = 0; i < interfaces.length; i++) { 65 | try { 66 | cinterfaces[i] = cl.loadClass(interfaces[i]); 67 | } catch (ClassNotFoundException ex) { 68 | ClassLoader tccl = Thread.currentThread().getContextClassLoader(); 69 | if (tccl != null) { 70 | return tccl.loadClass(interfaces[i]); 71 | } else { 72 | throw ex; 73 | } 74 | } 75 | } 76 | try { 77 | return Proxy.getProxyClass(cinterfaces[0].getClassLoader(), cinterfaces); 78 | } catch (IllegalArgumentException e) { 79 | throw new ClassNotFoundException(null, e); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/ContentWatcher.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.concurrent.locks.Condition; 19 | import java.util.concurrent.locks.Lock; 20 | import java.util.concurrent.locks.ReentrantLock; 21 | 22 | import org.I0Itec.zkclient.exception.ZkNoNodeException; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @param 28 | * The data type that is being watched. 29 | */ 30 | public final class ContentWatcher implements IZkDataListener { 31 | 32 | private static final Logger LOG = LoggerFactory.getLogger(ContentWatcher.class); 33 | 34 | private Lock _contentLock = new ReentrantLock(true); 35 | private Condition _contentAvailable = _contentLock.newCondition(); 36 | 37 | private Holder _content; 38 | private String _fileName; 39 | private ZkClient _zkClient; 40 | 41 | public ContentWatcher(ZkClient zkClient, String fileName) { 42 | _fileName = fileName; 43 | _zkClient = zkClient; 44 | } 45 | 46 | public void start() { 47 | _zkClient.subscribeDataChanges(_fileName, this); 48 | readData(); 49 | LOG.debug("Started ContentWatcher"); 50 | } 51 | 52 | @SuppressWarnings("unchecked") 53 | private void readData() { 54 | try { 55 | setContent((T) _zkClient.readData(_fileName)); 56 | } catch (ZkNoNodeException e) { 57 | // ignore if the node has not yet been created 58 | } 59 | } 60 | 61 | public void stop() { 62 | _zkClient.unsubscribeDataChanges(_fileName, this); 63 | } 64 | 65 | public void setContent(T data) { 66 | LOG.debug("Received new data: " + data); 67 | _contentLock.lock(); 68 | try { 69 | _content = new Holder(data); 70 | _contentAvailable.signalAll(); 71 | } finally { 72 | _contentLock.unlock(); 73 | } 74 | } 75 | 76 | @SuppressWarnings("unchecked") 77 | @Override 78 | public void handleDataChange(String dataPath, Object data) { 79 | setContent((T) data); 80 | } 81 | 82 | @Override 83 | public void handleDataDeleted(String dataPath) { 84 | // ignore 85 | } 86 | 87 | public T getContent() throws InterruptedException { 88 | _contentLock.lock(); 89 | try { 90 | while (_content == null) { 91 | _contentAvailable.await(); 92 | } 93 | return _content.get(); 94 | } finally { 95 | _contentLock.unlock(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/ZkEventThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.concurrent.BlockingQueue; 19 | import java.util.concurrent.LinkedBlockingQueue; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * All listeners registered at the {@link ZkClient} will be notified from this event thread. This is to prevent 28 | * dead-lock situations. The {@link ZkClient} pulls some information out of the {@link ZooKeeper} events to signal 29 | * {@link ZkLock} conditions. Re-using the {@link ZooKeeper} event thread to also notify {@link ZkClient} listeners, 30 | * would stop the ZkClient from receiving events from {@link ZooKeeper} as soon as one of the listeners blocks (because 31 | * it is waiting for something). {@link ZkClient} would then for instance not be able to maintain it's connection state 32 | * anymore. 33 | */ 34 | class ZkEventThread extends Thread { 35 | 36 | private static final Logger LOG = LoggerFactory.getLogger(ZkEventThread.class); 37 | 38 | private BlockingQueue _events = new LinkedBlockingQueue(); 39 | 40 | private static AtomicInteger _eventId = new AtomicInteger(0); 41 | 42 | static abstract class ZkEvent { 43 | 44 | private String _description; 45 | 46 | public ZkEvent(String description) { 47 | _description = description; 48 | } 49 | 50 | public abstract void run() throws Exception; 51 | 52 | @Override 53 | public String toString() { 54 | return "ZkEvent[" + _description + "]"; 55 | } 56 | } 57 | 58 | ZkEventThread(String name) { 59 | setDaemon(true); 60 | setName("ZkClient-EventThread-" + getId() + "-" + name); 61 | } 62 | 63 | @Override 64 | public void run() { 65 | LOG.info("Starting ZkClient event thread."); 66 | try { 67 | while (!isInterrupted()) { 68 | ZkEvent zkEvent = _events.take(); 69 | int eventId = _eventId.incrementAndGet(); 70 | LOG.debug("Delivering event #" + eventId + " " + zkEvent); 71 | try { 72 | zkEvent.run(); 73 | } catch (InterruptedException e) { 74 | interrupt(); 75 | } catch (ZkInterruptedException e) { 76 | interrupt(); 77 | } catch (Throwable e) { 78 | LOG.error("Error handling event " + zkEvent, e); 79 | } 80 | LOG.debug("Delivering event #" + eventId + " done"); 81 | } 82 | } catch (InterruptedException e) { 83 | LOG.info("Terminate ZkClient event thread."); 84 | } 85 | } 86 | 87 | public void send(ZkEvent event) { 88 | if (!isInterrupted()) { 89 | LOG.debug("New event: " + event); 90 | _events.add(event); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/testutil/ZkTestSystem.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient.testutil; 17 | 18 | import static org.mockito.Mockito.mock; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.util.List; 23 | 24 | import org.I0Itec.zkclient.IDefaultNameSpace; 25 | import org.I0Itec.zkclient.ZkClient; 26 | import org.I0Itec.zkclient.ZkServer; 27 | import org.apache.commons.io.FileUtils; 28 | import org.apache.log4j.Logger; 29 | import org.junit.rules.ExternalResource; 30 | 31 | public class ZkTestSystem extends ExternalResource { 32 | 33 | protected static final Logger LOG = Logger.getLogger(ZkTestSystem.class); 34 | 35 | private static int PORT = 10002; 36 | private static ZkTestSystem _instance; 37 | private ZkServer _zkServer; 38 | 39 | private ZkTestSystem() { 40 | LOG.info("~~~~~~~~~~~~~~~ starting zk system ~~~~~~~~~~~~~~~"); 41 | String baseDir = "build/zkdata"; 42 | try { 43 | FileUtils.deleteDirectory(new File(baseDir)); 44 | } catch (IOException e) { 45 | throw new RuntimeException(e); 46 | } 47 | String dataDir = baseDir + "/data"; 48 | String logDir = baseDir + "/log"; 49 | _zkServer = new ZkServer(dataDir, logDir, mock(IDefaultNameSpace.class), PORT); 50 | _zkServer.start(); 51 | LOG.info("~~~~~~~~~~~~~~~ zk system started ~~~~~~~~~~~~~~~"); 52 | } 53 | 54 | @Override 55 | // executed before every test method 56 | protected void before() throws Throwable { 57 | cleanupZk(); 58 | } 59 | 60 | @Override 61 | // executed after every test method 62 | protected void after() { 63 | cleanupZk(); 64 | } 65 | 66 | private void cleanupZk() { 67 | LOG.info("cleanup zk namespace"); 68 | List children = getZkClient().getChildren("/"); 69 | for (String child : children) { 70 | if (!child.equals("zookeeper")) { 71 | getZkClient().deleteRecursive("/" + child); 72 | } 73 | } 74 | LOG.info("unsubscribing " + getZkClient().numberOfListeners() + " listeners"); 75 | getZkClient().unsubscribeAll(); 76 | } 77 | 78 | public static ZkTestSystem getInstance() { 79 | if (_instance == null) { 80 | _instance = new ZkTestSystem(); 81 | _instance.cleanupZk(); 82 | Runtime.getRuntime().addShutdownHook(new Thread() { 83 | @Override 84 | public void run() { 85 | LOG.info("shutting zk down"); 86 | getInstance().getZkServer().shutdown(); 87 | } 88 | }); 89 | } 90 | return _instance; 91 | } 92 | 93 | public ZkServer getZkServer() { 94 | return _zkServer; 95 | } 96 | 97 | public String getZkServerAddress() { 98 | return "localhost:" + getServerPort(); 99 | } 100 | 101 | public ZkClient getZkClient() { 102 | return _zkServer.getZkClient(); 103 | } 104 | 105 | public int getServerPort() { 106 | return PORT; 107 | } 108 | 109 | public ZkClient createZkClient() { 110 | return new ZkClient("localhost:" + PORT); 111 | } 112 | 113 | public void showStructure() { 114 | getZkClient().showFolders(System.out); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/DistributedQueue.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.io.Serializable; 19 | import java.util.List; 20 | 21 | import org.I0Itec.zkclient.exception.ZkNoNodeException; 22 | 23 | public class DistributedQueue { 24 | 25 | private static class Element { 26 | private String _name; 27 | private T _data; 28 | 29 | public Element(String name, T data) { 30 | _name = name; 31 | _data = data; 32 | } 33 | 34 | public String getName() { 35 | return _name; 36 | } 37 | 38 | public T getData() { 39 | return _data; 40 | } 41 | } 42 | 43 | private ZkClient _zkClient; 44 | private String _root; 45 | 46 | private static final String ELEMENT_NAME = "element"; 47 | 48 | public DistributedQueue(ZkClient zkClient, String root) { 49 | _zkClient = zkClient; 50 | _root = root; 51 | } 52 | 53 | public boolean offer(T element) { 54 | try { 55 | _zkClient.createPersistentSequential(_root + "/" + ELEMENT_NAME + "-", element); 56 | } catch (Exception e) { 57 | throw ExceptionUtil.convertToRuntimeException(e); 58 | } 59 | return true; 60 | } 61 | 62 | public T poll() { 63 | while (true) { 64 | Element element = getFirstElement(); 65 | if (element == null) { 66 | return null; 67 | } 68 | 69 | try { 70 | _zkClient.delete(element.getName()); 71 | return element.getData(); 72 | } catch (ZkNoNodeException e) { 73 | // somebody else picked up the element first, so we have to 74 | // retry with the new first element 75 | } catch (Exception e) { 76 | throw ExceptionUtil.convertToRuntimeException(e); 77 | } 78 | } 79 | } 80 | 81 | private String getSmallestElement(List list) { 82 | String smallestElement = list.get(0); 83 | for (String element : list) { 84 | if (element.compareTo(smallestElement) < 0) { 85 | smallestElement = element; 86 | } 87 | } 88 | 89 | return smallestElement; 90 | } 91 | 92 | public boolean isEmpty() { 93 | return _zkClient.getChildren(_root).size() == 0; 94 | } 95 | 96 | @SuppressWarnings("unchecked") 97 | private Element getFirstElement() { 98 | try { 99 | while (true) { 100 | List list = _zkClient.getChildren(_root); 101 | if (list.size() == 0) { 102 | return null; 103 | } 104 | String elementName = getSmallestElement(list); 105 | 106 | try { 107 | return new Element(_root + "/" + elementName, (T) _zkClient.readData(_root + "/" + elementName)); 108 | } catch (ZkNoNodeException e) { 109 | // somebody else picked up the element first, so we have to 110 | // retry with the new first element 111 | } 112 | } 113 | } catch (Exception e) { 114 | throw ExceptionUtil.convertToRuntimeException(e); 115 | } 116 | } 117 | 118 | public T peek() { 119 | Element element = getFirstElement(); 120 | if (element == null) { 121 | return null; 122 | } 123 | return element.getData(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/DistributedQueueTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertNull; 20 | 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | import java.util.Set; 27 | import java.util.Vector; 28 | 29 | import org.junit.After; 30 | import org.junit.Before; 31 | import org.junit.Test; 32 | 33 | public class DistributedQueueTest { 34 | 35 | private ZkServer _zkServer; 36 | private ZkClient _zkClient; 37 | 38 | @Before 39 | public void setUp() throws IOException { 40 | _zkServer = TestUtil.startZkServer("ZkClientTest-testDistributedQueue", 4711); 41 | _zkClient = _zkServer.getZkClient(); 42 | } 43 | 44 | @After 45 | public void tearDown() { 46 | if (_zkServer != null) { 47 | _zkServer.shutdown(); 48 | } 49 | } 50 | 51 | @Test(timeout = 15000) 52 | public void testDistributedQueue() { 53 | _zkClient.createPersistent("/queue"); 54 | 55 | DistributedQueue distributedQueue = new DistributedQueue(_zkClient, "/queue"); 56 | distributedQueue.offer(17L); 57 | distributedQueue.offer(18L); 58 | distributedQueue.offer(19L); 59 | 60 | assertEquals(Long.valueOf(17L), distributedQueue.poll()); 61 | assertEquals(Long.valueOf(18L), distributedQueue.poll()); 62 | assertEquals(Long.valueOf(19L), distributedQueue.poll()); 63 | assertNull(distributedQueue.poll()); 64 | } 65 | 66 | @Test(timeout = 15000) 67 | public void testPeek() { 68 | _zkClient.createPersistent("/queue"); 69 | 70 | DistributedQueue distributedQueue = new DistributedQueue(_zkClient, "/queue"); 71 | distributedQueue.offer(17L); 72 | distributedQueue.offer(18L); 73 | 74 | assertEquals(Long.valueOf(17L), distributedQueue.peek()); 75 | assertEquals(Long.valueOf(17L), distributedQueue.peek()); 76 | assertEquals(Long.valueOf(17L), distributedQueue.poll()); 77 | assertEquals(Long.valueOf(18L), distributedQueue.peek()); 78 | assertEquals(Long.valueOf(18L), distributedQueue.poll()); 79 | assertNull(distributedQueue.peek()); 80 | } 81 | 82 | @Test(timeout = 30000) 83 | public void testMultipleReadingThreads() throws InterruptedException { 84 | _zkClient.createPersistent("/queue"); 85 | 86 | final DistributedQueue distributedQueue = new DistributedQueue(_zkClient, "/queue"); 87 | 88 | // insert 100 elements 89 | for (int i = 0; i < 100; i++) { 90 | distributedQueue.offer(new Long(i)); 91 | } 92 | 93 | // 3 reading threads 94 | final Set readElements = Collections.synchronizedSet(new HashSet()); 95 | List threads = new ArrayList(); 96 | final List exceptions = new Vector(); 97 | 98 | for (int i = 0; i < 3; i++) { 99 | Thread thread = new Thread() { 100 | @Override 101 | public void run() { 102 | try { 103 | while (true) { 104 | Long value = distributedQueue.poll(); 105 | if (value == null) { 106 | return; 107 | } 108 | readElements.add(value); 109 | } 110 | } catch (Exception e) { 111 | exceptions.add(e); 112 | e.printStackTrace(); 113 | } 114 | } 115 | }; 116 | threads.add(thread); 117 | thread.start(); 118 | } 119 | 120 | for (Thread thread : threads) { 121 | thread.join(); 122 | } 123 | 124 | assertEquals(0, exceptions.size()); 125 | assertEquals(100, readElements.size()); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/TestUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.mockito.Mockito.mock; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.util.concurrent.Callable; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | import org.apache.commons.io.FileUtils; 26 | import org.junit.rules.TemporaryFolder; 27 | import org.mockito.exceptions.base.MockitoAssertionError; 28 | 29 | public class TestUtil { 30 | 31 | /** 32 | * This waits until the provided {@link Callable} returns an object that is equals to the given expected value or 33 | * the timeout has been reached. In both cases this method will return the return value of the latest 34 | * {@link Callable} execution. 35 | * 36 | * @param expectedValue 37 | * The expected value of the callable. 38 | * @param callable 39 | * The callable. 40 | * @param 41 | * The return type of the callable. 42 | * @param timeUnit 43 | * The timeout timeunit. 44 | * @param timeout 45 | * The timeout. 46 | * @return the return value of the latest {@link Callable} execution. 47 | * @throws Exception 48 | * @throws InterruptedException 49 | */ 50 | public static T waitUntil(T expectedValue, Callable callable, TimeUnit timeUnit, long timeout) throws Exception { 51 | long startTime = System.currentTimeMillis(); 52 | do { 53 | T actual = callable.call(); 54 | if (expectedValue.equals(actual)) { 55 | return actual; 56 | } 57 | if (System.currentTimeMillis() > startTime + timeUnit.toMillis(timeout)) { 58 | return actual; 59 | } 60 | Thread.sleep(50); 61 | } while (true); 62 | } 63 | 64 | /** 65 | * This waits until a mockito verification passed (which is provided in the runnable). This waits until the 66 | * virification passed or the timeout has been reached. If the timeout has been reached this method will rethrow the 67 | * {@link MockitoAssertionError} that comes from the mockito verification code. 68 | * 69 | * @param runnable 70 | * The runnable containing the mockito verification. 71 | * @param timeUnit 72 | * The timeout timeunit. 73 | * @param timeout 74 | * The timeout. 75 | * @throws InterruptedException 76 | */ 77 | public static void waitUntilVerified(Runnable runnable, TimeUnit timeUnit, int timeout) throws InterruptedException { 78 | long startTime = System.currentTimeMillis(); 79 | do { 80 | MockitoAssertionError exception = null; 81 | try { 82 | runnable.run(); 83 | } catch (MockitoAssertionError e) { 84 | exception = e; 85 | } 86 | if (exception == null) { 87 | return; 88 | } 89 | if (System.currentTimeMillis() > startTime + timeUnit.toMillis(timeout)) { 90 | throw exception; 91 | } 92 | Thread.sleep(50); 93 | } while (true); 94 | } 95 | 96 | public static ZkServer startZkServer(TemporaryFolder temporaryFolder, int port) throws IOException { 97 | File dataFolder = temporaryFolder.newFolder("data"); 98 | File logFolder = temporaryFolder.newFolder("log"); 99 | return startServer(port, dataFolder.getAbsolutePath(), logFolder.getAbsolutePath()); 100 | } 101 | 102 | public static ZkServer startZkServer(String testName, int port) throws IOException { 103 | String dataPath = "./build/test/" + testName + "/data"; 104 | String logPath = "./build/test/" + testName + "/log"; 105 | FileUtils.deleteDirectory(new File(dataPath)); 106 | FileUtils.deleteDirectory(new File(logPath)); 107 | return startServer(port, dataPath, logPath); 108 | } 109 | 110 | private static ZkServer startServer(int port, String dataPath, String logPath) { 111 | ZkServer zkServer = new ZkServer(dataPath, logPath, mock(IDefaultNameSpace.class), port, ZkServer.DEFAULT_TICK_TIME, 100); 112 | zkServer.start(); 113 | return zkServer; 114 | } 115 | } -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/NetworkUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.io.IOException; 19 | import java.net.ConnectException; 20 | import java.net.InetAddress; 21 | import java.net.NetworkInterface; 22 | import java.net.Socket; 23 | import java.net.SocketException; 24 | import java.net.UnknownHostException; 25 | import java.util.Enumeration; 26 | import java.util.HashSet; 27 | import java.util.Set; 28 | 29 | public class NetworkUtil { 30 | 31 | public final static String OVERWRITE_HOSTNAME_SYSTEM_PROPERTY = "zkclient.hostname.overwritten"; 32 | 33 | public static String[] getLocalHostNames() { 34 | final Set hostNames = new HashSet(); 35 | // we add localhost to this set manually, because if the ip 127.0.0.1 is 36 | // configured with more than one name in the /etc/hosts, only the first 37 | // name 38 | // is returned 39 | hostNames.add("localhost"); 40 | try { 41 | final Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); 42 | for (final Enumeration ifaces = networkInterfaces; ifaces.hasMoreElements();) { 43 | final NetworkInterface iface = ifaces.nextElement(); 44 | InetAddress ia = null; 45 | for (final Enumeration ips = iface.getInetAddresses(); ips.hasMoreElements();) { 46 | ia = ips.nextElement(); 47 | hostNames.add(ia.getCanonicalHostName()); 48 | hostNames.add(ipToString(ia.getAddress())); 49 | } 50 | } 51 | } catch (final SocketException e) { 52 | throw new RuntimeException("unable to retrieve host names of localhost"); 53 | } 54 | return hostNames.toArray(new String[hostNames.size()]); 55 | } 56 | 57 | private static String ipToString(final byte[] bytes) { 58 | final StringBuffer addrStr = new StringBuffer(); 59 | for (int cnt = 0; cnt < bytes.length; cnt++) { 60 | final int uByte = bytes[cnt] < 0 ? bytes[cnt] + 256 : bytes[cnt]; 61 | addrStr.append(uByte); 62 | if (cnt < 3) 63 | addrStr.append('.'); 64 | } 65 | return addrStr.toString(); 66 | } 67 | 68 | public static int hostNamesInList(final String serverList, final String[] hostNames) { 69 | final String[] serverNames = serverList.split(","); 70 | for (int i = 0; i < hostNames.length; i++) { 71 | final String hostname = hostNames[i]; 72 | for (int j = 0; j < serverNames.length; j++) { 73 | final String serverNameAndPort = serverNames[j]; 74 | final String serverName = serverNameAndPort.split(":")[0]; 75 | if (serverName.equalsIgnoreCase(hostname)) { 76 | return j; 77 | } 78 | } 79 | } 80 | return -1; 81 | } 82 | 83 | public static boolean hostNameInArray(final String[] hostNames, final String hostName) { 84 | for (final String name : hostNames) { 85 | if (name.equalsIgnoreCase(hostName)) { 86 | return true; 87 | } 88 | } 89 | return false; 90 | } 91 | 92 | public static boolean isPortFree(int port) { 93 | try { 94 | Socket socket = new Socket("localhost", port); 95 | socket.close(); 96 | return false; 97 | } catch (ConnectException e) { 98 | return true; 99 | } catch (SocketException e) { 100 | if (e.getMessage().equals("Connection reset by peer")) { 101 | return true; 102 | } 103 | throw new RuntimeException(e); 104 | } catch (UnknownHostException e) { 105 | throw new RuntimeException(e); 106 | } catch (IOException e) { 107 | throw new RuntimeException(e); 108 | } 109 | } 110 | 111 | public static String getLocalhostName() { 112 | String property = System.getProperty(OVERWRITE_HOSTNAME_SYSTEM_PROPERTY); 113 | if (property != null && property.trim().length() > 0) { 114 | return property; 115 | } 116 | try { 117 | return InetAddress.getLocalHost().getHostName(); 118 | } catch (final UnknownHostException e) { 119 | throw new RuntimeException("unable to retrieve localhost name"); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ContentWatcherTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import java.util.concurrent.Callable; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import org.apache.log4j.Logger; 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | 28 | public class ContentWatcherTest { 29 | 30 | private static final Logger LOG = Logger.getLogger(ContentWatcherTest.class); 31 | 32 | private static final String FILE_NAME = "/ContentWatcherTest"; 33 | private ZkServer _zkServer; 34 | private ZkClient _zkClient; 35 | 36 | @Before 37 | public void setUp() throws Exception { 38 | LOG.info("------------ BEFORE -------------"); 39 | _zkServer = TestUtil.startZkServer("ContentWatcherTest", 4711); 40 | _zkClient = _zkServer.getZkClient(); 41 | } 42 | 43 | @After 44 | public void tearDown() throws Exception { 45 | if (_zkServer != null) { 46 | _zkServer.shutdown(); 47 | } 48 | LOG.info("------------ AFTER -------------"); 49 | } 50 | 51 | @Test 52 | public void testGetContent() throws Exception { 53 | LOG.info("--- testGetContent"); 54 | _zkClient.createPersistent(FILE_NAME, "a"); 55 | final ContentWatcher watcher = new ContentWatcher(_zkClient, FILE_NAME); 56 | watcher.start(); 57 | assertEquals("a", watcher.getContent()); 58 | 59 | // update the content 60 | _zkClient.writeData(FILE_NAME, "b"); 61 | 62 | String contentFromWatcher = TestUtil.waitUntil("b", new Callable() { 63 | 64 | @Override 65 | public String call() throws Exception { 66 | return watcher.getContent(); 67 | } 68 | }, TimeUnit.SECONDS, 5); 69 | 70 | assertEquals("b", contentFromWatcher); 71 | watcher.stop(); 72 | } 73 | 74 | @Test 75 | public void testGetContentWaitTillCreated() throws InterruptedException { 76 | LOG.info("--- testGetContentWaitTillCreated"); 77 | final Holder contentHolder = new Holder(); 78 | 79 | Thread thread = new Thread() { 80 | @Override 81 | public void run() { 82 | ContentWatcher watcher = new ContentWatcher(_zkClient, FILE_NAME); 83 | try { 84 | watcher.start(); 85 | contentHolder.set(watcher.getContent()); 86 | watcher.stop(); 87 | } catch (Exception e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | }; 92 | 93 | thread.start(); 94 | 95 | // create content after 200ms 96 | Thread.sleep(200); 97 | _zkClient.createPersistent(FILE_NAME, "aaa"); 98 | 99 | // we give the thread some time to pick up the change 100 | thread.join(1000); 101 | assertEquals("aaa", contentHolder.get()); 102 | } 103 | 104 | @Test 105 | public void testHandlingNullContent() throws InterruptedException { 106 | LOG.info("--- testHandlingNullContent"); 107 | _zkClient.createPersistent(FILE_NAME, null); 108 | ContentWatcher watcher = new ContentWatcher(_zkClient, FILE_NAME); 109 | watcher.start(); 110 | assertEquals(null, watcher.getContent()); 111 | watcher.stop(); 112 | } 113 | 114 | @Test(timeout = 20000) 115 | public void testHandlingOfConnectionLoss() throws Exception { 116 | LOG.info("--- testHandlingOfConnectionLoss"); 117 | final Gateway gateway = new Gateway(4712, 4711); 118 | gateway.start(); 119 | final ZkClient zkClient = new ZkClient("localhost:4712", 30000); 120 | 121 | // disconnect 122 | gateway.stop(); 123 | 124 | // reconnect after 250ms and create file with content 125 | new Thread() { 126 | @Override 127 | public void run() { 128 | try { 129 | Thread.sleep(250); 130 | gateway.start(); 131 | zkClient.createPersistent(FILE_NAME, "aaa"); 132 | zkClient.writeData(FILE_NAME, "b"); 133 | } catch (Exception e) { 134 | // ignore 135 | } 136 | } 137 | }.start(); 138 | 139 | final ContentWatcher watcher = new ContentWatcher(zkClient, FILE_NAME); 140 | watcher.start(); 141 | 142 | TestUtil.waitUntil("b", new Callable() { 143 | 144 | @Override 145 | public String call() throws Exception { 146 | return watcher.getContent(); 147 | } 148 | }, TimeUnit.SECONDS, 5); 149 | assertEquals("b", watcher.getContent()); 150 | 151 | watcher.stop(); 152 | zkClient.close(); 153 | gateway.stop(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/ZkConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.io.IOException; 19 | import java.util.AbstractMap.SimpleEntry; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.concurrent.locks.Lock; 23 | import java.util.concurrent.locks.ReentrantLock; 24 | 25 | import org.I0Itec.zkclient.exception.ZkException; 26 | import org.apache.zookeeper.CreateMode; 27 | import org.apache.zookeeper.KeeperException; 28 | import org.apache.zookeeper.Op; 29 | import org.apache.zookeeper.OpResult; 30 | import org.apache.zookeeper.Watcher; 31 | import org.apache.zookeeper.ZooDefs.Ids; 32 | import org.apache.zookeeper.ZooKeeper; 33 | import org.apache.zookeeper.ZooKeeper.States; 34 | import org.apache.zookeeper.data.ACL; 35 | import org.apache.zookeeper.data.Stat; 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | 39 | public class ZkConnection implements IZkConnection { 40 | 41 | private static final Logger LOG = LoggerFactory.getLogger(ZkConnection.class); 42 | 43 | /** It is recommended to use quite large sessions timeouts for ZooKeeper. */ 44 | private static final int DEFAULT_SESSION_TIMEOUT = 30000; 45 | 46 | private ZooKeeper _zk = null; 47 | private final Lock _zookeeperLock = new ReentrantLock(); 48 | 49 | private final String _servers; 50 | private final int _sessionTimeOut; 51 | 52 | public ZkConnection(String zkServers) { 53 | this(zkServers, DEFAULT_SESSION_TIMEOUT); 54 | } 55 | 56 | public ZkConnection(String zkServers, int sessionTimeOut) { 57 | _servers = zkServers; 58 | _sessionTimeOut = sessionTimeOut; 59 | } 60 | 61 | @Override 62 | public void connect(Watcher watcher) { 63 | _zookeeperLock.lock(); 64 | try { 65 | if (_zk != null) { 66 | throw new IllegalStateException("zk client has already been started"); 67 | } 68 | try { 69 | LOG.debug("Creating new ZookKeeper instance to connect to " + _servers + "."); 70 | _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher); 71 | } catch (IOException e) { 72 | throw new ZkException("Unable to connect to " + _servers, e); 73 | } 74 | } finally { 75 | _zookeeperLock.unlock(); 76 | } 77 | } 78 | 79 | @Override 80 | public void close() throws InterruptedException { 81 | _zookeeperLock.lock(); 82 | try { 83 | if (_zk != null) { 84 | LOG.debug("Closing ZooKeeper connected to " + _servers); 85 | _zk.close(); 86 | _zk = null; 87 | } 88 | } finally { 89 | _zookeeperLock.unlock(); 90 | } 91 | } 92 | 93 | @Override 94 | public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException { 95 | return _zk.create(path, data, Ids.OPEN_ACL_UNSAFE, mode); 96 | } 97 | 98 | @Override 99 | public String create(String path, byte[] data, List acl, CreateMode mode) throws KeeperException, InterruptedException { 100 | return _zk.create(path, data, acl, mode); 101 | } 102 | 103 | @Override 104 | public void delete(String path) throws InterruptedException, KeeperException { 105 | _zk.delete(path, -1); 106 | } 107 | 108 | @Override 109 | public void delete(String path, int version) throws InterruptedException, KeeperException { 110 | _zk.delete(path, version); 111 | } 112 | 113 | @Override 114 | public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException { 115 | return _zk.exists(path, watch) != null; 116 | } 117 | 118 | @Override 119 | public List getChildren(final String path, final boolean watch) throws KeeperException, InterruptedException { 120 | return _zk.getChildren(path, watch); 121 | } 122 | 123 | @Override 124 | public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException { 125 | return _zk.getData(path, watch, stat); 126 | } 127 | 128 | public void writeData(String path, byte[] data) throws KeeperException, InterruptedException { 129 | writeData(path, data, -1); 130 | } 131 | 132 | @Override 133 | public void writeData(String path, byte[] data, int version) throws KeeperException, InterruptedException { 134 | _zk.setData(path, data, version); 135 | } 136 | 137 | @Override 138 | public Stat writeDataReturnStat(String path, byte[] data, int version) throws KeeperException, InterruptedException { 139 | return _zk.setData(path, data, version); 140 | } 141 | 142 | @Override 143 | public States getZookeeperState() { 144 | return _zk != null ? _zk.getState() : null; 145 | } 146 | 147 | public ZooKeeper getZookeeper() { 148 | return _zk; 149 | } 150 | 151 | @Override 152 | public long getCreateTime(String path) throws KeeperException, InterruptedException { 153 | Stat stat = _zk.exists(path, false); 154 | if (stat != null) { 155 | return stat.getCtime(); 156 | } 157 | return -1; 158 | } 159 | 160 | @Override 161 | public String getServers() { 162 | return _servers; 163 | } 164 | 165 | @Override 166 | public List multi(Iterable ops) throws KeeperException, InterruptedException { 167 | return _zk.multi(ops); 168 | } 169 | 170 | @Override 171 | public void addAuthInfo(String scheme, byte[] auth) { 172 | _zk.addAuthInfo(scheme, auth); 173 | } 174 | 175 | @Override 176 | public void setAcl(String path, List acl, int version) throws KeeperException, InterruptedException { 177 | _zk.setACL(path, acl, version); 178 | } 179 | 180 | @Override 181 | public Map.Entry, Stat> getAcl(String path) throws KeeperException, InterruptedException { 182 | Stat stat = new Stat(); 183 | List acl = _zk.getACL(path, stat); 184 | return new SimpleEntry(acl, stat); 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/GatewayThread.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.io.Closeable; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | import java.net.ServerSocket; 23 | import java.net.Socket; 24 | import java.net.SocketException; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Vector; 28 | import java.util.concurrent.locks.Condition; 29 | import java.util.concurrent.locks.Lock; 30 | import java.util.concurrent.locks.ReentrantLock; 31 | 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | public class GatewayThread extends Thread { 36 | 37 | protected final static Logger LOG = LoggerFactory.getLogger(GatewayThread.class); 38 | 39 | private final int _port; 40 | private final int _destinationPort; 41 | private ServerSocket _serverSocket; 42 | private Lock _lock = new ReentrantLock(); 43 | private Condition _runningCondition = _lock.newCondition(); 44 | private boolean _running = false; 45 | 46 | public GatewayThread(int port, int destinationPort) { 47 | _port = port; 48 | _destinationPort = destinationPort; 49 | setDaemon(true); 50 | } 51 | 52 | @Override 53 | public void run() { 54 | final List runningThreads = new Vector(); 55 | try { 56 | LOG.info("Starting gateway on port " + _port + " pointing to port " + _destinationPort); 57 | _serverSocket = new ServerSocket(_port); 58 | _lock.lock(); 59 | try { 60 | _running = true; 61 | _runningCondition.signalAll(); 62 | } finally { 63 | _lock.unlock(); 64 | } 65 | while (true) { 66 | final Socket socket = _serverSocket.accept(); 67 | LOG.info("new client is connected " + socket.getInetAddress()); 68 | final InputStream incomingInputStream = socket.getInputStream(); 69 | final OutputStream incomingOutputStream = socket.getOutputStream(); 70 | 71 | final Socket outgoingSocket; 72 | try { 73 | outgoingSocket = new Socket("localhost", _destinationPort); 74 | } catch (Exception e) { 75 | LOG.warn("could not connect to " + _destinationPort); 76 | continue; 77 | } 78 | final InputStream outgoingInputStream = outgoingSocket.getInputStream(); 79 | final OutputStream outgoingOutputStream = outgoingSocket.getOutputStream(); 80 | 81 | Thread writeThread = new Thread() { 82 | @Override 83 | public void run() { 84 | runningThreads.add(this); 85 | try { 86 | int read = -1; 87 | while ((read = incomingInputStream.read()) != -1) { 88 | outgoingOutputStream.write(read); 89 | } 90 | } catch (IOException e) { 91 | // ignore 92 | } finally { 93 | closeQuietly(outgoingOutputStream); 94 | runningThreads.remove(this); 95 | } 96 | } 97 | 98 | @Override 99 | public void interrupt() { 100 | try { 101 | socket.close(); 102 | outgoingSocket.close(); 103 | } catch (IOException e) { 104 | LOG.error("error on stopping closing sockets", e); 105 | } 106 | 107 | super.interrupt(); 108 | } 109 | }; 110 | 111 | Thread readThread = new Thread() { 112 | @Override 113 | public void run() { 114 | runningThreads.add(this); 115 | try { 116 | int read = -1; 117 | while ((read = outgoingInputStream.read()) != -1) { 118 | incomingOutputStream.write(read); 119 | } 120 | } catch (IOException e) { 121 | // ignore 122 | } finally { 123 | closeQuietly(incomingOutputStream); 124 | runningThreads.remove(this); 125 | } 126 | } 127 | }; 128 | 129 | writeThread.setDaemon(true); 130 | readThread.setDaemon(true); 131 | 132 | writeThread.start(); 133 | readThread.start(); 134 | } 135 | } catch (SocketException e) { 136 | if (!_running) { 137 | throw ExceptionUtil.convertToRuntimeException(e); 138 | } 139 | LOG.info("Stopping gateway"); 140 | } catch (Exception e) { 141 | LOG.error("error on gateway execution", e); 142 | } 143 | 144 | for (Thread thread : new ArrayList(runningThreads)) { 145 | thread.interrupt(); 146 | try { 147 | thread.join(); 148 | } catch (InterruptedException e) { 149 | // ignore 150 | } 151 | } 152 | } 153 | 154 | protected void closeQuietly(Closeable closable) { 155 | try { 156 | closable.close(); 157 | } catch (IOException e) { 158 | // ignore 159 | } 160 | } 161 | 162 | @Override 163 | public void interrupt() { 164 | try { 165 | _serverSocket.close(); 166 | } catch (Exception cE) { 167 | LOG.error("error on stopping gateway", cE); 168 | } 169 | super.interrupt(); 170 | } 171 | 172 | public void interruptAndJoin() throws InterruptedException { 173 | interrupt(); 174 | join(); 175 | } 176 | 177 | public void awaitUp() { 178 | _lock.lock(); 179 | try { 180 | while (!_running) { 181 | _runningCondition.await(); 182 | } 183 | } catch (InterruptedException e) { 184 | Thread.currentThread().interrupt(); 185 | } finally { 186 | _lock.unlock(); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/ZkServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.net.InetSocketAddress; 21 | import java.util.Arrays; 22 | 23 | import javax.annotation.PostConstruct; 24 | import javax.annotation.PreDestroy; 25 | 26 | import org.I0Itec.zkclient.exception.ZkException; 27 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 28 | import org.apache.zookeeper.server.NIOServerCnxnFactory; 29 | import org.apache.zookeeper.server.ZooKeeperServer; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | public class ZkServer { 34 | 35 | private final static Logger LOG = LoggerFactory.getLogger(ZkServer.class); 36 | 37 | public static final int DEFAULT_PORT = 2181; 38 | public static final int DEFAULT_TICK_TIME = 5000; 39 | public static final int DEFAULT_MIN_SESSION_TIMEOUT = 2 * DEFAULT_TICK_TIME; 40 | 41 | private String _dataDir; 42 | private String _logDir; 43 | 44 | private IDefaultNameSpace _defaultNameSpace; 45 | 46 | private ZooKeeperServer _zk; 47 | private NIOServerCnxnFactory _nioFactory; 48 | private ZkClient _zkClient; 49 | private int _port; 50 | private int _tickTime; 51 | private int _minSessionTimeout; 52 | 53 | public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace) { 54 | this(dataDir, logDir, defaultNameSpace, DEFAULT_PORT); 55 | } 56 | 57 | public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace, int port) { 58 | this(dataDir, logDir, defaultNameSpace, port, DEFAULT_TICK_TIME); 59 | } 60 | 61 | public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace, int port, int tickTime) { 62 | this(dataDir, logDir, defaultNameSpace, port, tickTime, DEFAULT_MIN_SESSION_TIMEOUT); 63 | } 64 | 65 | public ZkServer(String dataDir, String logDir, IDefaultNameSpace defaultNameSpace, int port, int tickTime, int minSessionTimeout) { 66 | _dataDir = dataDir; 67 | _logDir = logDir; 68 | _defaultNameSpace = defaultNameSpace; 69 | _port = port; 70 | _tickTime = tickTime; 71 | _minSessionTimeout = minSessionTimeout; 72 | } 73 | 74 | public int getPort() { 75 | return _port; 76 | } 77 | 78 | @PostConstruct 79 | public void start() { 80 | final String[] localHostNames = NetworkUtil.getLocalHostNames(); 81 | String names = ""; 82 | for (int i = 0; i < localHostNames.length; i++) { 83 | final String name = localHostNames[i]; 84 | names += " " + name; 85 | if (i + 1 != localHostNames.length) { 86 | names += ","; 87 | } 88 | } 89 | LOG.info("Starting ZkServer on: [" + names + "] port " + _port + "..."); 90 | try { 91 | startZooKeeperServer(); 92 | _zkClient = new ZkClient("localhost:" + _port, 10000); 93 | _defaultNameSpace.createDefaultNameSpace(_zkClient); 94 | } catch (RuntimeException e) { 95 | shutdown(); 96 | throw e; 97 | } 98 | } 99 | 100 | private void startZooKeeperServer() { 101 | final String[] localhostHostNames = NetworkUtil.getLocalHostNames(); 102 | final String servers = "localhost:" + _port; 103 | // check if this server needs to start a _client server. 104 | int pos = -1; 105 | LOG.debug("check if hostNames " + servers + " is in list: " + Arrays.asList(localhostHostNames)); 106 | if ((pos = NetworkUtil.hostNamesInList(servers, localhostHostNames)) != -1) { 107 | // yes this server needs to start a zookeeper server 108 | final String[] hosts = servers.split(","); 109 | final String[] hostSplitted = hosts[pos].split(":"); 110 | int port = _port; 111 | if (hostSplitted.length > 1) { 112 | port = Integer.parseInt(hostSplitted[1]); 113 | } 114 | // check if this machine is already something running.. 115 | if (NetworkUtil.isPortFree(port)) { 116 | final File dataDir = new File(_dataDir); 117 | final File dataLogDir = new File(_logDir); 118 | dataDir.mkdirs(); 119 | dataLogDir.mkdirs(); 120 | 121 | if (hosts.length > 1) { 122 | // multiple zk servers 123 | LOG.info("Start distributed zookeeper server..."); 124 | throw new IllegalArgumentException("Unable to start distributed zookeeper server"); 125 | } 126 | // single zk server 127 | LOG.info("Start single zookeeper server..."); 128 | LOG.info("data dir: " + dataDir.getAbsolutePath()); 129 | LOG.info("data log dir: " + dataLogDir.getAbsolutePath()); 130 | LOG.info("JAAS login file: " + System.getProperty("java.security.auth.login.config", "none")); 131 | startSingleZkServer(_tickTime, dataDir, dataLogDir, port); 132 | } else { 133 | throw new IllegalStateException("Zookeeper port " + port + " was already in use. Running in single machine mode?"); 134 | } 135 | } 136 | } 137 | 138 | private void startSingleZkServer(final int tickTime, final File dataDir, final File dataLogDir, final int port) { 139 | try { 140 | _zk = new ZooKeeperServer(dataDir, dataLogDir, tickTime); 141 | _zk.setMinSessionTimeout(_minSessionTimeout); 142 | _nioFactory = new NIOServerCnxnFactory(); 143 | int maxClientConnections = 0; // 0 means unlimited 144 | _nioFactory.configure(new InetSocketAddress(port), maxClientConnections); 145 | _nioFactory.startup(_zk); 146 | } catch (IOException e) { 147 | throw new ZkException("Unable to start single ZooKeeper server.", e); 148 | } catch (InterruptedException e) { 149 | throw new ZkInterruptedException(e); 150 | } 151 | } 152 | 153 | @PreDestroy 154 | public void shutdown() { 155 | LOG.info("Shutting down ZkServer..."); 156 | try { 157 | if(_zkClient != null) { 158 | _zkClient.close(); 159 | } 160 | } catch (ZkException e) { 161 | LOG.warn("Error on closing zkclient: " + e.getClass().getName()); 162 | } 163 | if (_nioFactory != null) { 164 | _nioFactory.shutdown(); 165 | try { 166 | _nioFactory.join(); 167 | } catch (InterruptedException e) { 168 | Thread.currentThread().interrupt(); 169 | } 170 | _nioFactory = null; 171 | } 172 | if (_zk != null) { 173 | _zk.shutdown(); 174 | _zk = null; 175 | } 176 | LOG.info("Shutting down ZkServer...done"); 177 | } 178 | 179 | public ZkClient getZkClient() { 180 | return _zkClient; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/SaslAuthenticatedTest.java: -------------------------------------------------------------------------------- 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, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package org.I0Itec.zkclient; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.junit.Assert.fail; 23 | 24 | import java.io.File; 25 | import java.io.FileOutputStream; 26 | import java.io.IOException; 27 | 28 | import javax.security.auth.login.Configuration; 29 | 30 | import org.I0Itec.zkclient.exception.ZkAuthFailedException; 31 | import org.I0Itec.zkclient.exception.ZkException; 32 | import org.I0Itec.zkclient.exception.ZkTimeoutException; 33 | import org.apache.log4j.Logger; 34 | import org.apache.zookeeper.ZooDefs.Ids; 35 | import org.junit.After; 36 | import org.junit.Before; 37 | import org.junit.Rule; 38 | import org.junit.Test; 39 | import org.junit.rules.TemporaryFolder; 40 | 41 | public class SaslAuthenticatedTest { 42 | protected static final Logger LOG = Logger.getLogger(SaslAuthenticatedTest.class); 43 | static final String ZK_AUTH_PROVIDER = "zookeeper.authProvider.1"; 44 | static final String ZK_ALLOW_FAILED_SASL = "zookeeper.allowSaslFailedClients"; 45 | 46 | @Rule 47 | public TemporaryFolder _temporaryFolder = new TemporaryFolder(); 48 | private int _port = 4700; 49 | private ZkClient _client; 50 | private ZkServer _zkServer; 51 | private String _zkServerContextName = "Server"; 52 | private String _zkClientContextName = "Client"; 53 | private String _userSuperPasswd = "adminpasswd"; 54 | private String _userServerSide = "fpj"; 55 | private String _userClientSide = "fpj"; 56 | private String _userServerSidePasswd = "fpjsecret"; 57 | private String _userClientSidePasswd = "fpjsecret"; 58 | private String _zkModule = "org.apache.zookeeper.server.auth.DigestLoginModule"; 59 | 60 | private String createJaasFile() throws IOException { 61 | File jaasFile = _temporaryFolder.newFile("jaas.conf"); 62 | FileOutputStream jaasOutputStream = new java.io.FileOutputStream(jaasFile); 63 | jaasOutputStream.write(String.format("%s {\n\t%s required\n", _zkServerContextName, _zkModule).getBytes()); 64 | jaasOutputStream.write(String.format("\tuser_super=\"%s\"\n", _userSuperPasswd).getBytes()); 65 | jaasOutputStream.write(String.format("\tuser_%s=\"%s\";\n};\n\n", _userServerSide, _userServerSidePasswd).getBytes()); 66 | jaasOutputStream.write(String.format("%s {\n\t%s required\n", _zkClientContextName, _zkModule).getBytes()); 67 | jaasOutputStream.write(String.format("\tusername=\"%s\"\n", _userClientSide).getBytes()); 68 | jaasOutputStream.write(String.format("\tpassword=\"%s\";\n};", _userClientSidePasswd).getBytes()); 69 | jaasOutputStream.close(); 70 | return jaasFile.getAbsolutePath(); 71 | } 72 | 73 | @Before 74 | public void setUp() throws IOException { 75 | // Reset all variables used for the jaas login file 76 | _zkServerContextName = "Server"; 77 | _zkClientContextName = "Client"; 78 | _userSuperPasswd = "adminpasswd"; 79 | _userServerSide = "fpj"; 80 | _userClientSide = "fpj"; 81 | _userServerSidePasswd = "fpjsecret"; 82 | _userClientSidePasswd = "fpjsecret"; 83 | _zkModule = "org.apache.zookeeper.server.auth.DigestLoginModule"; 84 | } 85 | 86 | @After 87 | public void tearDown() { 88 | if (_client != null) { 89 | _client.close(); 90 | } 91 | if (_zkServer != null) { 92 | _zkServer.shutdown(); 93 | } 94 | System.clearProperty(ZK_AUTH_PROVIDER); 95 | System.clearProperty(ZkClient.JAVA_LOGIN_CONFIG_PARAM); 96 | Configuration.setConfiguration(null); 97 | } 98 | 99 | private void bootstrap() throws IOException { 100 | Configuration.setConfiguration(null); 101 | String jaasFileName = createJaasFile(); 102 | System.setProperty(ZK_AUTH_PROVIDER, "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); 103 | System.setProperty(ZkClient.JAVA_LOGIN_CONFIG_PARAM, jaasFileName); 104 | _zkServer = TestUtil.startZkServer(_temporaryFolder, _port); 105 | _client = _zkServer.getZkClient(); 106 | } 107 | 108 | private void bootstrapWithAuthFailure() throws IOException { 109 | _userServerSide = "otheruser"; 110 | bootstrap(); 111 | } 112 | 113 | /** 114 | * Tests that a connection authenticates successfully. 115 | * 116 | * @throws IOException 117 | */ 118 | @Test 119 | public void testConnection() throws IOException { 120 | bootstrap(); 121 | _client.createPersistent("/test", new byte[0], Ids.CREATOR_ALL_ACL); 122 | assertThat(_client.exists("/test")).isTrue(); 123 | } 124 | 125 | /** 126 | * Tests that ZkClient throws an exception in the case ZooKeeper keeps dropping the connection due to authentication 127 | * failures. 128 | * 129 | * @throws IOException 130 | */ 131 | @Test 132 | public void testAuthFailure() throws IOException { 133 | try { 134 | bootstrapWithAuthFailure(); 135 | fail("Expected to fail!"); 136 | } catch (ZkException e) { 137 | assertThat(e).isInstanceOf(ZkTimeoutException.class); 138 | } 139 | } 140 | 141 | /** 142 | * Tests that ZkClient spots the AuthFailed event in the case the property to allow failed SASL connections is 143 | * enabled. 144 | * 145 | * @throws IOException 146 | */ 147 | @Test 148 | public void testAuthFailure_AllowFailedSasl() throws IOException { 149 | System.setProperty(ZK_ALLOW_FAILED_SASL, "true"); 150 | try { 151 | bootstrapWithAuthFailure(); 152 | fail("Expected to fail!"); 153 | } catch (ZkException e) { 154 | assertThat(e).isInstanceOf(ZkAuthFailedException.class); 155 | } finally { 156 | System.clearProperty(ZK_ALLOW_FAILED_SASL); 157 | } 158 | } 159 | 160 | /** 161 | * Tests that ZkClient spots the AuthFailed event in the case the property to allow failed SASL connections is 162 | * enabled. 163 | * 164 | * @throws IOException 165 | */ 166 | @Test 167 | public void testAuthFailure_DisabledSasl() throws IOException { 168 | System.setProperty(ZkClient.ZK_SASL_CLIENT, "false"); 169 | try { 170 | bootstrapWithAuthFailure(); 171 | } finally { 172 | System.clearProperty(ZkClient.ZK_SASL_CLIENT); 173 | } 174 | } 175 | 176 | @Test 177 | public void testUnauthenticatedClient() throws IOException { 178 | ZkClient unauthed = null; 179 | try { 180 | bootstrap(); 181 | System.clearProperty(ZkClient.JAVA_LOGIN_CONFIG_PARAM); 182 | System.setProperty("zookeeper.sasl.client", "true"); 183 | unauthed = new ZkClient("localhost:" + _port, 6000); 184 | unauthed.createPersistent("/test", new byte[0], Ids.OPEN_ACL_UNSAFE); 185 | } finally { 186 | if (unauthed != null) { 187 | unauthed.close(); 188 | } 189 | } 190 | } 191 | 192 | @Test 193 | public void testNoZkJaasFile() throws IOException { 194 | try { 195 | _zkClientContextName = "OtherClient"; 196 | _zkServerContextName = "OtherServer"; 197 | bootstrap(); 198 | _client.createPersistent("/test", new byte[0], Ids.OPEN_ACL_UNSAFE); 199 | assertThat(_client.exists("/test")).isTrue(); 200 | } catch (ZkAuthFailedException e) { 201 | fail("Caught ZkAuthFailed exception and was not expecting it"); 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ZkStateChangeTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.concurrent.Callable; 23 | import java.util.concurrent.TimeUnit; 24 | 25 | import org.apache.zookeeper.CreateMode; 26 | import org.apache.zookeeper.KeeperException; 27 | import org.apache.zookeeper.Op; 28 | import org.apache.zookeeper.OpResult; 29 | import org.apache.zookeeper.WatchedEvent; 30 | import org.apache.zookeeper.Watcher; 31 | import org.apache.zookeeper.Watcher.Event.KeeperState; 32 | import org.apache.zookeeper.ZooKeeper.States; 33 | import org.apache.zookeeper.data.ACL; 34 | import org.apache.zookeeper.data.Stat; 35 | import org.junit.After; 36 | import org.junit.Before; 37 | import org.junit.Test; 38 | 39 | public class ZkStateChangeTest { 40 | 41 | private StateOnlyConnection zkConn; 42 | private ZkClient client; 43 | private TestStateListener listener; 44 | 45 | @Before 46 | public void setUp() { 47 | zkConn = new StateOnlyConnection(); 48 | client = new ZkClient(zkConn); 49 | listener = new TestStateListener(); 50 | client.subscribeStateChanges(listener); 51 | } 52 | 53 | @After 54 | public void tearDown() { 55 | client.close(); 56 | } 57 | 58 | @Test 59 | public void testNewSessionEvent() throws Exception { 60 | zkConn.expireSession(); 61 | assertTimed(1, new Callable() { 62 | @Override 63 | public Integer call() throws Exception { 64 | return listener.expiredEvents; 65 | } 66 | }); 67 | 68 | assertTimed(0, new Callable() { 69 | @Override 70 | public Integer call() throws Exception { 71 | return listener.sessionEstablishErrors; 72 | } 73 | }); 74 | 75 | assertTimed(1, new Callable() { 76 | @Override 77 | public Integer call() throws Exception { 78 | return listener.newSessionEvent; 79 | } 80 | }); 81 | } 82 | 83 | @Test 84 | public void testFailConnectEvent() throws Exception { 85 | zkConn.setFailOnConnect(true); 86 | zkConn.expireSession(); 87 | assertTimed(1, new Callable() { 88 | @Override 89 | public Integer call() throws Exception { 90 | return listener.expiredEvents; 91 | } 92 | }); 93 | 94 | assertTimed(1, new Callable() { 95 | @Override 96 | public Integer call() throws Exception { 97 | return listener.sessionEstablishErrors; 98 | } 99 | }); 100 | 101 | assertTimed(0, new Callable() { 102 | @Override 103 | public Integer call() throws Exception { 104 | return listener.newSessionEvent; 105 | } 106 | }); 107 | 108 | client.close(); 109 | } 110 | 111 | private void assertTimed(T expectedVal, Callable condition) throws Exception { 112 | assertEquals(expectedVal, TestUtil.waitUntil(expectedVal, condition, TimeUnit.SECONDS, 5)); 113 | } 114 | 115 | private static class StateOnlyConnection implements IZkConnection { 116 | private Watcher _watcher; 117 | private boolean failOnConnect = false; 118 | 119 | @Override 120 | public void connect(Watcher w) { 121 | _watcher = w; 122 | if (failOnConnect) { 123 | // As as example: 124 | throw new RuntimeException("Testing connection failure"); 125 | } 126 | new Thread() { 127 | @Override 128 | public void run() { 129 | _watcher.process(new WatchedEvent(null, KeeperState.SyncConnected, null)); 130 | } 131 | }.start(); 132 | } 133 | 134 | public void expireSession() { 135 | _watcher.process(new WatchedEvent(null, KeeperState.Expired, null)); 136 | } 137 | 138 | public void setFailOnConnect(boolean failFlag) { 139 | this.failOnConnect = failFlag; 140 | } 141 | 142 | @Override 143 | public void close() throws InterruptedException { 144 | 145 | } 146 | 147 | @Override 148 | public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException { 149 | throw new RuntimeException("not implemented"); 150 | } 151 | 152 | @Override 153 | public String create(String path, byte[] data, List acl, CreateMode mode) throws KeeperException, InterruptedException { 154 | throw new RuntimeException("not implemented"); 155 | } 156 | 157 | @Override 158 | public void delete(String path) throws InterruptedException, KeeperException { 159 | throw new RuntimeException("not implemented"); 160 | } 161 | 162 | @Override 163 | public void delete(String path, int version) throws InterruptedException, KeeperException { 164 | throw new RuntimeException("not implemented"); 165 | } 166 | 167 | @Override 168 | public boolean exists(final String path, final boolean watch) throws KeeperException, InterruptedException { 169 | throw new RuntimeException("not implemented"); 170 | } 171 | 172 | @Override 173 | public List getChildren(final String path, final boolean watch) throws KeeperException, InterruptedException { 174 | throw new RuntimeException("not implemented"); 175 | } 176 | 177 | @Override 178 | public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException { 179 | throw new RuntimeException("not implemented"); 180 | } 181 | 182 | @Override 183 | public void writeData(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException { 184 | throw new RuntimeException("not implemented"); 185 | } 186 | 187 | @Override 188 | public Stat writeDataReturnStat(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException { 189 | throw new RuntimeException("not implemented"); 190 | } 191 | 192 | @Override 193 | public States getZookeeperState() { 194 | throw new RuntimeException("not implemented"); 195 | } 196 | 197 | @Override 198 | public long getCreateTime(String path) throws KeeperException, InterruptedException { 199 | throw new RuntimeException("not implemented"); 200 | } 201 | 202 | @Override 203 | public String getServers() { 204 | return "test"; 205 | } 206 | 207 | @Override 208 | public List multi(Iterable ops) throws KeeperException, InterruptedException { 209 | throw new UnsupportedOperationException(); 210 | } 211 | 212 | @Override 213 | public void addAuthInfo(String scheme, byte[] auth) { 214 | throw new RuntimeException("not implemented"); 215 | } 216 | 217 | @Override 218 | public void setAcl(String path, List acl, int version) throws KeeperException, InterruptedException { 219 | throw new UnsupportedOperationException(); 220 | } 221 | 222 | @Override 223 | public Map.Entry, Stat> getAcl(String path) throws KeeperException, InterruptedException { 224 | throw new UnsupportedOperationException(); 225 | } 226 | } 227 | 228 | private static class TestStateListener implements IZkStateListener { 229 | public int expiredEvents = 0; 230 | public int newSessionEvent = 0; 231 | public int sessionEstablishErrors = 0; 232 | 233 | @Override 234 | public void handleStateChanged(KeeperState state) throws Exception { 235 | if (state == KeeperState.Expired) { 236 | expiredEvents++; 237 | } 238 | } 239 | 240 | @Override 241 | public void handleNewSession() throws Exception { 242 | newSessionEvent++; 243 | } 244 | 245 | @Override 246 | public void handleSessionEstablishmentError(final Throwable error) throws Exception { 247 | sessionEstablishErrors++; 248 | } 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2009 Stefan Groschupf 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/ServerZkClientTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.assertj.core.api.Assertions.assertThat; 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertTrue; 22 | import static org.junit.Assert.fail; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.concurrent.Callable; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | import org.I0Itec.zkclient.exception.ZkBadVersionException; 30 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 31 | import org.I0Itec.zkclient.exception.ZkNoNodeException; 32 | import org.I0Itec.zkclient.exception.ZkTimeoutException; 33 | import org.I0Itec.zkclient.serialize.SerializableSerializer; 34 | import org.apache.zookeeper.Watcher.Event.KeeperState; 35 | import org.apache.zookeeper.data.Stat; 36 | import org.junit.After; 37 | import org.junit.Before; 38 | import org.junit.Rule; 39 | import org.junit.Test; 40 | import org.junit.rules.TemporaryFolder; 41 | 42 | public class ServerZkClientTest extends AbstractBaseZkClientTest { 43 | 44 | private static final int CONNECTION_TIMEOUT = 30000; 45 | 46 | @Rule 47 | public TemporaryFolder _temporaryFolder = new TemporaryFolder(); 48 | 49 | @Override 50 | @Before 51 | public void setUp() throws Exception { 52 | super.setUp(); 53 | _zkServer = TestUtil.startZkServer(_temporaryFolder, 4711); 54 | _client = new ZkClient("localhost:4711", CONNECTION_TIMEOUT); 55 | } 56 | 57 | @Override 58 | @After 59 | public void tearDown() throws Exception { 60 | super.tearDown(); 61 | _client.close(); 62 | _zkServer.shutdown(); 63 | } 64 | 65 | @Test(timeout = 15000) 66 | public void testConnectionTimeout() throws Exception { 67 | LOG.info("--- testConnectionTimeout"); 68 | _zkServer.shutdown(); 69 | try { 70 | new ZkClient("localhost:4711", 500).close(); 71 | fail("should throw exception"); 72 | } catch (Exception e) { 73 | assertThat(e).isInstanceOf(ZkTimeoutException.class).hasMessageContaining("localhost:4711"); 74 | } 75 | } 76 | 77 | @Test(timeout = 15000) 78 | public void testRetryUntilConnected() throws Exception { 79 | LOG.info("--- testRetryUntilConnected"); 80 | Gateway gateway = new Gateway(4712, 4711); 81 | gateway.start(); 82 | final ZkConnection zkConnection = new ZkConnection("localhost:4712"); 83 | final ZkClient zkClient = new ZkClient(zkConnection, CONNECTION_TIMEOUT); 84 | 85 | gateway.stop(); 86 | 87 | // start server in 250ms 88 | new DeferredGatewayStarter(gateway, 250).start(); 89 | 90 | // this should work as soon as the connection is reestablished, if it 91 | // fails it throws a ConnectionLossException 92 | zkClient.retryUntilConnected(new Callable() { 93 | 94 | @Override 95 | public Object call() throws Exception { 96 | zkConnection.exists("/a", false); 97 | return null; 98 | } 99 | }); 100 | 101 | zkClient.close(); 102 | gateway.stop(); 103 | } 104 | 105 | /** 106 | * Test for reproducing #25 / https://issues.apache.org/jira/browse/KAFKA-824 107 | * 108 | * @throws Exception 109 | */ 110 | @Test(timeout = 15000) 111 | public void testOperationInRetryLoopWhileClientGetsClosed() throws Exception { 112 | LOG.info("--- testRetryUntilConnected"); 113 | Gateway gateway = new Gateway(4712, 4711); 114 | gateway.start(); 115 | final ZkConnection zkConnection = new ZkConnection("localhost:4712"); 116 | final ZkClient zkClient = new ZkClient(zkConnection, CONNECTION_TIMEOUT); 117 | 118 | gateway.stop(); 119 | final Holder exceptionHolder = new Holder(); 120 | Thread actionThread = new Thread() { 121 | @Override 122 | public void run() { 123 | try { 124 | zkClient.createPersistent("/root"); 125 | } catch (Exception e) { 126 | exceptionHolder.set(e); 127 | } 128 | } 129 | }; 130 | actionThread.start(); 131 | zkClient.close(); 132 | actionThread.join(); 133 | 134 | assertThat(exceptionHolder.get()).isNotNull().isInstanceOf(IllegalStateException.class).hasMessageContaining("ZkClient already closed!"); 135 | } 136 | 137 | @Test(timeout = 10000) 138 | public void testReadWithTimeout() throws Exception { 139 | final ZkClient zkClient = new ZkClient("localhost:4711", 5000, CONNECTION_TIMEOUT, new SerializableSerializer(), 5000); 140 | // shutdown the server 141 | LOG.info("Shutting down zookeeper server " + _zkServer); 142 | _zkServer.shutdown(); 143 | // now invoke read operation through the client 144 | try { 145 | LOG.info("Invoking read on ZkClient when ZK server is down"); 146 | zkClient.readData("/b"); 147 | fail("A timeout exception was expected while performing a read through ZkClient, when ZK server is down"); 148 | } catch (ZkTimeoutException zkte) { 149 | // expected 150 | LOG.info("Received the *expected* timeout exception while doing an operation through ZkClient", zkte); 151 | } 152 | } 153 | 154 | @Test(timeout = 30000) 155 | public void testRetryWithTimeout() throws Exception { 156 | final ZkClient zkClient = new ZkClient("localhost:4711", CONNECTION_TIMEOUT, CONNECTION_TIMEOUT, new SerializableSerializer(), 5000); 157 | // shutdown the server 158 | LOG.info("Shutting down zookeeper server " + _zkServer); 159 | _zkServer.shutdown(); 160 | // test the retry method directly 161 | try { 162 | LOG.info("Invoking retryUntilConnected on ZkClient when ZK server is down"); 163 | zkClient.retryUntilConnected(new Callable() { 164 | @Override 165 | public Boolean call() throws Exception { 166 | return zkClient._connection.exists("/b", true); 167 | } 168 | }); 169 | fail("A timeout exception was expected while performing an operation through ZkClient with the ZK server down"); 170 | } catch (ZkTimeoutException zkte) { 171 | // expected 172 | LOG.info("Received the *expected* timeout exception from ZkClient.retryUntilConnected", zkte); 173 | } 174 | } 175 | 176 | @Test(timeout = 15000) 177 | public void testWaitUntilConnected() throws Exception { 178 | LOG.info("--- testWaitUntilConnected"); 179 | ZkClient _client = new ZkClient("localhost:4711", CONNECTION_TIMEOUT); 180 | 181 | _zkServer.shutdown(); 182 | 183 | // the _client state should change to KeeperState.Disconnected 184 | assertTrue(_client.waitForKeeperState(KeeperState.Disconnected, 1, TimeUnit.SECONDS)); 185 | 186 | // connection should not be possible and timeout after 100ms 187 | assertFalse(_client.waitUntilConnected(100, TimeUnit.MILLISECONDS)); 188 | } 189 | 190 | @Test(timeout = 15000) 191 | public void testRetryUntilConnected_SessionExpiredException() { 192 | LOG.info("--- testRetryUntilConnected_SessionExpiredException"); 193 | 194 | // Use a tick time of 100ms, because the minimum session timeout is 2 x tick-time. 195 | // ZkServer zkServer = TestUtil.startZkServer("ZkClientTest-testSessionExpiredException", 4711, 100); 196 | Gateway gateway = new Gateway(4712, 4711); 197 | gateway.start(); 198 | 199 | // Use a session timeout of 200ms 200 | final ZkClient zkClient = new ZkClient("localhost:4712", 200, CONNECTION_TIMEOUT); 201 | 202 | gateway.stop(); 203 | 204 | // Start server in 600ms, the session should have expired by then 205 | new DeferredGatewayStarter(gateway, 600).start(); 206 | 207 | // This should work as soon as a new session has been created (and the connection is reestablished), if it fails 208 | // it throws a SessionExpiredException 209 | zkClient.retryUntilConnected(new Callable() { 210 | 211 | @Override 212 | public Object call() throws Exception { 213 | zkClient.exists("/a"); 214 | return null; 215 | } 216 | }); 217 | 218 | zkClient.close(); 219 | // zkServer.shutdown(); 220 | gateway.stop(); 221 | } 222 | 223 | @Test(timeout = 15000) 224 | public void testChildListenerAfterSessionExpiredException() throws Exception { 225 | LOG.info("--- testChildListenerAfterSessionExpiredException"); 226 | 227 | int sessionTimeout = 200; 228 | ZkClient connectedClient = _zkServer.getZkClient(); 229 | connectedClient.createPersistent("/root"); 230 | 231 | Gateway gateway = new Gateway(4712, 4711); 232 | gateway.start(); 233 | 234 | final ZkClient disconnectedZkClient = new ZkClient("localhost:4712", sessionTimeout, CONNECTION_TIMEOUT); 235 | final Holder> children = new Holder>(); 236 | disconnectedZkClient.subscribeChildChanges("/root", new IZkChildListener() { 237 | 238 | @Override 239 | public void handleChildChange(String parentPath, List currentChilds) throws Exception { 240 | children.set(currentChilds); 241 | } 242 | }); 243 | 244 | gateway.stop(); 245 | 246 | // The connected client now created a new child node 247 | connectedClient.createPersistent("/root/node"); 248 | 249 | // Wait for 3 x sessionTimeout, the session should have expired by then and start the gateway again 250 | Thread.sleep(sessionTimeout * 3); 251 | gateway.start(); 252 | 253 | Boolean hasOneChild = TestUtil.waitUntil(true, new Callable() { 254 | 255 | @Override 256 | public Boolean call() throws Exception { 257 | return children.get() != null && children.get().size() == 1; 258 | } 259 | }, TimeUnit.SECONDS, 30); 260 | 261 | assertTrue(hasOneChild); 262 | 263 | disconnectedZkClient.close(); 264 | gateway.stop(); 265 | } 266 | 267 | @Test(timeout = 10000) 268 | public void testZkClientConnectedToGatewayClosesQuickly() throws Exception { 269 | LOG.info("--- testZkClientConnectedToGatewayClosesQuickly"); 270 | final Gateway gateway = new Gateway(4712, 4711); 271 | gateway.start(); 272 | 273 | ZkClient zkClient = new ZkClient("localhost:4712", CONNECTION_TIMEOUT); 274 | zkClient.close(); 275 | 276 | gateway.stop(); 277 | } 278 | 279 | @Test 280 | public void testCountChildren() throws InterruptedException { 281 | assertEquals(0, _client.countChildren("/a")); 282 | _client.createPersistent("/a"); 283 | assertEquals(0, _client.countChildren("/a")); 284 | _client.createPersistent("/a/b"); 285 | assertEquals(1, _client.countChildren("/a")); 286 | 287 | // test concurrent access 288 | Thread thread = new Thread() { 289 | @Override 290 | public void run() { 291 | try { 292 | while (!isInterrupted()) { 293 | _client.createPersistent("/test"); 294 | _client.delete("/test"); 295 | } 296 | } catch (ZkInterruptedException e) { 297 | // ignore and finish 298 | } 299 | } 300 | }; 301 | 302 | thread.start(); 303 | for (int i = 0; i < 1000; i++) { 304 | assertEquals(0, _client.countChildren("/test")); 305 | } 306 | thread.interrupt(); 307 | thread.join(); 308 | } 309 | 310 | @Test 311 | public void testReadDataWithStat() { 312 | _client.createPersistent("/a", "data"); 313 | Stat stat = new Stat(); 314 | _client.readData("/a", stat); 315 | assertEquals(0, stat.getVersion()); 316 | assertTrue(stat.getDataLength() > 0); 317 | } 318 | 319 | @Test 320 | public void testWriteDataWithExpectedVersion() { 321 | _client.createPersistent("/a", "data"); 322 | _client.writeData("/a", "data2", 0); 323 | 324 | try { 325 | _client.writeData("/a", "data3", 0); 326 | fail("expected exception"); 327 | } catch (ZkBadVersionException e) { 328 | // expected 329 | } 330 | } 331 | 332 | @Test 333 | public void testCreateWithParentDirs() { 334 | String path = "/a/b"; 335 | try { 336 | _client.createPersistent(path, false); 337 | fail("should throw exception"); 338 | } catch (ZkNoNodeException e) { 339 | assertFalse(_client.exists(path)); 340 | } 341 | 342 | _client.createPersistent(path, true); 343 | assertTrue(_client.exists(path)); 344 | } 345 | 346 | @Test 347 | public void testUpdateSerialized() throws InterruptedException { 348 | _client.createPersistent("/a", 0); 349 | 350 | int numberOfThreads = 2; 351 | final int numberOfIncrementsPerThread = 100; 352 | 353 | List threads = new ArrayList(); 354 | for (int i = 0; i < numberOfThreads; i++) { 355 | Thread thread = new Thread() { 356 | @Override 357 | public void run() { 358 | for (int j = 0; j < numberOfIncrementsPerThread; j++) { 359 | _client.updateDataSerialized("/a", new DataUpdater() { 360 | 361 | @Override 362 | public Integer update(Integer integer) { 363 | return integer + 1; 364 | } 365 | }); 366 | } 367 | } 368 | }; 369 | thread.start(); 370 | threads.add(thread); 371 | } 372 | 373 | for (Thread thread : threads) { 374 | thread.join(); 375 | } 376 | 377 | Integer finalValue = _client.readData("/a"); 378 | assertEquals(numberOfIncrementsPerThread * numberOfThreads, finalValue.intValue()); 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /src/test/java/org/I0Itec/zkclient/AbstractBaseZkClientTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertNull; 21 | import static org.junit.Assert.assertTrue; 22 | import static org.junit.Assert.fail; 23 | import static org.mockito.Mockito.mock; 24 | 25 | import java.util.List; 26 | import java.util.concurrent.Callable; 27 | import java.util.concurrent.TimeUnit; 28 | import java.util.concurrent.atomic.AtomicInteger; 29 | 30 | import org.I0Itec.zkclient.exception.ZkBadVersionException; 31 | import org.I0Itec.zkclient.exception.ZkTimeoutException; 32 | import org.apache.log4j.Logger; 33 | import org.junit.After; 34 | import org.junit.Assert; 35 | import org.junit.Before; 36 | import org.junit.Test; 37 | 38 | public abstract class AbstractBaseZkClientTest { 39 | 40 | protected static final Logger LOG = Logger.getLogger(AbstractBaseZkClientTest.class); 41 | protected ZkServer _zkServer; 42 | protected ZkClient _client; 43 | 44 | @Before 45 | public void setUp() throws Exception { 46 | LOG.info("------------ BEFORE -------------"); 47 | 48 | } 49 | 50 | @After 51 | public void tearDown() throws Exception { 52 | LOG.info("------------ AFTER -------------"); 53 | } 54 | 55 | @Test(expected = ZkTimeoutException.class, timeout = 15000) 56 | public void testUnableToConnect() throws Exception { 57 | LOG.info("--- testUnableToConnect"); 58 | // we are using port 4711 to avoid conflicts with the zk server that is 59 | // started by the Spring context 60 | new ZkClient("localhost:4712", 5000); 61 | } 62 | 63 | @Test 64 | public void testWriteAndRead() throws Exception { 65 | LOG.info("--- testWriteAndRead"); 66 | String data = "something"; 67 | String path = "/a"; 68 | _client.createPersistent(path, data); 69 | String data2 = _client.readData(path); 70 | Assert.assertEquals(data, data2); 71 | _client.delete(path); 72 | } 73 | 74 | @Test 75 | public void testDelete() throws Exception { 76 | LOG.info("--- testDelete"); 77 | String path = "/a"; 78 | assertFalse(_client.delete(path)); 79 | _client.createPersistent(path, null); 80 | assertTrue(_client.delete(path)); 81 | assertFalse(_client.delete(path)); 82 | } 83 | 84 | @Test 85 | public void testDeleteWithVersion() throws Exception { 86 | LOG.info("--- testDelete"); 87 | String path = "/a"; 88 | assertFalse(_client.delete(path, 0)); 89 | _client.createPersistent(path, null); 90 | assertTrue(_client.delete(path, 0)); 91 | _client.createPersistent(path, null); 92 | _client.writeData(path, new byte[0]); 93 | assertTrue(_client.delete(path, 1)); 94 | _client.createPersistent(path, null); 95 | try { 96 | _client.delete(path, 1); 97 | fail("Bad version excpetion expected."); 98 | } catch (ZkBadVersionException e) { 99 | // expected 100 | } 101 | assertTrue(_client.delete(path, 0)); 102 | } 103 | 104 | @Test 105 | public void testDeleteRecursive() throws Exception { 106 | LOG.info("--- testDeleteRecursive"); 107 | 108 | // should be able to call this on a not existing directory 109 | _client.deleteRecursive("/doesNotExist"); 110 | } 111 | 112 | @Test 113 | public void testWaitUntilExists() { 114 | LOG.info("--- testWaitUntilExists"); 115 | // create /gaga node asynchronously 116 | new Thread() { 117 | @Override 118 | public void run() { 119 | try { 120 | Thread.sleep(100); 121 | _client.createPersistent("/gaga"); 122 | } catch (Exception e) { 123 | // ignore 124 | } 125 | } 126 | }.start(); 127 | 128 | // wait until this was created 129 | assertTrue(_client.waitUntilExists("/gaga", TimeUnit.SECONDS, 5)); 130 | assertTrue(_client.exists("/gaga")); 131 | 132 | // waiting for /neverCreated should timeout 133 | assertFalse(_client.waitUntilExists("/neverCreated", TimeUnit.MILLISECONDS, 100)); 134 | } 135 | 136 | @Test 137 | public void testDataChanges1() throws Exception { 138 | LOG.info("--- testDataChanges1"); 139 | String path = "/a"; 140 | final Holder holder = new Holder(); 141 | 142 | IZkDataListener listener = new IZkDataListener() { 143 | 144 | @Override 145 | public void handleDataChange(String dataPath, Object data) throws Exception { 146 | holder.set((String) data); 147 | } 148 | 149 | @Override 150 | public void handleDataDeleted(String dataPath) throws Exception { 151 | holder.set(null); 152 | } 153 | }; 154 | _client.subscribeDataChanges(path, listener); 155 | _client.createPersistent(path, "aaa"); 156 | 157 | // wait some time to make sure the event was triggered 158 | String contentFromHolder = TestUtil.waitUntil("b", new Callable() { 159 | 160 | @Override 161 | public String call() throws Exception { 162 | return holder.get(); 163 | } 164 | }, TimeUnit.SECONDS, 5); 165 | 166 | assertEquals("aaa", contentFromHolder); 167 | } 168 | 169 | @Test 170 | public void testDataChanges2() throws Exception { 171 | LOG.info("--- testDataChanges2"); 172 | String path = "/a"; 173 | final AtomicInteger countChanged = new AtomicInteger(0); 174 | final AtomicInteger countDeleted = new AtomicInteger(0); 175 | 176 | IZkDataListener listener = new IZkDataListener() { 177 | 178 | @Override 179 | public void handleDataChange(String dataPath, Object data) throws Exception { 180 | countChanged.incrementAndGet(); 181 | } 182 | 183 | @Override 184 | public void handleDataDeleted(String dataPath) throws Exception { 185 | countDeleted.incrementAndGet(); 186 | } 187 | }; 188 | _client.subscribeDataChanges(path, listener); 189 | 190 | // create node 191 | _client.createPersistent(path, "aaa"); 192 | 193 | // wait some time to make sure the event was triggered 194 | TestUtil.waitUntil(1, new Callable() { 195 | 196 | @Override 197 | public Integer call() throws Exception { 198 | return countChanged.get(); 199 | } 200 | }, TimeUnit.SECONDS, 5); 201 | assertEquals(1, countChanged.get()); 202 | assertEquals(0, countDeleted.get()); 203 | 204 | countChanged.set(0); 205 | countDeleted.set(0); 206 | // delete node, this should trigger a delete event 207 | _client.delete(path); 208 | // wait some time to make sure the event was triggered 209 | TestUtil.waitUntil(1, new Callable() { 210 | 211 | @Override 212 | public Integer call() throws Exception { 213 | return countDeleted.get(); 214 | } 215 | }, TimeUnit.SECONDS, 5); 216 | assertEquals(0, countChanged.get()); 217 | assertEquals(1, countDeleted.get()); 218 | 219 | // test if watch was reinstalled after the file got deleted 220 | countChanged.set(0); 221 | _client.createPersistent(path, "aaa"); 222 | 223 | // wait some time to make sure the event was triggered 224 | TestUtil.waitUntil(1, new Callable() { 225 | 226 | @Override 227 | public Integer call() throws Exception { 228 | return countChanged.get(); 229 | } 230 | }, TimeUnit.SECONDS, 5); 231 | assertEquals(1, countChanged.get()); 232 | 233 | // test if changing the contents notifies the listener 234 | _client.writeData(path, "bbb"); 235 | 236 | // wait some time to make sure the event was triggered 237 | TestUtil.waitUntil(2, new Callable() { 238 | 239 | @Override 240 | public Integer call() throws Exception { 241 | return countChanged.get(); 242 | } 243 | }, TimeUnit.SECONDS, 5); 244 | assertEquals(2, countChanged.get()); 245 | } 246 | 247 | @Test(timeout = 15000) 248 | public void testHandleChildChanges() throws Exception { 249 | LOG.info("--- testHandleChildChanges"); 250 | String path = "/a"; 251 | final AtomicInteger count = new AtomicInteger(0); 252 | final Holder> children = new Holder>(); 253 | 254 | IZkChildListener listener = new IZkChildListener() { 255 | 256 | @Override 257 | public void handleChildChange(String parentPath, List currentChilds) throws Exception { 258 | count.incrementAndGet(); 259 | children.set(currentChilds); 260 | } 261 | }; 262 | _client.subscribeChildChanges(path, listener); 263 | 264 | // ---- 265 | // Create the root node should throw the first child change event 266 | // ---- 267 | _client.createPersistent(path); 268 | 269 | // wait some time to make sure the event was triggered 270 | TestUtil.waitUntil(1, new Callable() { 271 | 272 | @Override 273 | public Integer call() throws Exception { 274 | return count.get(); 275 | } 276 | }, TimeUnit.SECONDS, 5); 277 | assertEquals(1, count.get()); 278 | assertEquals(0, children.get().size()); 279 | 280 | // ---- 281 | // Creating a child node should throw another event 282 | // ---- 283 | count.set(0); 284 | _client.createPersistent(path + "/child1"); 285 | 286 | // wait some time to make sure the event was triggered 287 | TestUtil.waitUntil(1, new Callable() { 288 | 289 | @Override 290 | public Integer call() throws Exception { 291 | return count.get(); 292 | } 293 | }, TimeUnit.SECONDS, 5); 294 | assertEquals(1, count.get()); 295 | assertEquals(1, children.get().size()); 296 | assertEquals("child1", children.get().get(0)); 297 | 298 | // ---- 299 | // Creating another child and deleting the node should also throw an event 300 | // ---- 301 | count.set(0); 302 | _client.createPersistent(path + "/child2"); 303 | _client.deleteRecursive(path); 304 | 305 | // wait some time to make sure the event was triggered 306 | Boolean eventReceived = TestUtil.waitUntil(true, new Callable() { 307 | 308 | @Override 309 | public Boolean call() throws Exception { 310 | return count.get() > 0 && children.get() == null; 311 | } 312 | }, TimeUnit.SECONDS, 5); 313 | assertTrue(eventReceived); 314 | assertNull(children.get()); 315 | 316 | // ---- 317 | // Creating root again should throw an event 318 | // ---- 319 | count.set(0); 320 | _client.createPersistent(path); 321 | 322 | // wait some time to make sure the event was triggered 323 | eventReceived = TestUtil.waitUntil(true, new Callable() { 324 | 325 | @Override 326 | public Boolean call() throws Exception { 327 | return count.get() > 0 && children.get() != null; 328 | } 329 | }, TimeUnit.SECONDS, 5); 330 | assertTrue(eventReceived); 331 | assertEquals(0, children.get().size()); 332 | 333 | // ---- 334 | // Creating child now should throw an event 335 | // ---- 336 | count.set(0); 337 | _client.createPersistent(path + "/child"); 338 | 339 | // wait some time to make sure the event was triggered 340 | eventReceived = TestUtil.waitUntil(true, new Callable() { 341 | 342 | @Override 343 | public Boolean call() throws Exception { 344 | return count.get() > 0; 345 | } 346 | }, TimeUnit.SECONDS, 5); 347 | assertTrue(eventReceived); 348 | assertEquals(1, children.get().size()); 349 | assertEquals("child", children.get().get(0)); 350 | 351 | // ---- 352 | // Deleting root node should throw an event 353 | // ---- 354 | count.set(0); 355 | _client.deleteRecursive(path); 356 | 357 | // wait some time to make sure the event was triggered 358 | eventReceived = TestUtil.waitUntil(true, new Callable() { 359 | 360 | @Override 361 | public Boolean call() throws Exception { 362 | return count.get() > 0 && children.get() == null; 363 | } 364 | }, TimeUnit.SECONDS, 5); 365 | assertTrue(eventReceived); 366 | assertNull(children.get()); 367 | } 368 | 369 | @Test 370 | public void testGetCreationTime() throws Exception { 371 | long start = System.currentTimeMillis(); 372 | Thread.sleep(100); 373 | String path = "/a"; 374 | _client.createPersistent(path); 375 | Thread.sleep(100); 376 | long end = System.currentTimeMillis(); 377 | long creationTime = _client.getCreationTime(path); 378 | assertTrue(start < creationTime && end > creationTime); 379 | } 380 | 381 | @Test 382 | public void testNumberOfListeners() { 383 | IZkChildListener zkChildListener = mock(IZkChildListener.class); 384 | _client.subscribeChildChanges("/", zkChildListener); 385 | assertEquals(1, _client.numberOfListeners()); 386 | 387 | IZkDataListener zkDataListener = mock(IZkDataListener.class); 388 | _client.subscribeDataChanges("/a", zkDataListener); 389 | assertEquals(2, _client.numberOfListeners()); 390 | 391 | _client.subscribeDataChanges("/b", zkDataListener); 392 | assertEquals(3, _client.numberOfListeners()); 393 | 394 | IZkStateListener zkStateListener = mock(IZkStateListener.class); 395 | _client.subscribeStateChanges(zkStateListener); 396 | assertEquals(4, _client.numberOfListeners()); 397 | 398 | _client.unsubscribeChildChanges("/", zkChildListener); 399 | assertEquals(3, _client.numberOfListeners()); 400 | 401 | _client.unsubscribeDataChanges("/b", zkDataListener); 402 | assertEquals(2, _client.numberOfListeners()); 403 | 404 | _client.unsubscribeDataChanges("/a", zkDataListener); 405 | assertEquals(1, _client.numberOfListeners()); 406 | 407 | _client.unsubscribeStateChanges(zkStateListener); 408 | assertEquals(0, _client.numberOfListeners()); 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /src/main/java/org/I0Itec/zkclient/InMemoryConnection.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2010 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.I0Itec.zkclient; 17 | 18 | import java.util.AbstractMap; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.HashSet; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Set; 25 | import java.util.concurrent.BlockingQueue; 26 | import java.util.concurrent.LinkedBlockingDeque; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import java.util.concurrent.locks.Lock; 29 | import java.util.concurrent.locks.ReentrantLock; 30 | 31 | import org.I0Itec.zkclient.exception.ZkException; 32 | import org.I0Itec.zkclient.exception.ZkInterruptedException; 33 | import org.I0Itec.zkclient.exception.ZkNoNodeException; 34 | import org.I0Itec.zkclient.util.ZkPathUtil; 35 | import org.apache.zookeeper.CreateMode; 36 | import org.apache.zookeeper.KeeperException; 37 | import org.apache.zookeeper.KeeperException.Code; 38 | import org.apache.zookeeper.Op; 39 | import org.apache.zookeeper.OpResult; 40 | import org.apache.zookeeper.WatchedEvent; 41 | import org.apache.zookeeper.Watcher; 42 | import org.apache.zookeeper.Watcher.Event.EventType; 43 | import org.apache.zookeeper.Watcher.Event.KeeperState; 44 | import org.apache.zookeeper.ZooDefs; 45 | import org.apache.zookeeper.ZooKeeper.States; 46 | import org.apache.zookeeper.data.ACL; 47 | import org.apache.zookeeper.data.Id; 48 | import org.apache.zookeeper.data.Stat; 49 | import org.apache.zookeeper.proto.CheckVersionRequest; 50 | import org.apache.zookeeper.proto.CreateRequest; 51 | import org.apache.zookeeper.proto.DeleteRequest; 52 | import org.apache.zookeeper.proto.SetDataRequest; 53 | 54 | /** 55 | * Emulating a ZooKeeper server with few hash tables. Basically a mock class used for testing. Please avoid using this 56 | * as your ZK in production :) 57 | * 58 | * Note that the addAuth is even more mocked than usual Since we have no authentication provider (i.e. Kerberos) around 59 | * we simply take the auth byte[] and convert it to string to get the Id scheme remains the same 60 | */ 61 | public class InMemoryConnection implements IZkConnection { 62 | 63 | public static class DataAndVersion { 64 | private final byte[] _data; 65 | private final int _version; 66 | private final List _acl; 67 | 68 | public DataAndVersion(byte[] data, int version, List acl) { 69 | _data = data; 70 | _version = version; 71 | _acl = acl; 72 | } 73 | 74 | public DataAndVersion(byte[] data, int version) { 75 | this(data, version, null); 76 | } 77 | 78 | public byte[] getData() { 79 | return _data; 80 | } 81 | 82 | public int getVersion() { 83 | return _version; 84 | } 85 | 86 | public List getAcl() { 87 | return _acl; 88 | } 89 | } 90 | 91 | private final Lock _lock = new ReentrantLock(true); 92 | private final Map _data = new HashMap<>(); 93 | private final Map _creationTime = new HashMap<>(); 94 | private final List _ids = new ArrayList<>(); 95 | private final AtomicInteger sequence = new AtomicInteger(0); 96 | 97 | private final Set _dataWatches = new HashSet<>(); 98 | private final Set _nodeWatches = new HashSet<>(); 99 | private EventThread _eventThread; 100 | 101 | private class EventThread extends Thread { 102 | 103 | private final Watcher _watcher; 104 | private final BlockingQueue _blockingQueue = new LinkedBlockingDeque<>(); 105 | 106 | public EventThread(Watcher watcher) { 107 | _watcher = watcher; 108 | } 109 | 110 | @Override 111 | public void run() { 112 | try { 113 | while (true) { 114 | _watcher.process(_blockingQueue.take()); 115 | } 116 | } catch (InterruptedException e) { 117 | // stop event thread 118 | } 119 | } 120 | 121 | public void send(WatchedEvent event) { 122 | _blockingQueue.add(event); 123 | } 124 | } 125 | 126 | public InMemoryConnection() { 127 | try { 128 | create("/", null, CreateMode.PERSISTENT); 129 | } catch (KeeperException e) { 130 | throw ZkException.create(e); 131 | } catch (InterruptedException e) { 132 | Thread.currentThread().interrupt(); 133 | throw new ZkInterruptedException(e); 134 | } 135 | } 136 | 137 | @Override 138 | public void close() throws InterruptedException { 139 | _lock.lockInterruptibly(); 140 | try { 141 | if (_eventThread != null) { 142 | _eventThread.interrupt(); 143 | _eventThread.join(); 144 | _eventThread = null; 145 | } 146 | } finally { 147 | _lock.unlock(); 148 | } 149 | } 150 | 151 | @Override 152 | public void connect(Watcher watcher) { 153 | _lock.lock(); 154 | try { 155 | if (_eventThread != null) { 156 | throw new IllegalStateException("Already connected."); 157 | } 158 | _eventThread = new EventThread(watcher); 159 | _eventThread.start(); 160 | _eventThread.send(new WatchedEvent(null, KeeperState.SyncConnected, null)); 161 | } finally { 162 | _lock.unlock(); 163 | } 164 | } 165 | 166 | @Override 167 | public String create(String path, byte[] data, List acl, CreateMode mode) throws KeeperException, InterruptedException { 168 | _lock.lock(); 169 | try { 170 | 171 | if (mode.isSequential()) { 172 | final int newSequence = sequence.getAndIncrement(); 173 | path = path + ZkPathUtil.leadingZeros(newSequence, 10); 174 | } 175 | 176 | if (exists(path, false)) { 177 | throw new KeeperException.NodeExistsException(); 178 | } 179 | String parentPath = getParentPath(path); 180 | checkACL(parentPath, ZooDefs.Perms.CREATE); 181 | 182 | _data.put(path, new DataAndVersion(data, 0, acl)); 183 | _creationTime.put(path, System.currentTimeMillis()); 184 | checkWatch(_nodeWatches, path, EventType.NodeCreated); 185 | // we also need to send a child change event for the parent 186 | if (parentPath != null) { 187 | checkWatch(_nodeWatches, parentPath, EventType.NodeChildrenChanged); 188 | } 189 | return path; 190 | } finally { 191 | _lock.unlock(); 192 | } 193 | } 194 | 195 | @Override 196 | public String create(String path, byte[] data, CreateMode mode) throws KeeperException, InterruptedException { 197 | return create(path, data, null, mode); 198 | } 199 | 200 | private String getParentPath(String path) { 201 | int lastIndexOf = path.lastIndexOf("/"); 202 | if (lastIndexOf == -1 || lastIndexOf == 0) { 203 | return null; 204 | } 205 | return path.substring(0, lastIndexOf); 206 | } 207 | 208 | @Override 209 | public void delete(String path) throws InterruptedException, KeeperException { 210 | this.delete(path, -1); 211 | } 212 | 213 | @Override 214 | public void delete(String path, int version) throws InterruptedException, KeeperException { 215 | _lock.lock(); 216 | try { 217 | if (!exists(path, false)) { 218 | throw new KeeperException.NoNodeException(); 219 | } 220 | String parentPath = getParentPath(path); 221 | checkACL(parentPath, ZooDefs.Perms.DELETE); 222 | // If version isn't -1, check that it mateches 223 | if (version != -1) { 224 | DataAndVersion item = _data.get(path); 225 | if (item._version != version) { 226 | throw KeeperException.create(Code.BADVERSION); 227 | } 228 | } 229 | _data.remove(path); 230 | _creationTime.remove(path); 231 | checkWatch(_nodeWatches, path, EventType.NodeDeleted); 232 | if (parentPath != null) { 233 | checkWatch(_nodeWatches, parentPath, EventType.NodeChildrenChanged); 234 | } 235 | } finally { 236 | _lock.unlock(); 237 | } 238 | } 239 | 240 | @Override 241 | public boolean exists(String path, boolean watch) throws KeeperException, InterruptedException { 242 | _lock.lock(); 243 | try { 244 | if (watch) { 245 | installWatch(_nodeWatches, path); 246 | } 247 | return _data.containsKey(path); 248 | } finally { 249 | _lock.unlock(); 250 | } 251 | } 252 | 253 | private void installWatch(Set watches, String path) { 254 | watches.add(path); 255 | } 256 | 257 | @Override 258 | public List getChildren(String path, boolean watch) throws KeeperException, InterruptedException { 259 | if (!exists(path, false)) { 260 | throw KeeperException.create(Code.NONODE, path); 261 | } 262 | if (exists(path, false) && watch) { 263 | installWatch(_nodeWatches, path); 264 | } 265 | 266 | checkACL(path, ZooDefs.Perms.READ); 267 | ArrayList children = new ArrayList<>(); 268 | String[] directoryStack = path.split("/"); 269 | Set keySet = _data.keySet(); 270 | 271 | for (String string : keySet) { 272 | if (string.startsWith(path)) { 273 | String[] stack = string.split("/"); 274 | // is one folder level below the one we loockig for and starts 275 | // with path... 276 | if (stack.length == directoryStack.length + 1) { 277 | children.add(stack[stack.length - 1]); 278 | } 279 | } 280 | 281 | } 282 | return children; 283 | } 284 | 285 | @Override 286 | public States getZookeeperState() { 287 | _lock.lock(); 288 | try { 289 | if (_eventThread == null) { 290 | return States.CLOSED; 291 | } 292 | return States.CONNECTED; 293 | } finally { 294 | _lock.unlock(); 295 | } 296 | } 297 | 298 | @Override 299 | public byte[] readData(String path, Stat stat, boolean watch) throws KeeperException, InterruptedException { 300 | if (watch) { 301 | installWatch(_dataWatches, path); 302 | } 303 | _lock.lock(); 304 | try { 305 | DataAndVersion dataAndVersion = _data.get(path); 306 | if (dataAndVersion == null) { 307 | throw new ZkNoNodeException(new KeeperException.NoNodeException()); 308 | } 309 | checkACL(path, ZooDefs.Perms.READ); 310 | byte[] bs = dataAndVersion.getData(); 311 | if (stat != null) { 312 | stat.setVersion(dataAndVersion.getVersion()); 313 | } 314 | return bs; 315 | } finally { 316 | _lock.unlock(); 317 | } 318 | } 319 | 320 | @Override 321 | public void writeData(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException { 322 | writeDataReturnStat(path, data, expectedVersion); 323 | } 324 | 325 | @Override 326 | public Stat writeDataReturnStat(String path, byte[] data, int expectedVersion) throws KeeperException, InterruptedException { 327 | int newVersion = -1; 328 | _lock.lock(); 329 | try { 330 | checkWatch(_dataWatches, path, EventType.NodeDataChanged); 331 | if (!exists(path, false)) { 332 | throw new KeeperException.NoNodeException(); 333 | } 334 | checkACL(path, ZooDefs.Perms.WRITE); 335 | newVersion = _data.get(path).getVersion() + 1; 336 | _data.put(path, new DataAndVersion(data, newVersion)); 337 | String parentPath = getParentPath(path); 338 | if (parentPath != null) { 339 | checkWatch(_nodeWatches, parentPath, EventType.NodeChildrenChanged); 340 | } 341 | } finally { 342 | _lock.unlock(); 343 | } 344 | Stat stat = new Stat(); 345 | stat.setVersion(newVersion); 346 | return stat; 347 | } 348 | 349 | private void checkWatch(Set watches, String path, EventType eventType) { 350 | if (watches.contains(path)) { 351 | watches.remove(path); 352 | _eventThread.send(new WatchedEvent(eventType, KeeperState.SyncConnected, path)); 353 | } 354 | } 355 | 356 | @Override 357 | public long getCreateTime(String path) { 358 | Long time = _creationTime.get(path); 359 | if (time == null) { 360 | return -1; 361 | } 362 | return time; 363 | } 364 | 365 | @Override 366 | public String getServers() { 367 | return "mem"; 368 | } 369 | 370 | @Override 371 | public List multi(Iterable ops) throws KeeperException, InterruptedException { 372 | List opResults = new ArrayList<>(); 373 | for (Op op : ops) { 374 | if (Op.Check.class.isAssignableFrom(op.getClass())) { 375 | CheckVersionRequest check = (CheckVersionRequest) op.toRequestRecord(); 376 | exists(check.getPath(), false); 377 | opResults.add(new OpResult.CheckResult()); 378 | } else if (Op.Create.class.isAssignableFrom(op.getClass())) { 379 | CreateRequest create = (CreateRequest) op.toRequestRecord(); 380 | String path = create(create.getPath(), create.getData(), CreateMode.fromFlag(create.getFlags())); 381 | opResults.add(new OpResult.CreateResult(path)); 382 | } else if (Op.Delete.class.isAssignableFrom(op.getClass())) { 383 | DeleteRequest delete = (DeleteRequest) op.toRequestRecord(); 384 | delete(delete.getPath()); 385 | opResults.add(new OpResult.DeleteResult()); 386 | } else if (Op.SetData.class.isAssignableFrom(op.getClass())) { 387 | SetDataRequest setData = (SetDataRequest) op.toRequestRecord(); 388 | writeData(setData.getPath(), setData.getData(), setData.getVersion()); 389 | opResults.add(new OpResult.SetDataResult(null)); 390 | } 391 | } 392 | return opResults; 393 | } 394 | 395 | @Override 396 | public void addAuthInfo(String scheme, byte[] auth) { 397 | _ids.add(new Id(scheme, new String(auth))); 398 | } 399 | 400 | @Override 401 | public void setAcl(String path, List acl, int version) throws KeeperException, InterruptedException { 402 | if (!exists(path, false)) { 403 | throw new KeeperException.NoNodeException(); 404 | } 405 | 406 | DataAndVersion dataAndVersion = _data.get(path); 407 | if (version != dataAndVersion._version) { 408 | throw new KeeperException.BadVersionException(); 409 | } 410 | 411 | checkACL(path, ZooDefs.Perms.ADMIN); 412 | 413 | _lock.lock(); 414 | try { 415 | _data.put(path, new DataAndVersion(dataAndVersion.getData(), dataAndVersion.getVersion() + 1, acl)); 416 | } finally { 417 | _lock.unlock(); 418 | } 419 | } 420 | 421 | @Override 422 | public Map.Entry, Stat> getAcl(String path) throws KeeperException, InterruptedException { 423 | if (!exists(path, false)) { 424 | throw new KeeperException.NoNodeException(); 425 | } 426 | 427 | DataAndVersion dataAndVersion = _data.get(path); 428 | 429 | Stat stat = new Stat(); 430 | stat.setVersion(dataAndVersion.getVersion()); 431 | stat.setCtime(_creationTime.get(path)); 432 | 433 | return new AbstractMap.SimpleEntry<>(dataAndVersion.getAcl(), stat); 434 | } 435 | 436 | /*** 437 | * 438 | * @param path 439 | * - path of znode we are accessing 440 | * @param perm 441 | * - Privileges required for the action 442 | * @throws KeeperException.NoAuthException 443 | */ 444 | private void checkACL(String path, int perm) throws KeeperException.NoAuthException { 445 | DataAndVersion node = _data.get(path); 446 | if (node == null) { 447 | return; 448 | } 449 | List acl = node.getAcl(); 450 | if (acl == null || acl.size() == 0) { 451 | return; 452 | } 453 | for (Id authId : _ids) { 454 | if (authId.getScheme().equals("super")) { 455 | return; 456 | } 457 | } 458 | for (ACL a : acl) { 459 | Id id = a.getId(); 460 | if ((a.getPerms() & perm) != 0) { 461 | if (id.getScheme().equals("world") && id.getId().equals("anyone")) { 462 | return; 463 | } 464 | for (Id authId : _ids) { 465 | if (authId.getScheme().equals(id.getScheme()) && authId.getId().equals(id.getId())) { 466 | return; 467 | } 468 | } 469 | } 470 | } 471 | throw new KeeperException.NoAuthException(); 472 | } 473 | } 474 | --------------------------------------------------------------------------------