├── lib ├── zookeeper-3.2.2.jar └── jackhammer_2.7.7-1.0.jar ├── .gitignore ├── src ├── main │ ├── resources │ │ └── config.conf │ └── scala │ │ └── com │ │ └── twitter │ │ └── zookeeper │ │ └── client │ │ ├── FakeWatcher.scala │ │ ├── LoadTest.scala │ │ └── ZookeeperClient.scala └── test │ └── scala │ └── com │ └── twitter │ └── zookeeper │ └── client │ └── ZookeeperClientSpec.scala ├── project ├── build.properties └── build │ └── ZookeeperClientProject.scala ├── LICENSE └── README.textile /lib/zookeeper-3.2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3x/zookeeper-client/master/lib/zookeeper-3.2.2.jar -------------------------------------------------------------------------------- /lib/jackhammer_2.7.7-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al3x/zookeeper-client/master/lib/jackhammer_2.7.7-1.0.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | dist 3 | #*# 4 | .#* 5 | *.log 6 | *.iml 7 | *.ipr 8 | *.iws 9 | *.swp 10 | lib_managed 11 | project/boot 12 | .DS_Store 13 | src_managed 14 | -------------------------------------------------------------------------------- /src/main/resources/config.conf: -------------------------------------------------------------------------------- 1 | log { 2 | filename = "/tmp/zookeeper-client.log" 3 | roll = "daily" 4 | level = "debug" 5 | } 6 | 7 | # where do the Zookeeper servers live? 8 | hostlist = "localhost:2181" 9 | -------------------------------------------------------------------------------- /src/main/scala/com/twitter/zookeeper/client/FakeWatcher.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.zookeeper.client 2 | 3 | import org.apache.zookeeper.{Watcher, WatchedEvent} 4 | 5 | 6 | class FakeWatcher extends Watcher { 7 | def process(event: WatchedEvent) { 8 | // nop 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | # 2 | #Wed Mar 03 19:39:42 UTC 2010 3 | project.name=zookeeper-client 4 | project.organization=Twitter, Inc. 5 | scala.version=2.7.7 6 | project.version=1.0 7 | sbt.version=0.7.1 8 | def.scala.version=2.7.7 9 | build.scala.versions=2.7.7 10 | project.initialize=false 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 Alex Payne 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. zookeeper-client 2 | 3 | h2. About 4 | 5 | A Scala client for "Apache ZooKeeper":http://hadoop.apache.org/zookeeper/, "a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services". 6 | 7 | ZooKeeper provides a Java client library that's perfectly usable from Scala. This just wraps some idioms and niceties around that library to make it as Scala-friendly as possible. It also ships with tests, so you can have some confidence that you'll be able to interact with ZooKeeper from Scala in a predictable and reliable way. 8 | 9 | h2. Usage 10 | 11 | TODO 12 | 13 | h2. Authors 14 | 15 | * "Alex Payne":http://github.com/al3x 16 | * "Matt Knox":http://github.com/mattknox 17 | 18 | h2. License 19 | 20 | Apache License, Version 2.0. See the LICENSE file for more information. 21 | -------------------------------------------------------------------------------- /src/main/scala/com/twitter/zookeeper/client/LoadTest.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.zookeeper.client.loadtest 2 | 3 | import java.io.File 4 | import com.twitter.jackhammer.LoggingLoadTest 5 | import net.lag.configgy.{Config, Configgy} 6 | import net.lag.logging.Logger 7 | 8 | 9 | object LoadTest extends LoggingLoadTest { 10 | Configgy.configure("src/main/resources/config.conf") 11 | 12 | private val config = Configgy.config 13 | private val log = Logger.get 14 | 15 | val GETS = config.getInt("times-to-get", 100000) 16 | //val CONCURRENCY = config.getInt("concurrency", 4) 17 | val HOSTLIST = config.getString("hostlist", "localhost:2181") 18 | 19 | val watcher = new FakeWatcher 20 | val zkClient = new ZookeeperClient(watcher, HOSTLIST) 21 | 22 | def singleClientTest { 23 | runWithTimingNTimes(GETS) { 24 | zkClient.get("/") 25 | } 26 | } 27 | 28 | def parallelClientTest { 29 | runInParallelNTimes(GETS) { 30 | zkClient.get("/") 31 | } 32 | } 33 | 34 | def main(args: Array[String]) { 35 | log.info("starting up") 36 | 37 | // run tests 38 | singleClientTest 39 | val singleClientLogFile = new File("/tmp/single-client-loadtest-%d.log".format(System.currentTimeMillis)) 40 | dumpLogOutput(singleClientLogFile) 41 | 42 | parallelClientTest 43 | val parallelClientLogFile = new File("/tmp/parallel-client-loadtest-%d.log".format(System.currentTimeMillis)) 44 | dumpLogOutput(parallelClientLogFile) 45 | 46 | // peace out 47 | log.info("done with tests, exiting") 48 | exit(0) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/scala/com/twitter/zookeeper/client/ZookeeperClient.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.zookeeper.client 2 | 3 | import org.scala_tools.javautils.Imports._ 4 | import net.lag.configgy.{Config, Configgy} 5 | import net.lag.logging.Logger 6 | import org.apache.zookeeper.{CreateMode, KeeperException, Watcher, ZooKeeper} 7 | import org.apache.zookeeper.data.{ACL, Stat} 8 | 9 | 10 | class ZookeeperClient(watcher: Watcher, hostnamePortPairs: String) { 11 | Configgy.configure("src/main/resources/config.conf") 12 | 13 | private val log = Logger.get 14 | private val config = Configgy.config 15 | 16 | val sessionTimeout = config.getInt("session-timeout", 3000) 17 | 18 | private lazy val zk = new ZooKeeper(hostnamePortPairs, sessionTimeout, watcher) 19 | 20 | def close { 21 | zk.close() 22 | } 23 | 24 | def get(path: String): Array[Byte] = { 25 | val stat: Stat = zk.exists(path, false) 26 | zk.getData(path, false, stat) 27 | } 28 | 29 | // FIXME update to 2.8 Java collection conversions 30 | def getChildren(path: String): Seq[String] = { 31 | zk.getChildren(path, false).asScala 32 | } 33 | 34 | def create(path: String, data: Array[Byte], acl: java.util.List[ACL], createMode: CreateMode): String = { 35 | zk.create(path, data, acl, createMode) 36 | } 37 | 38 | def delete(path: String) { 39 | zk.delete(path, -1) 40 | } 41 | 42 | def isAlive: Boolean = { 43 | val result: Stat = zk.exists("/", false) // do not watch 44 | 45 | if (result.getVersion >= 0) { 46 | true 47 | } else { 48 | false 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /project/build/ZookeeperClientProject.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Process._ 3 | 4 | 5 | class ZookeeperClientProject(info: ProjectInfo) extends DefaultProject(info) { 6 | // Maven repositories 7 | val scalaToolsTesting = "testing.scala-tools.org" at "http://scala-tools.org/repo-releases/" 8 | val powerMock = "powermock-api" at "http://powermock.googlecode.com/svn/repo/" 9 | val mavenDotOrg = "repo1" at "http://repo1.maven.org/maven2/" 10 | val scalaToolsReleases = "scala-tools.org" at "http://scala-tools.org/repo-releases/" 11 | val reucon = "reucon" at "http://maven.reucon.com/public/" 12 | val lagDotNet = "lag.net" at "http://www.lag.net/repo/" 13 | val oauthDotNet = "oauth.net" at "http://oauth.googlecode.com/svn/code/maven" 14 | val javaDotNet = "download.java.net" at "http://download.java.net/maven/2/" 15 | val jBoss = "jboss-repo" at "http://repository.jboss.org/maven2/" 16 | val nest = "nest" at "http://www.lag.net/nest/" 17 | 18 | // dependencies 19 | val specs = "org.scala-tools.testing" % "specs" % "1.6.2" 20 | val vscaladoc = "org.scala-tools" % "vscaladoc" % "1.1-md-3" 21 | val markdownj = "markdownj" % "markdownj" % "1.0.2b4-0.3.0" 22 | val slf4jApi = "org.slf4j" % "slf4j-api" % "1.5.8" 23 | val slf4jLog = "org.slf4j" % "slf4j-log4j12" % "1.5.8" 24 | val log4j = "apache-log4j" % "log4j" % "1.2.15" 25 | val commonsLogging = "commons-logging" % "commons-logging" % "1.1" 26 | val commonsLang = "commons-lang" % "commons-lang" % "2.2" 27 | val oro = "oro" % "oro" % "2.0.7" 28 | val configgy = "net.lag" % "configgy" % "1.4.7" 29 | val mockito = "org.mockito" % "mockito-core" % "1.8.1" 30 | val xrayspecs = "com.twitter" % "xrayspecs" % "1.0.5" 31 | val hamcrest = "org.hamcrest" % "hamcrest-all" % "1.1" 32 | val asm = "asm" % "asm-all" % "2.2" 33 | val objenesis = "org.objenesis" % "objenesis" % "1.1" 34 | val javautils = "org.scala-tools" % "javautils" % "2.7.4-0.1" 35 | val ostrich = "com.twitter" % "ostrich" % "1.1.6" 36 | } 37 | -------------------------------------------------------------------------------- /src/test/scala/com/twitter/zookeeper/client/ZookeeperClientSpec.scala: -------------------------------------------------------------------------------- 1 | package com.twitter.zookeeper.client 2 | 3 | import java.net.{Socket, SocketException} 4 | import org.scala_tools.javautils.Imports._ 5 | import org.apache.zookeeper.{CreateMode, Watcher, WatchedEvent} 6 | import org.apache.zookeeper.CreateMode._ 7 | import org.apache.zookeeper.KeeperException.NoNodeException 8 | import org.apache.zookeeper.data.{ACL, Id} 9 | import org.specs._ 10 | 11 | 12 | class ZookeeperClientSpec extends Specification { 13 | "ZookeeperClient" should { 14 | val zookeeperHost = "localhost" 15 | val zookeeperPort = 2181 16 | 17 | val watcher = new FakeWatcher 18 | val zkClient = new ZookeeperClient(watcher, "%s:%s".format(zookeeperHost, zookeeperPort)) 19 | 20 | doBefore { 21 | // we need to be sure that a ZooKeeper server is running in order to test 22 | new Socket(zookeeperHost, zookeeperPort) must throwA[SocketException] 23 | } 24 | 25 | doLast { 26 | zkClient.close 27 | } 28 | 29 | "be able to be instantiated with a FakeWatcher" in { 30 | zkClient mustNot beNull 31 | } 32 | 33 | "connect to local Zookeeper server and retrieve version" in { 34 | zkClient.isAlive mustBe true 35 | } 36 | 37 | "get data at a known-good specified path" in { 38 | val results: Array[Byte] = zkClient.get("/") 39 | results.size must beGreaterThanOrEqualTo(0) 40 | } 41 | 42 | "get data at a known-bad specified path" in { 43 | zkClient.get("/thisdoesnotexist") must throwA[NoNodeException] 44 | } 45 | 46 | "get list of children" in { 47 | zkClient.getChildren("/") must notBeEmpty 48 | } 49 | 50 | "create a node at a specified path" in { 51 | val data: Array[Byte] = Array(0x63) 52 | val id = new Id("world", "anyone") 53 | val acl = new ACL(0, id) 54 | val aclList = List[ACL](acl).asJava 55 | val createMode = EPHEMERAL 56 | 57 | zkClient.create("/foo", data, aclList, createMode) mustEqual "/foo" 58 | zkClient.delete("/foo") 59 | } 60 | } 61 | } 62 | --------------------------------------------------------------------------------