From 3c69a3ff4e3705d087c3ef59d0267a7e3ae66a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=93=B6=E6=97=B6?= Date: Mon, 28 Oct 2013 17:08:13 +0800 Subject: [PATCH 1/3] Init zookeeper-3.3.5 of apache release. --- CHANGES.txt | 1369 ----- NOTICE.txt | 5 - README.txt | 36 - bin/README.txt | 6 - bin/zkCleanup.sh | 51 - bin/zkCli.cmd | 24 - bin/zkCli.sh | 41 - bin/zkEnv.cmd | 34 - bin/zkEnv.sh | 96 - bin/zkServer.cmd | 25 - bin/zkServer.sh | 122 - build.xml | 1347 ----- conf/configuration.xsl | 24 - conf/log4j.properties | 49 - conf/zoo_sample.cfg | 12 - docs/bookkeeperConfig.html | 382 -- docs/bookkeeperConfig.pdf | Bin 8731 -> 0 bytes docs/bookkeeperOverview.html | 692 --- docs/bookkeeperOverview.pdf | Bin 142977 -> 0 bytes docs/bookkeeperProgrammer.html | 1081 ---- docs/bookkeeperProgrammer.pdf | Bin 21083 -> 0 bytes docs/bookkeeperStarted.html | 446 -- docs/bookkeeperStarted.pdf | Bin 11778 -> 0 bytes docs/bookkeeperStream.html | 610 --- docs/bookkeeperStream.pdf | Bin 8245 -> 0 bytes docs/broken-links.xml | 2 - docs/images/2pc.jpg | Bin 15174 -> 0 bytes docs/images/bk-overview.jpg | Bin 124211 -> 0 bytes docs/images/built-with-forrest-button.png | Bin 1936 -> 0 bytes docs/images/favicon.ico | Bin 766 -> 0 bytes docs/images/hadoop-logo.jpg | Bin 9443 -> 0 bytes docs/images/instruction_arrow.png | Bin 285 -> 0 bytes docs/images/state_dia.jpg | Bin 51364 -> 0 bytes docs/images/zkcomponents.jpg | Bin 30831 -> 0 bytes docs/images/zknamespace.jpg | Bin 35414 -> 0 bytes docs/images/zkperfRW-3.2.jpg | Bin 41948 -> 0 bytes docs/images/zkperfRW.jpg | Bin 161542 -> 0 bytes docs/images/zkperfreliability.jpg | Bin 69825 -> 0 bytes docs/images/zkservice.jpg | Bin 86790 -> 0 bytes docs/images/zookeeper_small.gif | Bin 4847 -> 0 bytes docs/index.html | 390 -- docs/index.pdf | Bin 10615 -> 0 bytes docs/javaExample.html | 906 ---- docs/javaExample.pdf | Bin 30954 -> 0 bytes docs/linkmap.html | 433 -- docs/linkmap.pdf | Bin 3284 -> 0 bytes docs/recipes.html | 972 ---- docs/recipes.pdf | Bin 27986 -> 0 bytes docs/releasenotes.html | 1910 ------- docs/releasenotes.pdf | Bin 63756 -> 0 bytes docs/skin/CommonMessages_de.xml | 23 - docs/skin/CommonMessages_en_US.xml | 23 - docs/skin/CommonMessages_es.xml | 23 - docs/skin/CommonMessages_fr.xml | 23 - docs/skin/basic.css | 166 - docs/skin/breadcrumbs-optimized.js | 90 - docs/skin/breadcrumbs.js | 237 - docs/skin/fontsize.js | 166 - docs/skin/getBlank.js | 40 - docs/skin/getMenu.js | 45 - docs/skin/images/README.txt | 1 - docs/skin/images/add.jpg | Bin 1142 -> 0 bytes .../skin/images/built-with-forrest-button.png | Bin 1936 -> 0 bytes docs/skin/images/chapter.gif | Bin 49 -> 0 bytes docs/skin/images/chapter_open.gif | Bin 49 -> 0 bytes docs/skin/images/current.gif | Bin 54 -> 0 bytes docs/skin/images/error.png | Bin 1709 -> 0 bytes docs/skin/images/external-link.gif | Bin 71 -> 0 bytes docs/skin/images/fix.jpg | Bin 932 -> 0 bytes docs/skin/images/forrest-credit-logo.png | Bin 4633 -> 0 bytes docs/skin/images/hack.jpg | Bin 743 -> 0 bytes docs/skin/images/header_white_line.gif | Bin 37 -> 0 bytes docs/skin/images/info.png | Bin 1320 -> 0 bytes docs/skin/images/instruction_arrow.png | Bin 285 -> 0 bytes docs/skin/images/label.gif | Bin 54 -> 0 bytes docs/skin/images/page.gif | Bin 79 -> 0 bytes docs/skin/images/pdfdoc.gif | Bin 1008 -> 0 bytes docs/skin/images/poddoc.png | Bin 856 -> 0 bytes docs/skin/images/printer.gif | Bin 603 -> 0 bytes .../images/rc-b-l-15-1body-2menu-3menu.png | Bin 348 -> 0 bytes .../images/rc-b-r-15-1body-2menu-3menu.png | Bin 319 -> 0 bytes ...-5-1header-2tab-selected-3tab-selected.png | Bin 200 -> 0 bytes ...rc-t-l-5-1header-2searchbox-3searchbox.png | Bin 199 -> 0 bytes ...-5-1header-2tab-selected-3tab-selected.png | Bin 209 -> 0 bytes ...header-2tab-unselected-3tab-unselected.png | Bin 199 -> 0 bytes .../images/rc-t-r-15-1body-2menu-3menu.png | Bin 390 -> 0 bytes ...rc-t-r-5-1header-2searchbox-3searchbox.png | Bin 214 -> 0 bytes ...-5-1header-2tab-selected-3tab-selected.png | Bin 215 -> 0 bytes ...header-2tab-unselected-3tab-unselected.png | Bin 214 -> 0 bytes docs/skin/images/remove.jpg | Bin 1251 -> 0 bytes docs/skin/images/rss.png | Bin 360 -> 0 bytes docs/skin/images/spacer.gif | Bin 43 -> 0 bytes docs/skin/images/success.png | Bin 1291 -> 0 bytes docs/skin/images/txtdoc.png | Bin 784 -> 0 bytes docs/skin/images/update.jpg | Bin 990 -> 0 bytes docs/skin/images/valid-html401.png | Bin 2948 -> 0 bytes docs/skin/images/vcss.png | Bin 1134 -> 0 bytes docs/skin/images/warning.png | Bin 1215 -> 0 bytes docs/skin/images/xmldoc.gif | Bin 992 -> 0 bytes docs/skin/menu.js | 48 - docs/skin/note.txt | 50 - docs/skin/print.css | 54 - docs/skin/profile.css | 168 - docs/skin/prototype.js | 1257 ----- docs/skin/screen.css | 587 --- docs/zookeeperAdmin.html | 1682 ------ docs/zookeeperAdmin.pdf | Bin 67692 -> 0 bytes docs/zookeeperHierarchicalQuorums.html | 275 - docs/zookeeperHierarchicalQuorums.pdf | Bin 2521 -> 0 bytes docs/zookeeperInternals.html | 803 --- docs/zookeeperInternals.pdf | Bin 44380 -> 0 bytes docs/zookeeperJMX.html | 477 -- docs/zookeeperJMX.pdf | Bin 12240 -> 0 bytes docs/zookeeperObservers.html | 364 -- docs/zookeeperObservers.pdf | Bin 8660 -> 0 bytes docs/zookeeperOtherInfo.html | 230 - docs/zookeeperOtherInfo.pdf | Bin 2947 -> 0 bytes docs/zookeeperOver.html | 702 --- docs/zookeeperOver.pdf | Bin 307962 -> 0 bytes docs/zookeeperProgrammers.html | 2236 -------- docs/zookeeperProgrammers.pdf | Bin 134924 -> 0 bytes docs/zookeeperQuotas.html | 289 -- docs/zookeeperQuotas.pdf | Bin 5701 -> 0 bytes docs/zookeeperStarted.html | 643 --- docs/zookeeperStarted.pdf | Bin 23209 -> 0 bytes docs/zookeeperTutorial.html | 892 ---- docs/zookeeperTutorial.pdf | Bin 28912 -> 0 bytes ivy.xml | 65 - ivysettings.xml | 46 - src/java/main/overview.html => overview.html | 0 pom.xml | 71 + src/c/ChangeLog | 116 - src/c/INSTALL | 234 - src/c/LICENSE | 202 - src/c/Makefile.am | 108 - src/c/README | 124 - src/c/acinclude.m4 | 312 -- src/c/aminclude.am | 186 - src/c/c-doc.Doxyfile | 1252 ----- src/c/configure.ac | 149 - src/c/include/proto.h | 45 - src/c/include/recordio.h | 76 - src/c/include/zookeeper.h | 1402 ----- src/c/include/zookeeper_log.h | 51 - src/c/include/zookeeper_version.h | 33 - src/c/src/cli.c | 625 --- src/c/src/hashtable/LICENSE.txt | 30 - src/c/src/hashtable/hashtable.c | 274 - src/c/src/hashtable/hashtable.h | 207 - src/c/src/hashtable/hashtable_itr.c | 188 - src/c/src/hashtable/hashtable_itr.h | 119 - src/c/src/hashtable/hashtable_private.h | 85 - src/c/src/load_gen.c | 276 - src/c/src/mt_adaptor.c | 383 -- src/c/src/recordio.c | 358 -- src/c/src/st_adaptor.c | 99 - src/c/src/zk_adaptor.h | 263 - src/c/src/zk_hashtable.c | 333 -- src/c/src/zk_hashtable.h | 69 - src/c/src/zk_log.c | 164 - src/c/src/zookeeper.c | 3246 ------------ src/c/tests/CollectionUtil.h | 195 - src/c/tests/CppAssertHelper.h | 37 - src/c/tests/LibCMocks.cc | 322 -- src/c/tests/LibCMocks.h | 408 -- src/c/tests/LibCSymTable.cc | 83 - src/c/tests/LibCSymTable.h | 107 - src/c/tests/MocksBase.cc | 36 - src/c/tests/MocksBase.h | 36 - src/c/tests/PthreadMocks.cc | 106 - src/c/tests/PthreadMocks.h | 449 -- src/c/tests/TestClient.cc | 1082 ---- src/c/tests/TestClientRetry.cc | 273 - src/c/tests/TestDriver.cc | 173 - src/c/tests/TestOperations.cc | 675 --- src/c/tests/TestWatchers.cc | 773 --- src/c/tests/TestZookeeperClose.cc | 472 -- src/c/tests/TestZookeeperInit.cc | 301 -- src/c/tests/ThreadingUtil.cc | 79 - src/c/tests/ThreadingUtil.h | 261 - src/c/tests/Util.cc | 51 - src/c/tests/Util.h | 137 - src/c/tests/Vector.h | 37 - src/c/tests/ZKMocks.cc | 519 -- src/c/tests/ZKMocks.h | 509 -- src/c/tests/wrappers-mt.opt | 3 - src/c/tests/wrappers.opt | 6 - src/c/tests/zkServer.sh | 145 - src/contrib/bookkeeper/README.txt | 62 - .../bookkeeper/benchmark/MySqlClient.java | 137 - .../bookkeeper/benchmark/TestClient.java | 252 - src/contrib/bookkeeper/build.xml | 144 - src/contrib/bookkeeper/conf/log4j.properties | 72 - src/contrib/bookkeeper/ivy.xml | 40 - .../org/apache/bookkeeper/bookie/Bookie.java | 536 -- .../bookkeeper/bookie/BookieException.java | 81 - .../bookkeeper/bookie/BufferedChannel.java | 168 - .../apache/bookkeeper/bookie/EntryLogger.java | 487 -- .../apache/bookkeeper/bookie/FileInfo.java | 124 - .../apache/bookkeeper/bookie/LedgerCache.java | 536 -- .../bookkeeper/bookie/LedgerDescriptor.java | 133 - .../bookkeeper/bookie/LedgerEntryPage.java | 151 - .../bookkeeper/bookie/MarkerFileChannel.java | 147 - .../bookkeeper/client/AsyncCallback.java | 126 - .../apache/bookkeeper/client/BKException.java | 238 - .../apache/bookkeeper/client/BookKeeper.java | 410 -- .../bookkeeper/client/BookieWatcher.java | 204 - .../bookkeeper/client/DigestManager.java | 162 - .../client/DistributionSchedule.java | 61 - .../bookkeeper/client/LedgerCreateOp.java | 167 - .../bookkeeper/client/LedgerDeleteOp.java | 80 - .../apache/bookkeeper/client/LedgerEntry.java | 78 - .../bookkeeper/client/LedgerHandle.java | 512 -- .../bookkeeper/client/LedgerMetadata.java | 190 - .../bookkeeper/client/LedgerOpenOp.java | 140 - .../bookkeeper/client/LedgerRecoveryOp.java | 176 - .../bookkeeper/client/MacDigestManager.java | 67 - .../bookkeeper/client/PendingAddOp.java | 138 - .../bookkeeper/client/PendingReadOp.java | 161 - .../RoundRobinDistributionSchedule.java | 87 - .../apache/bookkeeper/client/SyncCounter.java | 85 - .../apache/bookkeeper/proto/BookieClient.java | 178 - .../bookkeeper/proto/BookieProtocol.java | 75 - .../apache/bookkeeper/proto/BookieServer.java | 208 - .../proto/BookkeeperInternalCallbacks.java | 57 - .../bookkeeper/proto/NIOServerFactory.java | 517 -- .../proto/PerChannelBookieClient.java | 573 --- .../apache/bookkeeper/proto/ServerStats.java | 148 - .../streaming/LedgerInputStream.java | 173 - .../streaming/LedgerOutputStream.java | 147 - .../bookkeeper/tools/BookKeeperTools.java | 762 --- .../bookkeeper/util/LocalBookKeeper.java | 209 - .../java/org/apache/bookkeeper/util/Main.java | 54 - .../org/apache/bookkeeper/util/MathUtils.java | 38 - .../bookkeeper/util/OrderedSafeExecutor.java | 98 - .../apache/bookkeeper/util/SafeRunnable.java | 38 - .../apache/bookkeeper/util/StringUtils.java | 94 - .../bookkeeper/test/AsyncLedgerOpsTest.java | 256 - .../apache/bookkeeper/test/BaseTestCase.java | 176 - .../bookkeeper/test/BookieClientTest.java | 232 - .../bookkeeper/test/BookieFailureTest.java | 305 -- .../bookkeeper/test/BookieReadWriteTest.java | 666 --- .../bookkeeper/test/BookieRecoveryTest.java | 396 -- .../org/apache/bookkeeper/test/CloseTest.java | 74 - .../bookkeeper/test/ConcurrentLedgerTest.java | 178 - .../bookkeeper/test/LedgerDeleteTest.java | 163 - .../bookkeeper/test/LedgerRecoveryTest.java | 85 - .../bookkeeper/test/LoopbackClient.java | 117 - .../bookkeeper/test/NIOServerFactoryTest.java | 60 - src/contrib/build-contrib.xml | 226 - src/contrib/build.xml | 71 - src/contrib/fatjar/README.txt | 2 - src/contrib/fatjar/build.xml | 73 - src/contrib/fatjar/conf/mainClasses | 10 - .../org/apache/zookeeper/util/FatJarMain.java | 126 - src/contrib/hedwig/LICENSE.txt | 202 - src/contrib/hedwig/NOTICE.txt | 2 - src/contrib/hedwig/README | 3 - src/contrib/hedwig/client/pom.xml | 73 - .../hedwig/client/src/main/cpp/Makefile.am | 29 - .../hedwig/client/src/main/cpp/aminclude.am | 186 - .../hedwig/client/src/main/cpp/c-doc.Doxyfile | 1252 ----- .../hedwig/client/src/main/cpp/config.h.in | 56 - .../hedwig/client/src/main/cpp/configure.ac | 40 - .../client/src/main/cpp/hedwig-0.1.pc.in | 30 - .../client/src/main/cpp/inc/hedwig/callback.h | 45 - .../client/src/main/cpp/inc/hedwig/client.h | 79 - .../src/main/cpp/inc/hedwig/exceptions.h | 51 - .../client/src/main/cpp/inc/hedwig/publish.h | 61 - .../src/main/cpp/inc/hedwig/subscribe.h | 52 - .../client/src/main/cpp/lib/Makefile.am | 32 - .../client/src/main/cpp/lib/channel.cpp | 444 -- .../hedwig/client/src/main/cpp/lib/channel.h | 156 - .../hedwig/client/src/main/cpp/lib/client.cpp | 59 - .../client/src/main/cpp/lib/clientimpl.cpp | 387 -- .../client/src/main/cpp/lib/clientimpl.h | 150 - .../hedwig/client/src/main/cpp/lib/data.cpp | 173 - .../hedwig/client/src/main/cpp/lib/data.h | 99 - .../src/main/cpp/lib/eventdispatcher.cpp | 76 - .../client/src/main/cpp/lib/eventdispatcher.h | 44 - .../client/src/main/cpp/lib/exceptions.cpp | 27 - .../client/src/main/cpp/lib/publisherimpl.cpp | 85 - .../client/src/main/cpp/lib/publisherimpl.h | 54 - .../src/main/cpp/lib/subscriberimpl.cpp | 448 -- .../client/src/main/cpp/lib/subscriberimpl.h | 166 - .../hedwig/client/src/main/cpp/lib/util.cpp | 141 - .../hedwig/client/src/main/cpp/lib/util.h | 86 - .../hedwig/client/src/main/cpp/log4cpp.conf | 49 - .../client/src/main/cpp/m4/ax_boost_asio.m4 | 111 - .../client/src/main/cpp/m4/ax_boost_base.m4 | 252 - .../client/src/main/cpp/m4/ax_boost_thread.m4 | 149 - .../client/src/main/cpp/m4/ax_doxygen.m4 | 533 -- .../client/src/main/cpp/scripts/log4cpp.conf | 49 - .../src/main/cpp/scripts/network-delays.sh | 64 - .../src/main/cpp/scripts/server-control.sh | 49 - .../client/src/main/cpp/scripts/tester.sh | 95 - .../client/src/main/cpp/test/Makefile.am | 26 - .../hedwig/client/src/main/cpp/test/main.cpp | 77 - .../client/src/main/cpp/test/publishtest.cpp | 286 -- .../src/main/cpp/test/pubsubdatatest.cpp | 47 - .../client/src/main/cpp/test/pubsubtest.cpp | 333 -- .../src/main/cpp/test/servercontrol.cpp | 184 - .../client/src/main/cpp/test/servercontrol.h | 66 - .../src/main/cpp/test/subscribetest.cpp | 238 - .../hedwig/client/src/main/cpp/test/test.sh | 21 - .../hedwig/client/src/main/cpp/test/util.h | 143 - .../client/src/main/cpp/test/utiltest.cpp | 90 - .../hedwig/client/api/MessageHandler.java | 48 - .../apache/hedwig/client/api/Publisher.java | 63 - .../apache/hedwig/client/api/Subscriber.java | 237 - .../client/benchmark/BenchmarkPublisher.java | 133 - .../client/benchmark/BenchmarkSubscriber.java | 136 - .../client/benchmark/BenchmarkUtils.java | 176 - .../client/benchmark/BenchmarkWorker.java | 46 - .../client/benchmark/HedwigBenchmark.java | 127 - .../client/conf/ClientConfiguration.java | 148 - .../client/data/MessageConsumeData.java | 58 - .../apache/hedwig/client/data/PubSubData.java | 149 - .../hedwig/client/data/TopicSubscriber.java | 74 - .../InvalidSubscriberIdException.java | 37 - .../ServerRedirectLoopException.java | 38 - .../TooManyServerRedirectsException.java | 39 - .../handlers/MessageConsumeCallback.java | 95 - .../client/handlers/PubSubCallback.java | 87 - .../handlers/PublishResponseHandler.java | 70 - .../handlers/SubscribeReconnectCallback.java | 113 - .../handlers/SubscribeResponseHandler.java | 329 -- .../handlers/UnsubscribeResponseHandler.java | 83 - .../netty/ClientChannelPipelineFactory.java | 58 - .../hedwig/client/netty/ConnectCallback.java | 122 - .../hedwig/client/netty/HedwigClient.java | 359 -- .../hedwig/client/netty/HedwigPublisher.java | 224 - .../hedwig/client/netty/HedwigSubscriber.java | 585 --- .../hedwig/client/netty/ResponseHandler.java | 365 -- .../hedwig/client/netty/WriteCallback.java | 98 - .../client/ssl/SslClientContextFactory.java | 41 - .../hedwig/client/ssl/SslContextFactory.java | 65 - .../hedwig/conf/AbstractConfiguration.java | 45 - .../java/org/apache/hedwig/util/Callback.java | 47 - .../org/apache/hedwig/util/CallbackUtils.java | 185 - .../apache/hedwig/util/ConcurrencyUtils.java | 49 - .../java/org/apache/hedwig/util/Either.java | 50 - .../org/apache/hedwig/util/FileUtils.java | 97 - .../hedwig/util/HedwigSocketAddress.java | 138 - .../java/org/apache/hedwig/util/Option.java | 43 - .../java/org/apache/hedwig/util/Pair.java | 42 - .../org/apache/hedwig/util/PathUtils.java | 56 - .../src/main/resources/log4j.properties | 32 - .../org/apache/hedwig/client/AppTest.java | 51 - .../org/apache/hedwig/util/TestFileUtils.java | 41 - .../hedwig/util/TestHedwigSocketAddress.java | 104 - .../org/apache/hedwig/util/TestPathUtils.java | 54 - src/contrib/hedwig/conf/hw_client_sample.conf | 7 - src/contrib/hedwig/conf/hw_server_sample.conf | 10 - src/contrib/hedwig/doc/build.txt | 146 - src/contrib/hedwig/doc/dev.txt | 338 -- src/contrib/hedwig/doc/doc.txt | 17 - src/contrib/hedwig/doc/user.txt | 252 - src/contrib/hedwig/formatter.xml | 286 -- src/contrib/hedwig/pom.xml | 68 - src/contrib/hedwig/protocol/Makefile | 26 - src/contrib/hedwig/protocol/pom.xml | 77 - .../hedwig/exceptions/PubSubException.java | 162 - .../protoextensions/MessageIdUtils.java | 153 - .../protoextensions/PubSubResponseUtils.java | 43 - .../SubscriptionStateUtils.java | 41 - .../src/main/protobuf/PubSubProtocol.proto | 175 - src/contrib/hedwig/scripts/README.txt | 39 - src/contrib/hedwig/scripts/analyze.py | 201 - src/contrib/hedwig/scripts/hw.bash | 734 --- src/contrib/hedwig/scripts/hwServer.sh | 101 - src/contrib/hedwig/scripts/quote | 23 - src/contrib/hedwig/server/lib/README | 4 - src/contrib/hedwig/server/pom.xml | 153 - .../server/benchmark/AbstractBenchmark.java | 104 - .../server/benchmark/BookieBenchmark.java | 103 - .../server/benchmark/BookkeeperBenchmark.java | 91 - .../hedwig/server/benchmark/FakeBookie.java | 100 - .../server/common/ByteStringInterner.java | 45 - .../server/common/ServerConfiguration.java | 268 - .../common/TerminateJVMExceptionHandler.java | 31 - .../hedwig/server/common/TopicOpQueuer.java | 111 - .../hedwig/server/common/UnexpectedError.java | 35 - .../server/delivery/ChannelEndPoint.java | 81 - .../server/delivery/DeliveryCallback.java | 27 - .../server/delivery/DeliveryEndPoint.java | 28 - .../server/delivery/DeliveryManager.java | 29 - .../server/delivery/FIFODeliveryManager.java | 561 -- .../hedwig/server/handlers/BaseHandler.java | 64 - .../handlers/ChannelDisconnectListener.java | 29 - .../server/handlers/ConsumeHandler.java | 64 - .../hedwig/server/handlers/Handler.java | 37 - .../server/handlers/PublishHandler.java | 68 - .../server/handlers/SubscribeHandler.java | 153 - .../server/handlers/UnsubscribeHandler.java | 72 - .../hedwig/server/netty/PubSubServer.java | 364 -- .../netty/PubSubServerPipelineFactory.java | 76 - .../hedwig/server/netty/UmbrellaHandler.java | 158 - .../BookkeeperPersistenceManager.java | 739 --- .../hedwig/server/persistence/CacheKey.java | 74 - .../hedwig/server/persistence/CacheValue.java | 93 - .../hedwig/server/persistence/Factory.java | 22 - .../LocalDBPersistenceManager.java | 426 -- .../hedwig/server/persistence/MapMethods.java | 62 - .../server/persistence/PersistRequest.java | 58 - .../persistence/PersistenceManager.java | 91 - .../PersistenceManagerWithRangeScan.java | 27 - .../server/persistence/RangeScanRequest.java | 77 - .../server/persistence/ReadAheadCache.java | 703 --- .../server/persistence/ScanCallback.java | 63 - .../persistence/ScanCallbackWithContext.java | 37 - .../server/persistence/ScanRequest.java | 64 - .../hedwig/server/proxy/ChannelTracker.java | 124 - .../hedwig/server/proxy/HedwigProxy.java | 159 - .../server/proxy/ProxyConfiguration.java | 36 - .../server/proxy/ProxyConsumeHandler.java | 57 - .../server/proxy/ProxyPublishHander.java | 62 - .../proxy/ProxyStartDeliveryHandler.java | 129 - .../proxy/ProxyStopDeliveryHandler.java | 73 - .../server/proxy/ProxySubscribeHandler.java | 82 - .../server/proxy/ProxyUnsubscribeHandler.java | 74 - .../server/regions/HedwigHubClient.java | 48 - .../regions/HedwigHubClientFactory.java | 60 - .../server/regions/HedwigHubSubscriber.java | 70 - .../hedwig/server/regions/RegionManager.java | 180 - .../server/ssl/SslServerContextFactory.java | 53 - .../AbstractSubscriptionManager.java | 450 -- .../subscriptions/AllToAllTopologyFilter.java | 39 - .../InMemorySubscriptionManager.java | 78 - .../InMemorySubscriptionState.java | 64 - .../server/subscriptions/MessageFilter.java | 31 - .../SubscriptionEventListener.java | 57 - .../subscriptions/SubscriptionManager.java | 104 - .../server/subscriptions/TrueFilter.java | 32 - .../subscriptions/ZkSubscriptionManager.java | 226 - .../server/topics/AbstractTopicManager.java | 187 - .../hedwig/server/topics/TopicManager.java | 75 - .../topics/TopicOwnershipChangeListener.java | 28 - .../topics/TrivialOwnAllTopicManager.java | 52 - .../hedwig/server/topics/ZkTopicManager.java | 428 -- .../hedwig/zookeeper/SafeAsynBKCallback.java | 104 - .../hedwig/zookeeper/SafeAsyncCallback.java | 35 - .../hedwig/zookeeper/SafeAsyncZKCallback.java | 98 - .../org/apache/hedwig/zookeeper/ZkUtils.java | 78 - .../hedwig/server/src/main/resources/p12.pass | 1 - .../server/src/main/resources/server.p12 | Bin 3925 -> 0 bytes .../java/org/apache/hedwig/HelperMethods.java | 57 - .../java/org/apache/hedwig/ServerControl.java | 231 - .../apache/hedwig/ServerControlDaemon.java | 171 - .../java/org/apache/hedwig/StubCallback.java | 51 - .../org/apache/hedwig/StubScanCallback.java | 57 - .../hedwig/client/TestPubSubClient.java | 230 - .../hedwig/server/HedwigHubTestBase.java | 133 - .../hedwig/server/HedwigRegionTestBase.java | 221 - .../PubSubServerStandAloneTestBase.java | 61 - .../server/delivery/StubDeliveryManager.java | 65 - .../server/handlers/TestBaseHandler.java | 116 - .../server/handlers/TestSubUnsubHandler.java | 166 - .../server/integration/TestHedwigHub.java | 692 --- .../server/integration/TestHedwigRegion.java | 93 - .../hedwig/server/netty/TestPubSubServer.java | 260 - .../server/netty/WriteRecordingChannel.java | 170 - .../persistence/BookKeeperTestBase.java | 126 - .../persistence/StubPersistenceManager.java | 112 - .../server/persistence/StubScanCallback.java | 48 - ...tBookKeeperPersistenceManagerBlackBox.java | 76 - ...tBookkeeperPersistenceManagerWhiteBox.java | 129 - ...TestLocalDBPersistenceManagerBlackBox.java | 53 - .../TestPersistenceManagerBlackBox.java | 305 -- .../TestReadAheadCacheBlackBox.java | 54 - .../TestReadAheadCacheWhiteBox.java | 268 - .../StubSubscriptionManager.java | 52 - .../TestZkSubscriptionManager.java | 206 - .../server/topics/StubTopicManager.java | 64 - .../server/topics/TestZkTopicManager.java | 311 -- .../apache/hedwig/zookeeper/TestZkUtils.java | 47 - .../hedwig/zookeeper/ZooKeeperTestBase.java | 92 - src/contrib/huebrowser/README | 62 - src/contrib/huebrowser/zkui/Makefile | 21 - src/contrib/huebrowser/zkui/setup.py | 46 - .../huebrowser/zkui/src/zkui/__init__.py | 16 - src/contrib/huebrowser/zkui/src/zkui/forms.py | 29 - .../huebrowser/zkui/src/zkui/models.py | 17 - src/contrib/huebrowser/zkui/src/zkui/rest.py | 230 - .../huebrowser/zkui/src/zkui/settings.py | 30 - .../zkui/src/zkui/static/art/line_icons.png | Bin 7499 -> 0 bytes .../zkui/src/zkui/static/art/zkui.png | Bin 4430 -> 0 bytes .../zkui/src/zkui/static/bootstrap.js | 32 - .../zkui/src/zkui/static/css/zkui.css | 56 - .../zkui/src/zkui/static/help/index.html | 10 - .../src/zkui/static/js/Source/Zkui/Zkui.js | 50 - .../zkui/src/zkui/static/js/package.yml | 5 - src/contrib/huebrowser/zkui/src/zkui/stats.py | 170 - .../zkui/src/zkui/templates/clients.mako | 51 - .../zkui/src/zkui/templates/create.mako | 34 - .../zkui/src/zkui/templates/edit.mako | 34 - .../zkui/src/zkui/templates/index.mako | 54 - .../src/zkui/templates/shared_components.mako | 66 - .../zkui/src/zkui/templates/tree.mako | 75 - .../zkui/src/zkui/templates/view.mako | 128 - src/contrib/huebrowser/zkui/src/zkui/urls.py | 28 - src/contrib/huebrowser/zkui/src/zkui/utils.py | 33 - src/contrib/huebrowser/zkui/src/zkui/views.py | 165 - .../huebrowser/zkui/src/zkui/windmilltests.py | 23 - src/contrib/loggraph/README.txt | 69 - src/contrib/loggraph/bin/loggraph-dev.sh | 43 - src/contrib/loggraph/bin/loggraph.sh | 48 - src/contrib/loggraph/build.xml | 70 - src/contrib/loggraph/ivy.xml | 41 - .../zookeeper/graph/FilterException.java | 22 - .../org/apache/zookeeper/graph/FilterOp.java | 75 - .../apache/zookeeper/graph/FilterParser.java | 131 - .../apache/zookeeper/graph/JsonGenerator.java | 223 - .../apache/zookeeper/graph/Log4JEntry.java | 40 - .../apache/zookeeper/graph/Log4JSource.java | 380 -- .../org/apache/zookeeper/graph/LogEntry.java | 46 - .../apache/zookeeper/graph/LogIterator.java | 26 - .../org/apache/zookeeper/graph/LogServer.java | 66 - .../apache/zookeeper/graph/LogSkipList.java | 94 - .../org/apache/zookeeper/graph/LogSource.java | 33 - .../zookeeper/graph/MeasureThroughput.java | 103 - .../zookeeper/graph/MergedLogSource.java | 218 - .../graph/RandomAccessFileReader.java | 327 -- .../zookeeper/graph/TransactionEntry.java | 59 - .../apache/zookeeper/graph/TxnLogSource.java | 377 -- .../zookeeper/graph/filterops/AndOp.java | 33 - .../apache/zookeeper/graph/filterops/Arg.java | 36 - .../zookeeper/graph/filterops/EqualsOp.java | 44 - .../graph/filterops/GreaterThanOp.java | 70 - .../zookeeper/graph/filterops/LessThanOp.java | 69 - .../zookeeper/graph/filterops/NotOp.java | 31 - .../zookeeper/graph/filterops/NumberArg.java | 28 - .../zookeeper/graph/filterops/OrOp.java | 33 - .../zookeeper/graph/filterops/StringArg.java | 28 - .../zookeeper/graph/filterops/SymbolArg.java | 27 - .../zookeeper/graph/filterops/XorOp.java | 40 - .../zookeeper/graph/servlets/FileLoader.java | 60 - .../apache/zookeeper/graph/servlets/Fs.java | 69 - .../zookeeper/graph/servlets/GraphData.java | 84 - .../zookeeper/graph/servlets/JsonServlet.java | 85 - .../zookeeper/graph/servlets/NumEvents.java | 86 - .../graph/servlets/StaticContent.java | 50 - .../zookeeper/graph/servlets/Throughput.java | 124 - .../apache/zookeeper/graph/log4j.properties | 11 - .../zookeeper/graph/resources/date.format.js | 126 - .../apache/zookeeper/graph/resources/g.bar.js | 385 -- .../apache/zookeeper/graph/resources/g.dot.js | 110 - .../zookeeper/graph/resources/g.line.js | 230 - .../apache/zookeeper/graph/resources/g.pie.js | 205 - .../zookeeper/graph/resources/g.raphael.js | 481 -- .../zookeeper/graph/resources/load-big.gif | Bin 1924 -> 0 bytes .../apache/zookeeper/graph/resources/load.gif | Bin 673 -> 0 bytes .../zookeeper/graph/resources/loggraph.css | 54 - .../zookeeper/graph/resources/loggraph.js | 262 - .../zookeeper/graph/resources/loggraph.log.js | 57 - .../graph/resources/loggraph.server.js | 329 -- .../graph/resources/loggraph.session.js | 202 - .../graph/resources/loggraph.stats.js | 44 - .../zookeeper/graph/resources/loggraph.ui.js | 377 -- .../zookeeper/graph/resources/main.html | 60 - .../zookeeper/graph/resources/raphael.js | 3296 ------------ .../zookeeper/graph/resources/yui-min.js | 12 - src/contrib/monitoring/JMX-RESOURCES | 38 - src/contrib/monitoring/README | 84 - src/contrib/monitoring/cacti/README | 56 - src/contrib/monitoring/check_zookeeper.py | 353 -- src/contrib/monitoring/ganglia/README | 48 - src/contrib/monitoring/ganglia/Screenshot.png | Bin 111055 -> 0 bytes src/contrib/monitoring/ganglia/modpython.conf | 28 - .../monitoring/ganglia/zookeeper.pyconf | 49 - .../monitoring/ganglia/zookeeper_ganglia.py | 209 - src/contrib/monitoring/nagios/README.txt | 86 - .../monitoring/nagios/Screenshot-1.png | Bin 196668 -> 0 bytes src/contrib/monitoring/nagios/Screenshot.png | Bin 163646 -> 0 bytes src/contrib/monitoring/nagios/hostgroups.cfg | 25 - src/contrib/monitoring/nagios/services.cfg | 67 - src/contrib/monitoring/nagios/zookeeper.cfg | 30 - src/contrib/monitoring/test.py | 282 -- src/contrib/rest/NOTICE.txt | 7 - src/contrib/rest/README.txt | 72 - src/contrib/rest/SPEC.txt | 355 -- src/contrib/rest/build.xml | 167 - src/contrib/rest/conf/keys/README | 8 - src/contrib/rest/conf/keys/rest.cer | Bin 595 -> 0 bytes src/contrib/rest/conf/keys/rest.jks | Bin 1363 -> 0 bytes src/contrib/rest/conf/log4j.properties | 72 - src/contrib/rest/conf/rest.properties | 70 - src/contrib/rest/ivy.xml | 45 - src/contrib/rest/rest.sh | 90 - .../zookeeper/server/jersey/RestMain.java | 150 - .../server/jersey/ZooKeeperService.java | 241 - .../server/jersey/cfg/Credentials.java | 47 - .../zookeeper/server/jersey/cfg/Endpoint.java | 72 - .../zookeeper/server/jersey/cfg/HostPort.java | 51 - .../server/jersey/cfg/HostPortSet.java | 51 - .../zookeeper/server/jersey/cfg/RestCfg.java | 106 - .../server/jersey/filters/HTTPBasicAuth.java | 87 - .../server/jersey/jaxb/ZChildren.java | 80 - .../server/jersey/jaxb/ZChildrenJSON.java | 76 - .../zookeeper/server/jersey/jaxb/ZError.java | 41 - .../zookeeper/server/jersey/jaxb/ZPath.java | 63 - .../server/jersey/jaxb/ZSession.java | 55 - .../zookeeper/server/jersey/jaxb/ZStat.java | 106 - .../jersey/resources/JAXBContextResolver.java | 72 - .../resources/KeeperExceptionMapper.java | 86 - .../resources/RuntimeExceptionMapper.java | 55 - .../jersey/resources/SessionsResource.java | 134 - .../server/jersey/resources/ZErrorWriter.java | 63 - .../jersey/resources/ZNodeResource.java | 412 -- src/contrib/rest/src/python/README.txt | 9 - .../rest/src/python/demo_master_election.py | 90 - src/contrib/rest/src/python/demo_queue.py | 99 - src/contrib/rest/src/python/test.py | 163 - src/contrib/rest/src/python/zk_dump_tree.py | 108 - src/contrib/rest/src/python/zkrest.py | 218 - .../apache/zookeeper/server/jersey/Base.java | 93 - .../zookeeper/server/jersey/CreateTest.java | 162 - .../zookeeper/server/jersey/DeleteTest.java | 94 - .../zookeeper/server/jersey/ExistsTest.java | 79 - .../server/jersey/GetChildrenTest.java | 138 - .../zookeeper/server/jersey/GetTest.java | 122 - .../server/jersey/RestTestSuite.java | 42 - .../zookeeper/server/jersey/RootTest.java | 67 - .../zookeeper/server/jersey/SessionTest.java | 133 - .../zookeeper/server/jersey/SetTest.java | 154 - .../zookeeper/server/jersey/WadlTest.java | 43 - src/contrib/rest/src/test/zkServer.sh | 91 - src/contrib/zkfuse/Makefile.am | 4 - src/contrib/zkfuse/README.txt | 62 - src/contrib/zkfuse/build.xml | 61 - src/contrib/zkfuse/configure.ac | 70 - src/contrib/zkfuse/src/Makefile.am | 7 - src/contrib/zkfuse/src/blockingqueue.h | 154 - src/contrib/zkfuse/src/doxygen.cfg | 1242 ----- src/contrib/zkfuse/src/event.cc | 29 - src/contrib/zkfuse/src/event.h | 553 -- src/contrib/zkfuse/src/log.cc | 36 - src/contrib/zkfuse/src/log.h | 116 - src/contrib/zkfuse/src/log4cxx.properties | 28 - src/contrib/zkfuse/src/mutex.h | 169 - src/contrib/zkfuse/src/thread.cc | 41 - src/contrib/zkfuse/src/thread.h | 99 - src/contrib/zkfuse/src/zkadapter.cc | 881 ---- src/contrib/zkfuse/src/zkadapter.h | 718 --- src/contrib/zkfuse/src/zkfuse.cc | 4492 ----------------- src/contrib/zkperl/Changes | 61 - src/contrib/zkperl/LICENSE | 202 - src/contrib/zkperl/MANIFEST | 23 - src/contrib/zkperl/Makefile.PL | 62 - src/contrib/zkperl/NOTICE | 6 - src/contrib/zkperl/README | 80 - src/contrib/zkperl/ZooKeeper.pm | 1258 ----- src/contrib/zkperl/ZooKeeper.xs | 2669 ---------- src/contrib/zkperl/build.xml | 61 - src/contrib/zkperl/build/check_zk_version.c | 25 - src/contrib/zkperl/build/check_zk_version.h | 27 - src/contrib/zkperl/t/10_invalid.t | 773 --- src/contrib/zkperl/t/15_thread.t | 121 - src/contrib/zkperl/t/20_tie.t | 353 -- src/contrib/zkperl/t/22_stat_tie.t | 438 -- src/contrib/zkperl/t/24_watch_tie.t | 292 -- src/contrib/zkperl/t/30_connect.t | 202 - src/contrib/zkperl/t/35_log.t | 88 - src/contrib/zkperl/t/40_basic.t | 277 - src/contrib/zkperl/t/45_class.t | 408 -- src/contrib/zkperl/t/50_access.t | 340 -- src/contrib/zkperl/t/60_watch.t | 304 -- src/contrib/zkperl/t/util.pl | 62 - src/contrib/zkperl/typemap | 38 - src/contrib/zkpython/README | 109 - src/contrib/zkpython/build.xml | 98 - src/contrib/zkpython/src/c/pyzk_docstrings.h | 594 --- src/contrib/zkpython/src/c/zookeeper.c | 1603 ------ src/contrib/zkpython/src/examples/README | 8 - .../src/examples/watch_znode_for_changes.py | 202 - src/contrib/zkpython/src/python/setup.py | 34 - src/contrib/zkpython/src/python/zk.py | 76 - src/contrib/zkpython/src/test/acl_test.py | 109 - src/contrib/zkpython/src/test/async_test.py | 33 - .../zkpython/src/test/callback_test.py | 155 - .../zkpython/src/test/clientid_test.py | 48 - .../zkpython/src/test/close_deadlock_test.py | 50 - .../zkpython/src/test/connection_test.py | 129 - src/contrib/zkpython/src/test/create_test.py | 104 - src/contrib/zkpython/src/test/delete_test.py | 68 - src/contrib/zkpython/src/test/exists_test.py | 64 - src/contrib/zkpython/src/test/get_set_test.py | 188 - src/contrib/zkpython/src/test/run_tests.sh | 40 - src/contrib/zkpython/src/test/zkServer.sh | 77 - src/contrib/zkpython/src/test/zktestbase.py | 93 - src/contrib/zktreeutil/Makefile.am | 4 - src/contrib/zktreeutil/README.txt | 74 - src/contrib/zktreeutil/build.xml | 61 - src/contrib/zktreeutil/configure.ac | 66 - src/contrib/zktreeutil/src/Makefile.am | 24 - src/contrib/zktreeutil/src/SimpleTree.h | 150 - src/contrib/zktreeutil/src/ZkAdaptor.cc | 513 -- src/contrib/zktreeutil/src/ZkAdaptor.h | 327 -- src/contrib/zktreeutil/src/ZkTreeUtil.cc | 705 --- src/contrib/zktreeutil/src/ZkTreeUtil.h | 262 - src/contrib/zktreeutil/src/ZkTreeUtilMain.cc | 247 - src/contrib/zktreeutil/tests/zk_sample.xml | 44 - src/contrib/zooinspector/NOTICE.txt | 3 - src/contrib/zooinspector/README.txt | 94 - src/contrib/zooinspector/build.xml | 152 - .../config/defaultConnectionSettings.cfg | 5 - .../config/defaultNodeVeiwers.cfg | 3 - .../zooinspector/icons/edtsrclkup_co.gif | Bin 204 -> 0 bytes src/contrib/zooinspector/icons/file_obj.gif | Bin 354 -> 0 bytes src/contrib/zooinspector/icons/fldr_obj.gif | Bin 216 -> 0 bytes src/contrib/zooinspector/icons/info_obj.gif | Bin 121 -> 0 bytes src/contrib/zooinspector/icons/jspdecl.gif | Bin 190 -> 0 bytes src/contrib/zooinspector/icons/launch_run.gif | Bin 379 -> 0 bytes .../zooinspector/icons/launch_stop.gif | Bin 916 -> 0 bytes src/contrib/zooinspector/icons/new_con.gif | Bin 353 -> 0 bytes src/contrib/zooinspector/icons/refresh.gif | Bin 327 -> 0 bytes src/contrib/zooinspector/icons/save_edit.gif | Bin 639 -> 0 bytes .../zooinspector/icons/search_next.gif | Bin 332 -> 0 bytes .../zooinspector/icons/search_prev.gif | Bin 323 -> 0 bytes src/contrib/zooinspector/icons/trash.gif | Bin 132 -> 0 bytes src/contrib/zooinspector/ivy.xml | 45 - .../zooinspector/lib/jtoaster-1.0.4.jar | Bin 14975 -> 0 bytes src/contrib/zooinspector/lib/log4j.properties | 9 - .../licences/Apache Software Licence v2.0.txt | 202 - .../zooinspector/licences/epl-v10.html | 261 - .../zookeeper/inspector/ZooInspector.java | 66 - .../BasicDataEncryptionManager.java | 50 - .../encryption/DataEncryptionManager.java | 39 - .../gui/NodeViewersChangeListener.java | 37 - .../gui/ZooInspectorAboutDialog.java | 80 - ...ooInspectorConnectionPropertiesDialog.java | 321 -- .../gui/ZooInspectorIconResources.java | 118 - .../gui/ZooInspectorNodeViewersDialog.java | 605 --- .../gui/ZooInspectorNodeViewersPanel.java | 140 - .../inspector/gui/ZooInspectorPanel.java | 361 -- .../inspector/gui/ZooInspectorTreeViewer.java | 362 -- .../apache/zookeeper/inspector/gui/about.html | 21 - .../gui/nodeviewer/NodeViewerACL.java | 187 - .../gui/nodeviewer/NodeViewerData.java | 146 - .../gui/nodeviewer/NodeViewerMetaData.java | 186 - .../nodeviewer/ZooInspectorNodeViewer.java | 138 - .../inspector/logger/LoggerFactory.java | 38 - .../inspector/manager/NodeListener.java | 37 - .../zookeeper/inspector/manager/Pair.java | 120 - .../manager/ZooInspectorManager.java | 139 - .../manager/ZooInspectorManagerImpl.java | 852 ---- .../manager/ZooInspectorNodeManager.java | 33 - .../manager/ZooInspectorNodeTreeManager.java | 43 - .../manager/ZooInspectorReadOnlyManager.java | 99 - .../zookeeper/retry/ZooKeeperRetry.java | 288 -- src/contrib/zooinspector/zooInspector-dev.sh | 18 - src/contrib/zooinspector/zooInspector.cmd | 18 - src/contrib/zooinspector/zooInspector.sh | 18 - src/docs/forrest.properties | 106 - src/docs/src/documentation/README.txt | 7 - src/docs/src/documentation/TODO.txt | 227 - .../classes/CatalogManager.properties | 37 - .../content/xdocs/bookkeeperConfig.xml | 156 - .../content/xdocs/bookkeeperOverview.xml | 419 -- .../content/xdocs/bookkeeperProgrammer.xml | 678 --- .../content/xdocs/bookkeeperStarted.xml | 208 - .../content/xdocs/bookkeeperStream.xml | 331 -- .../src/documentation/content/xdocs/index.xml | 98 - .../content/xdocs/javaExample.xml | 663 --- .../documentation/content/xdocs/recipes.xml | 637 --- .../content/xdocs/releasenotes.xml | 1250 ----- .../src/documentation/content/xdocs/site.xml | 105 - .../src/documentation/content/xdocs/tabs.xml | 36 - .../content/xdocs/zookeeperAdmin.xml | 1430 ------ .../xdocs/zookeeperHierarchicalQuorums.xml | 75 - .../content/xdocs/zookeeperInternals.xml | 484 -- .../content/xdocs/zookeeperJMX.xml | 236 - .../content/xdocs/zookeeperObservers.xml | 145 - .../content/xdocs/zookeeperOtherInfo.xml | 46 - .../content/xdocs/zookeeperOver.xml | 464 -- .../content/xdocs/zookeeperProgrammers.xml | 1628 ------ .../content/xdocs/zookeeperQuotas.xml | 72 - .../content/xdocs/zookeeperStarted.xml | 423 -- .../content/xdocs/zookeeperTutorial.xml | 675 --- .../documentation/resources/images/2pc.jpg | Bin 15174 -> 0 bytes .../resources/images/bk-overview.jpg | Bin 124211 -> 0 bytes .../resources/images/favicon.ico | Bin 766 -> 0 bytes .../resources/images/hadoop-logo.jpg | Bin 9443 -> 0 bytes .../resources/images/state_dia.dia | Bin 2597 -> 0 bytes .../resources/images/state_dia.jpg | Bin 51364 -> 0 bytes .../documentation/resources/images/zkarch.jpg | Bin 24535 -> 0 bytes .../resources/images/zkcomponents.jpg | Bin 30831 -> 0 bytes .../resources/images/zknamespace.jpg | Bin 35414 -> 0 bytes .../resources/images/zkperfRW-3.2.jpg | Bin 41948 -> 0 bytes .../resources/images/zkperfRW.jpg | Bin 161542 -> 0 bytes .../resources/images/zkperfreliability.jpg | Bin 69825 -> 0 bytes .../resources/images/zkservice.jpg | Bin 86790 -> 0 bytes .../resources/images/zookeeper_small.gif | Bin 4847 -> 0 bytes src/docs/src/documentation/skinconf.xml | 360 -- src/docs/status.xml | 74 - src/java/OldChangeLog | 0 src/java/lib/cobertura/README.txt | 3 - src/java/lib/jdiff/zookeeper_3.1.1.xml | 2717 ---------- src/java/lib/jline-0.9.94.LICENSE.txt | 33 - src/java/lib/log4j-1.2.15.LICENSE.txt | 202 - src/java/libtest/accessive.LICENSE.txt | 202 - src/java/libtest/accessive.jar | Bin 20879 -> 0 bytes .../apache/jute/compiler/CSharpGenerator.java | 53 - .../apache/zookeeper/ClientCnxnSocket.java | 126 - .../apache/zookeeper/ClientCnxnSocketNIO.java | 303 -- .../zookeeper/ClientCnxnSocketNetty.java | 299 -- .../server/NIOServerCnxnFactory.java | 306 -- .../zookeeper/server/NettyServerCnxn.java | 819 --- .../server/NettyServerCnxnFactory.java | 394 -- .../apache/zookeeper/server/ServerCnxn.java | 432 -- .../zookeeper/server/ServerCnxnFactory.java | 134 - .../org/apache/zookeeper/server/Stats.java | 68 - src/java/systest/README.txt | 62 - .../zookeeper/test/system/BaseSysTest.java | 264 - .../test/system/DuplicateNameException.java | 26 - .../zookeeper/test/system/GenerateLoad.java | 714 --- .../zookeeper/test/system/Instance.java | 59 - .../test/system/InstanceContainer.java | 309 -- .../test/system/InstanceManager.java | 356 -- .../test/system/NoAssignmentException.java | 24 - .../test/system/NoAvailableContainers.java | 28 - .../test/system/QuorumPeerInstance.java | 275 - .../zookeeper/test/system/SimpleClient.java | 116 - .../zookeeper/test/system/SimpleSysTest.java | 170 - src/java/test/checkstyle-noframes-sorted.xsl | 178 - src/java/test/checkstyle.xml | 170 - src/java/test/config/findbugsExcludeFile.xml | 124 - .../test/data/invalidsnap/version-2/log.1 | Bin 59360 -> 0 bytes .../test/data/invalidsnap/version-2/log.274 | Bin 91616 -> 0 bytes .../test/data/invalidsnap/version-2/log.63b | Bin 49008 -> 0 bytes .../data/invalidsnap/version-2/snapshot.0 | Bin 296 -> 0 bytes .../data/invalidsnap/version-2/snapshot.272 | Bin 56408 -> 0 bytes .../data/invalidsnap/version-2/snapshot.639 | Bin 143163 -> 0 bytes .../data/invalidsnap/version-2/snapshot.83f | Bin 4824 -> 0 bytes src/java/test/data/upgrade/log.100000001 | Bin 2458624 -> 0 bytes src/java/test/data/upgrade/log.100001bf0 | Bin 1025024 -> 0 bytes src/java/test/data/upgrade/snapshot.100000000 | Bin 73 -> 0 bytes src/java/test/data/upgrade/snapshot.100001bec | Bin 2567595 -> 0 bytes .../apache/zookeeper/JUnit4ZKTestRunner.java | 64 - .../org/apache/zookeeper/PortAssignment.java | 34 - .../apache/zookeeper/TestableZooKeeper.java | 87 - .../test/org/apache/zookeeper/ThreadUtil.java | 70 - .../test/org/apache/zookeeper/ZKTestCase.java | 69 - .../org/apache/zookeeper/ZooKeeperTest.java | 125 - .../org/apache/zookeeper/server/CRCTest.java | 188 - .../zookeeper/server/DataTreeUnitTest.java | 60 - .../server/DeserializationPerfTest.java | 118 - .../zookeeper/server/InvalidSnapshotTest.java | 111 - .../server/SerializationPerfTest.java | 124 - .../apache/zookeeper/server/ToStringTest.java | 38 - .../server/ZooKeeperServerMainTest.java | 137 - .../zookeeper/server/ZooKeeperServerTest.java | 95 - .../server/quorum/QuorumPeerMainTest.java | 375 -- .../server/quorum/QuorumPeerTestBase.java | 115 - .../apache/zookeeper/test/ACLRootTest.java | 97 - .../org/apache/zookeeper/test/ACLTest.java | 184 - .../zookeeper/test/AsyncHammerTest.java | 244 - .../org/apache/zookeeper/test/AsyncOps.java | 570 --- .../apache/zookeeper/test/AsyncOpsTest.java | 191 - .../org/apache/zookeeper/test/AsyncTest.java | 171 - .../org/apache/zookeeper/test/AuthTest.java | 125 - .../zookeeper/test/ChrootAsyncTest.java | 46 - .../zookeeper/test/ChrootClientTest.java | 53 - .../org/apache/zookeeper/test/ChrootTest.java | 136 - .../org/apache/zookeeper/test/ClientBase.java | 605 --- .../zookeeper/test/ClientHammerTest.java | 245 - .../zookeeper/test/ClientPortBindTest.java | 118 - .../apache/zookeeper/test/ClientRetry.java | 72 - .../org/apache/zookeeper/test/ClientTest.java | 744 --- .../apache/zookeeper/test/CnxManagerTest.java | 199 - .../apache/zookeeper/test/CreateModeTest.java | 81 - .../apache/zookeeper/test/DataTreeTest.java | 64 - .../test/DisconnectableZooKeeper.java | 58 - .../apache/zookeeper/test/EventTypeTest.java | 50 - .../zookeeper/test/FLELostMessageTest.java | 173 - .../zookeeper/test/FLENewEpochTest.java | 192 - .../apache/zookeeper/test/FLERestartTest.java | 203 - .../org/apache/zookeeper/test/FLETest.java | 315 -- .../zookeeper/test/FLEZeroWeightTest.java | 182 - .../test/FourLetterWordsQuorumTest.java | 111 - .../zookeeper/test/FourLetterWordsTest.java | 99 - .../zookeeper/test/GetChildren2Test.java | 136 - .../test/HierarchicalQuorumTest.java | 280 - .../apache/zookeeper/test/IntegrityCheck.java | 231 - .../zookeeper/test/InvalidSnapshotTest.java | 99 - .../org/apache/zookeeper/test/JMXEnv.java | 176 - .../zookeeper/test/KeeperStateTest.java | 76 - .../zookeeper/test/LENonTerminateTest.java | 377 -- .../org/apache/zookeeper/test/LETest.java | 138 - .../apache/zookeeper/test/MaxCnxnsTest.java | 129 - .../zookeeper/test/NettyNettySuiteBase.java | 48 - .../test/NettyNettySuiteHammerTest.java | 30 - .../zookeeper/test/NettyNettySuiteTest.java | 37 - .../zookeeper/test/NettyNioSuiteBase.java | 43 - .../test/NettyNioSuiteHammerTest.java | 30 - .../zookeeper/test/NettyNioSuiteTest.java | 37 - .../zookeeper/test/NioNettySuiteBase.java | 43 - .../test/NioNettySuiteHammerTest.java | 30 - .../zookeeper/test/NioNettySuiteTest.java | 37 - .../apache/zookeeper/test/NullDataTest.java | 75 - .../org/apache/zookeeper/test/OOMTest.java | 160 - .../test/ObserverHierarchicalQuorumTest.java | 42 - .../test/ObserverQuorumHammerTest.java | 39 - .../apache/zookeeper/test/ObserverTest.java | 232 - .../apache/zookeeper/test/PurgeTxnTest.java | 87 - .../org/apache/zookeeper/test/QuorumBase.java | 354 -- .../zookeeper/test/QuorumHammerTest.java | 51 - .../zookeeper/test/QuorumQuotaTest.java | 62 - .../org/apache/zookeeper/test/QuorumTest.java | 401 -- .../org/apache/zookeeper/test/QuorumUtil.java | 237 - .../zookeeper/test/QuorumZxidSyncTest.java | 171 - .../apache/zookeeper/test/RecoveryTest.java | 205 - .../zookeeper/test/RepeatStartupTest.java | 68 - .../apache/zookeeper/test/SessionTest.java | 424 -- .../apache/zookeeper/test/SledgeHammer.java | 115 - .../apache/zookeeper/test/StandaloneTest.java | 64 - .../org/apache/zookeeper/test/StatTest.java | 205 - .../apache/zookeeper/test/SyncCallTest.java | 108 - .../org/apache/zookeeper/test/TestHammer.java | 62 - .../apache/zookeeper/test/TruncateTest.java | 153 - .../apache/zookeeper/test/UpgradeTest.java | 104 - .../zookeeper/test/WatchedEventTest.java | 95 - .../zookeeper/test/WatcherFuncTest.java | 478 -- .../apache/zookeeper/test/WatcherTest.java | 385 -- .../test/ZkDatabaseCorruptionTest.java | 132 - .../zookeeper/test/ZooKeeperQuotaTest.java | 71 - .../zookeeper/test/ZooKeeperTestClient.java | 421 -- src/lastRevision.bat | 23 - src/lastRevision.sh | 21 - .../org/apache/jute/BinaryInputArchive.java | 0 .../org/apache/jute/BinaryOutputArchive.java | 0 .../org/apache/jute/CsvInputArchive.java | 0 .../org/apache/jute/CsvOutputArchive.java | 0 .../java}/org/apache/jute/Index.java | 0 .../java}/org/apache/jute/InputArchive.java | 0 .../java}/org/apache/jute/OutputArchive.java | 0 .../java}/org/apache/jute/Record.java | 0 .../java}/org/apache/jute/RecordReader.java | 0 .../java}/org/apache/jute/RecordWriter.java | 0 .../java}/org/apache/jute/Utils.java | 0 .../org/apache/jute/XmlInputArchive.java | 0 .../org/apache/jute/XmlOutputArchive.java | 0 .../org/apache/jute/compiler/CGenerator.java | 0 .../apache/jute/compiler/CppGenerator.java | 0 .../org/apache/jute/compiler/JBoolean.java | 10 +- .../org/apache/jute/compiler/JBuffer.java | 2 +- .../java}/org/apache/jute/compiler/JByte.java | 2 +- .../org/apache/jute/compiler/JCompType.java | 17 +- .../org/apache/jute/compiler/JDouble.java | 2 +- .../org/apache/jute/compiler/JField.java | 42 - .../java}/org/apache/jute/compiler/JFile.java | 4 - .../org/apache/jute/compiler/JFloat.java | 2 +- .../java}/org/apache/jute/compiler/JInt.java | 2 +- .../java}/org/apache/jute/compiler/JLong.java | 2 +- .../java}/org/apache/jute/compiler/JMap.java | 52 +- .../org/apache/jute/compiler/JRecord.java | 228 +- .../org/apache/jute/compiler/JString.java | 2 +- .../java}/org/apache/jute/compiler/JType.java | 69 +- .../org/apache/jute/compiler/JVector.java | 51 +- .../apache/jute/compiler/JavaGenerator.java | 0 .../compiler/generated/ParseException.java | 0 .../apache/jute/compiler/generated/Rcc.java | 0 .../jute/compiler/generated/RccConstants.java | 0 .../compiler/generated/RccTokenManager.java | 0 .../compiler/generated/SimpleCharStream.java | 0 .../apache/jute/compiler/generated/Token.java | 0 .../compiler/generated/TokenMgrError.java | 0 .../jute/compiler/generated/package.html | 0 .../org/apache/jute/compiler/generated/rcc.jj | 0 .../org/apache/jute/compiler/package.html | 0 .../java}/org/apache/jute/package.html | 0 .../org/apache/zookeeper/AsyncCallback.java | 0 .../org/apache/zookeeper/ClientCnxn.java | 1104 ++-- .../apache/zookeeper/ClientWatchManager.java | 0 .../org/apache/zookeeper/CreateMode.java | 0 .../org/apache/zookeeper/Environment.java | 0 .../apache/zookeeper/JLineZNodeCompletor.java | 0 .../org/apache/zookeeper/KeeperException.java | 0 .../java}/org/apache/zookeeper/Quotas.java | 0 .../apache/zookeeper/ServerAdminClient.java | 0 .../org/apache/zookeeper/StatsTrack.java | 0 .../java}/org/apache/zookeeper/Version.java | 5 +- .../org/apache/zookeeper/WatchedEvent.java | 0 .../java}/org/apache/zookeeper/Watcher.java | 16 +- .../java}/org/apache/zookeeper/ZooDefs.java | 0 .../java}/org/apache/zookeeper/ZooKeeper.java | 145 +- .../org/apache/zookeeper/ZooKeeperMain.java | 6 +- .../zookeeper/client/FourLetterWordMain.java | 79 + .../org/apache/zookeeper/common/PathTrie.java | 0 .../apache/zookeeper/common/PathUtils.java | 0 .../java/org/apache/zookeeper/data/ACL.java | 123 + .../java/org/apache/zookeeper/data/Id.java | 122 + .../java/org/apache/zookeeper/data/Stat.java | 284 ++ .../apache/zookeeper/data/StatPersisted.java | 248 + .../zookeeper/data/StatPersistedV1.java | 230 + .../org/apache/zookeeper/jmx/CommonNames.java | 0 .../apache/zookeeper/jmx/MBeanRegistry.java | 0 .../org/apache/zookeeper/jmx/ManagedUtil.java | 0 .../org/apache/zookeeper/jmx/ZKMBeanInfo.java | 0 .../apache/zookeeper/proto/AuthPacket.java | 144 + .../zookeeper/proto/ConnectRequest.java | 180 + .../zookeeper/proto/ConnectResponse.java | 162 + .../apache/zookeeper/proto/CreateRequest.java | 174 + .../zookeeper/proto/CreateResponse.java | 104 + .../apache/zookeeper/proto/DeleteRequest.java | 122 + .../apache/zookeeper/proto/ExistsRequest.java | 122 + .../zookeeper/proto/ExistsResponse.java | 105 + .../apache/zookeeper/proto/GetACLRequest.java | 104 + .../zookeeper/proto/GetACLResponse.java | 143 + .../zookeeper/proto/GetChildren2Request.java | 122 + .../zookeeper/proto/GetChildren2Response.java | 142 + .../zookeeper/proto/GetChildrenRequest.java | 122 + .../zookeeper/proto/GetChildrenResponse.java | 125 + .../zookeeper/proto/GetDataRequest.java | 122 + .../zookeeper/proto/GetDataResponse.java | 127 + .../proto/GetMaxChildrenRequest.java | 104 + .../proto/GetMaxChildrenResponse.java | 104 + .../apache/zookeeper/proto/ReplyHeader.java | 140 + .../apache/zookeeper/proto/RequestHeader.java | 122 + .../apache/zookeeper/proto/SetACLRequest.java | 158 + .../zookeeper/proto/SetACLResponse.java | 105 + .../zookeeper/proto/SetDataRequest.java | 144 + .../zookeeper/proto/SetDataResponse.java | 105 + .../proto/SetMaxChildrenRequest.java | 122 + .../apache/zookeeper/proto/SetWatches.java | 229 + .../apache/zookeeper/proto/SyncRequest.java | 104 + .../apache/zookeeper/proto/SyncResponse.java | 104 + .../apache/zookeeper/proto/WatcherEvent.java | 140 + .../apache/zookeeper/proto/op_result_t.java | 144 + .../server/ByteBufferInputStream.java | 0 .../zookeeper/server/ConnectionBean.java | 13 +- .../zookeeper/server/ConnectionMXBean.java | 0 .../org/apache/zookeeper/server/DataNode.java | 0 .../org/apache/zookeeper/server/DataTree.java | 141 +- .../apache/zookeeper/server/DataTreeBean.java | 11 +- .../zookeeper/server/DataTreeMXBean.java | 0 .../server/FinalRequestProcessor.java | 39 +- .../apache/zookeeper/server/LogFormatter.java | 0 .../zookeeper/server/NIOServerCnxn.java | 1063 +++- .../apache/zookeeper/server/ObserverBean.java | 0 .../server/PrepRequestProcessor.java | 32 +- .../apache/zookeeper/server/PurgeTxnLog.java | 0 .../org/apache/zookeeper/server/Request.java | 0 .../zookeeper/server/RequestProcessor.java | 9 +- .../apache/zookeeper/server/ServerCnxn.java | 108 + .../apache/zookeeper/server/ServerConfig.java | 0 .../apache/zookeeper/server/ServerStats.java | 0 .../zookeeper/server/SessionTracker.java | 7 + .../zookeeper/server/SessionTrackerImpl.java | 18 +- .../server/SyncRequestProcessor.java | 5 +- .../zookeeper/server/TraceFormatter.java | 0 .../apache/zookeeper/server/WatchManager.java | 6 +- .../apache/zookeeper/server/ZKDatabase.java | 47 +- .../zookeeper/server/ZooKeeperServer.java | 244 +- .../zookeeper/server/ZooKeeperServerBean.java | 6 +- .../server/ZooKeeperServerMXBean.java | 0 .../zookeeper/server/ZooKeeperServerMain.java | 5 +- .../org/apache/zookeeper/server/ZooTrace.java | 0 .../server/auth/AuthenticationProvider.java | 0 .../auth/DigestAuthenticationProvider.java | 4 +- .../server/auth/IPAuthenticationProvider.java | 6 +- .../server/auth/ProviderRegistry.java | 0 .../org/apache/zookeeper/server/package.html | 0 .../server/persistence/FileHeader.java | 140 + .../server/persistence/FileSnap.java | 0 .../server/persistence/FileTxnLog.java | 67 +- .../server/persistence/FileTxnSnapLog.java | 62 +- .../server/persistence/SnapShot.java | 0 .../zookeeper/server/persistence/TxnLog.java | 0 .../zookeeper/server/persistence/Util.java | 10 +- .../server/quorum/AckRequestProcessor.java | 0 .../server/quorum/AuthFastLeaderElection.java | 0 .../server/quorum/CommitProcessor.java | 5 +- .../zookeeper/server/quorum/Election.java | 0 .../server/quorum/FastLeaderElection.java | 4 +- .../zookeeper/server/quorum/Follower.java | 12 +- .../zookeeper/server/quorum/FollowerBean.java | 0 .../server/quorum/FollowerMXBean.java | 0 .../quorum/FollowerRequestProcessor.java | 1 - .../quorum/FollowerZooKeeperServer.java | 3 +- .../zookeeper/server/quorum/Leader.java | 50 +- .../zookeeper/server/quorum/LeaderBean.java | 0 .../server/quorum/LeaderElection.java | 0 .../server/quorum/LeaderElectionBean.java | 0 .../server/quorum/LeaderElectionMXBean.java | 0 .../zookeeper/server/quorum/LeaderMXBean.java | 0 .../server/quorum/LeaderZooKeeperServer.java | 8 +- .../zookeeper/server/quorum/Learner.java | 73 +- .../server/quorum/LearnerHandler.java | 135 +- .../server/quorum/LearnerSessionTracker.java | 4 + .../server/quorum/LearnerSyncRequest.java | 0 .../server/quorum/LearnerZooKeeperServer.java | 7 +- .../server/quorum/LocalPeerBean.java | 8 +- .../server/quorum/LocalPeerMXBean.java | 0 .../zookeeper/server/quorum/Observer.java | 3 +- .../server/quorum/ObserverMXBean.java | 0 .../quorum/ObserverRequestProcessor.java | 1 - .../quorum/ObserverZooKeeperServer.java | 0 .../quorum/ProposalRequestProcessor.java | 15 +- .../zookeeper/server/quorum/QuorumBean.java | 0 .../server/quorum/QuorumCnxManager.java | 82 +- .../zookeeper/server/quorum/QuorumMXBean.java | 0 .../zookeeper/server/quorum/QuorumPacket.java | 174 + .../zookeeper/server/quorum/QuorumPeer.java | 57 +- .../server/quorum/QuorumPeerConfig.java | 14 - .../server/quorum/QuorumPeerMain.java | 8 +- .../zookeeper/server/quorum/QuorumStats.java | 0 .../server/quorum/QuorumZooKeeperServer.java | 0 .../server/quorum/RemotePeerBean.java | 0 .../server/quorum/RemotePeerMXBean.java | 0 .../quorum/SendAckRequestProcessor.java | 0 .../zookeeper/server/quorum/ServerBean.java | 0 .../zookeeper/server/quorum/ServerMXBean.java | 0 .../apache/zookeeper/server/quorum/Vote.java | 0 .../quorum/flexible/QuorumHierarchical.java | 0 .../server/quorum/flexible/QuorumMaj.java | 0 .../quorum/flexible/QuorumVerifier.java | 0 .../zookeeper/server/upgrade/DataNodeV1.java | 0 .../zookeeper/server/upgrade/DataTreeV1.java | 0 .../zookeeper/server/upgrade/UpgradeMain.java | 0 .../server/upgrade/UpgradeSnapShot.java | 0 .../server/upgrade/UpgradeSnapShotV1.java | 0 .../zookeeper/server/util/Profiler.java | 0 .../zookeeper/server/util/SerializeUtils.java | 0 .../zookeeper/txn/CreateSessionTxn.java | 104 + .../org/apache/zookeeper/txn/CreateTxn.java | 174 + .../org/apache/zookeeper/txn/DeleteTxn.java | 104 + .../org/apache/zookeeper/txn/ErrorTxn.java | 104 + .../org/apache/zookeeper/txn/SetACLTxn.java | 158 + .../org/apache/zookeeper/txn/SetDataTxn.java | 144 + .../zookeeper/txn/SetMaxChildrenTxn.java | 122 + .../org/apache/zookeeper/txn/TxnHeader.java | 176 + .../org/apache/zookeeper/version/Info.java} | 42 +- .../apache/zookeeper/version/util/VerGen.java | 67 +- src/recipes/README.txt | 25 - src/recipes/build-recipes.xml | 162 - src/recipes/build.xml | 62 - src/recipes/lock/README.txt | 28 - src/recipes/lock/build.xml | 128 - src/recipes/lock/src/c/INSTALL | 234 - src/recipes/lock/src/c/LICENSE | 202 - src/recipes/lock/src/c/Makefile.am | 46 - src/recipes/lock/src/c/README.txt | 28 - src/recipes/lock/src/c/acinclude.m4 | 312 -- src/recipes/lock/src/c/aminclude.am | 186 - src/recipes/lock/src/c/c-doc.Doxyfile | 1252 ----- src/recipes/lock/src/c/configure.ac | 82 - src/recipes/lock/src/c/include/zoo_lock.h | 168 - src/recipes/lock/src/c/src/zoo_lock.c | 396 -- src/recipes/lock/src/c/tests/TestClient.cc | 201 - src/recipes/lock/src/c/tests/TestDriver.cc | 114 - src/recipes/lock/src/c/tests/Util.cc | 30 - src/recipes/lock/src/c/tests/Util.h | 134 - src/recipes/lock/src/c/tests/zkServer.sh | 75 - .../zookeeper/recipes/lock/LockListener.java | 38 - .../recipes/lock/ProtocolSupport.java | 192 - .../zookeeper/recipes/lock/WriteLock.java | 295 -- .../zookeeper/recipes/lock/ZNodeName.java | 109 - .../recipes/lock/ZooKeeperOperation.java | 38 - .../zookeeper/recipes/lock/WriteLockTest.java | 156 - .../zookeeper/recipes/lock/ZNodeNameTest.java | 59 - src/recipes/queue/README.txt | 30 - src/recipes/queue/build.xml | 128 - src/recipes/queue/src/c/INSTALL | 234 - src/recipes/queue/src/c/LICENSE | 202 - src/recipes/queue/src/c/Makefile.am | 46 - src/recipes/queue/src/c/README.txt | 30 - src/recipes/queue/src/c/acinclude.m4 | 312 -- src/recipes/queue/src/c/aminclude.am | 186 - src/recipes/queue/src/c/c-doc.Doxyfile | 1252 ----- src/recipes/queue/src/c/configure.ac | 82 - src/recipes/queue/src/c/include/zoo_queue.h | 118 - src/recipes/queue/src/c/src/zoo_queue.c | 442 -- src/recipes/queue/src/c/tests/TestClient.cc | 452 -- src/recipes/queue/src/c/tests/TestDriver.cc | 114 - src/recipes/queue/src/c/tests/Util.cc | 30 - src/recipes/queue/src/c/tests/Util.h | 134 - src/recipes/queue/src/c/tests/zkServer.sh | 75 - .../recipes/queue/DistributedQueue.java | 312 -- .../recipes/queue/DistributedQueueTest.java | 286 -- src/zookeeper.jute | 252 - 1181 files changed, 9009 insertions(+), 174496 deletions(-) delete mode 100644 CHANGES.txt delete mode 100644 NOTICE.txt delete mode 100644 README.txt delete mode 100644 bin/README.txt delete mode 100755 bin/zkCleanup.sh delete mode 100644 bin/zkCli.cmd delete mode 100755 bin/zkCli.sh delete mode 100644 bin/zkEnv.cmd delete mode 100755 bin/zkEnv.sh delete mode 100644 bin/zkServer.cmd delete mode 100755 bin/zkServer.sh delete mode 100644 build.xml delete mode 100644 conf/configuration.xsl delete mode 100644 conf/log4j.properties delete mode 100644 conf/zoo_sample.cfg delete mode 100644 docs/bookkeeperConfig.html delete mode 100644 docs/bookkeeperConfig.pdf delete mode 100644 docs/bookkeeperOverview.html delete mode 100644 docs/bookkeeperOverview.pdf delete mode 100644 docs/bookkeeperProgrammer.html delete mode 100644 docs/bookkeeperProgrammer.pdf delete mode 100644 docs/bookkeeperStarted.html delete mode 100644 docs/bookkeeperStarted.pdf delete mode 100644 docs/bookkeeperStream.html delete mode 100644 docs/bookkeeperStream.pdf delete mode 100644 docs/broken-links.xml delete mode 100644 docs/images/2pc.jpg delete mode 100644 docs/images/bk-overview.jpg delete mode 100644 docs/images/built-with-forrest-button.png delete mode 100644 docs/images/favicon.ico delete mode 100644 docs/images/hadoop-logo.jpg delete mode 100644 docs/images/instruction_arrow.png delete mode 100644 docs/images/state_dia.jpg delete mode 100644 docs/images/zkcomponents.jpg delete mode 100644 docs/images/zknamespace.jpg delete mode 100644 docs/images/zkperfRW-3.2.jpg delete mode 100644 docs/images/zkperfRW.jpg delete mode 100644 docs/images/zkperfreliability.jpg delete mode 100644 docs/images/zkservice.jpg delete mode 100644 docs/images/zookeeper_small.gif delete mode 100644 docs/index.html delete mode 100644 docs/index.pdf delete mode 100644 docs/javaExample.html delete mode 100644 docs/javaExample.pdf delete mode 100644 docs/linkmap.html delete mode 100644 docs/linkmap.pdf delete mode 100644 docs/recipes.html delete mode 100644 docs/recipes.pdf delete mode 100644 docs/releasenotes.html delete mode 100644 docs/releasenotes.pdf delete mode 100644 docs/skin/CommonMessages_de.xml delete mode 100644 docs/skin/CommonMessages_en_US.xml delete mode 100644 docs/skin/CommonMessages_es.xml delete mode 100644 docs/skin/CommonMessages_fr.xml delete mode 100644 docs/skin/basic.css delete mode 100644 docs/skin/breadcrumbs-optimized.js delete mode 100644 docs/skin/breadcrumbs.js delete mode 100644 docs/skin/fontsize.js delete mode 100644 docs/skin/getBlank.js delete mode 100644 docs/skin/getMenu.js delete mode 100644 docs/skin/images/README.txt delete mode 100644 docs/skin/images/add.jpg delete mode 100644 docs/skin/images/built-with-forrest-button.png delete mode 100644 docs/skin/images/chapter.gif delete mode 100644 docs/skin/images/chapter_open.gif delete mode 100644 docs/skin/images/current.gif delete mode 100644 docs/skin/images/error.png delete mode 100644 docs/skin/images/external-link.gif delete mode 100644 docs/skin/images/fix.jpg delete mode 100644 docs/skin/images/forrest-credit-logo.png delete mode 100644 docs/skin/images/hack.jpg delete mode 100644 docs/skin/images/header_white_line.gif delete mode 100644 docs/skin/images/info.png delete mode 100644 docs/skin/images/instruction_arrow.png delete mode 100644 docs/skin/images/label.gif delete mode 100644 docs/skin/images/page.gif delete mode 100644 docs/skin/images/pdfdoc.gif delete mode 100644 docs/skin/images/poddoc.png delete mode 100644 docs/skin/images/printer.gif delete mode 100644 docs/skin/images/rc-b-l-15-1body-2menu-3menu.png delete mode 100644 docs/skin/images/rc-b-r-15-1body-2menu-3menu.png delete mode 100644 docs/skin/images/rc-b-r-5-1header-2tab-selected-3tab-selected.png delete mode 100644 docs/skin/images/rc-t-l-5-1header-2searchbox-3searchbox.png delete mode 100644 docs/skin/images/rc-t-l-5-1header-2tab-selected-3tab-selected.png delete mode 100644 docs/skin/images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png delete mode 100644 docs/skin/images/rc-t-r-15-1body-2menu-3menu.png delete mode 100644 docs/skin/images/rc-t-r-5-1header-2searchbox-3searchbox.png delete mode 100644 docs/skin/images/rc-t-r-5-1header-2tab-selected-3tab-selected.png delete mode 100644 docs/skin/images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png delete mode 100644 docs/skin/images/remove.jpg delete mode 100644 docs/skin/images/rss.png delete mode 100644 docs/skin/images/spacer.gif delete mode 100644 docs/skin/images/success.png delete mode 100644 docs/skin/images/txtdoc.png delete mode 100644 docs/skin/images/update.jpg delete mode 100644 docs/skin/images/valid-html401.png delete mode 100644 docs/skin/images/vcss.png delete mode 100644 docs/skin/images/warning.png delete mode 100644 docs/skin/images/xmldoc.gif delete mode 100644 docs/skin/menu.js delete mode 100644 docs/skin/note.txt delete mode 100644 docs/skin/print.css delete mode 100644 docs/skin/profile.css delete mode 100644 docs/skin/prototype.js delete mode 100644 docs/skin/screen.css delete mode 100644 docs/zookeeperAdmin.html delete mode 100644 docs/zookeeperAdmin.pdf delete mode 100644 docs/zookeeperHierarchicalQuorums.html delete mode 100644 docs/zookeeperHierarchicalQuorums.pdf delete mode 100644 docs/zookeeperInternals.html delete mode 100644 docs/zookeeperInternals.pdf delete mode 100644 docs/zookeeperJMX.html delete mode 100644 docs/zookeeperJMX.pdf delete mode 100644 docs/zookeeperObservers.html delete mode 100644 docs/zookeeperObservers.pdf delete mode 100644 docs/zookeeperOtherInfo.html delete mode 100644 docs/zookeeperOtherInfo.pdf delete mode 100644 docs/zookeeperOver.html delete mode 100644 docs/zookeeperOver.pdf delete mode 100644 docs/zookeeperProgrammers.html delete mode 100644 docs/zookeeperProgrammers.pdf delete mode 100644 docs/zookeeperQuotas.html delete mode 100644 docs/zookeeperQuotas.pdf delete mode 100644 docs/zookeeperStarted.html delete mode 100644 docs/zookeeperStarted.pdf delete mode 100644 docs/zookeeperTutorial.html delete mode 100644 docs/zookeeperTutorial.pdf delete mode 100644 ivy.xml delete mode 100644 ivysettings.xml rename src/java/main/overview.html => overview.html (100%) create mode 100644 pom.xml delete mode 100644 src/c/ChangeLog delete mode 100644 src/c/INSTALL delete mode 100644 src/c/LICENSE delete mode 100644 src/c/Makefile.am delete mode 100644 src/c/README delete mode 100644 src/c/acinclude.m4 delete mode 100644 src/c/aminclude.am delete mode 100644 src/c/c-doc.Doxyfile delete mode 100644 src/c/configure.ac delete mode 100644 src/c/include/proto.h delete mode 100644 src/c/include/recordio.h delete mode 100644 src/c/include/zookeeper.h delete mode 100644 src/c/include/zookeeper_log.h delete mode 100644 src/c/include/zookeeper_version.h delete mode 100644 src/c/src/cli.c delete mode 100644 src/c/src/hashtable/LICENSE.txt delete mode 100644 src/c/src/hashtable/hashtable.c delete mode 100644 src/c/src/hashtable/hashtable.h delete mode 100644 src/c/src/hashtable/hashtable_itr.c delete mode 100644 src/c/src/hashtable/hashtable_itr.h delete mode 100644 src/c/src/hashtable/hashtable_private.h delete mode 100644 src/c/src/load_gen.c delete mode 100644 src/c/src/mt_adaptor.c delete mode 100644 src/c/src/recordio.c delete mode 100644 src/c/src/st_adaptor.c delete mode 100644 src/c/src/zk_adaptor.h delete mode 100644 src/c/src/zk_hashtable.c delete mode 100644 src/c/src/zk_hashtable.h delete mode 100644 src/c/src/zk_log.c delete mode 100644 src/c/src/zookeeper.c delete mode 100644 src/c/tests/CollectionUtil.h delete mode 100644 src/c/tests/CppAssertHelper.h delete mode 100644 src/c/tests/LibCMocks.cc delete mode 100644 src/c/tests/LibCMocks.h delete mode 100644 src/c/tests/LibCSymTable.cc delete mode 100644 src/c/tests/LibCSymTable.h delete mode 100644 src/c/tests/MocksBase.cc delete mode 100644 src/c/tests/MocksBase.h delete mode 100644 src/c/tests/PthreadMocks.cc delete mode 100644 src/c/tests/PthreadMocks.h delete mode 100644 src/c/tests/TestClient.cc delete mode 100644 src/c/tests/TestClientRetry.cc delete mode 100644 src/c/tests/TestDriver.cc delete mode 100644 src/c/tests/TestOperations.cc delete mode 100644 src/c/tests/TestWatchers.cc delete mode 100644 src/c/tests/TestZookeeperClose.cc delete mode 100644 src/c/tests/TestZookeeperInit.cc delete mode 100644 src/c/tests/ThreadingUtil.cc delete mode 100644 src/c/tests/ThreadingUtil.h delete mode 100644 src/c/tests/Util.cc delete mode 100644 src/c/tests/Util.h delete mode 100644 src/c/tests/Vector.h delete mode 100644 src/c/tests/ZKMocks.cc delete mode 100644 src/c/tests/ZKMocks.h delete mode 100644 src/c/tests/wrappers-mt.opt delete mode 100644 src/c/tests/wrappers.opt delete mode 100755 src/c/tests/zkServer.sh delete mode 100644 src/contrib/bookkeeper/README.txt delete mode 100644 src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/MySqlClient.java delete mode 100644 src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/TestClient.java delete mode 100644 src/contrib/bookkeeper/build.xml delete mode 100644 src/contrib/bookkeeper/conf/log4j.properties delete mode 100644 src/contrib/bookkeeper/ivy.xml delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/Bookie.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BookieException.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BufferedChannel.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/EntryLogger.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/FileInfo.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerCache.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerDescriptor.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerEntryPage.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/MarkerFileChannel.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/AsyncCallback.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BKException.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookKeeper.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookieWatcher.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DigestManager.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DistributionSchedule.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerCreateOp.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerDeleteOp.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerEntry.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerHandle.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerMetadata.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerOpenOp.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/MacDigestManager.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingAddOp.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingReadOp.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/RoundRobinDistributionSchedule.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/SyncCounter.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieClient.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieProtocol.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieServer.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookkeeperInternalCallbacks.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/NIOServerFactory.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/ServerStats.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerInputStream.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerOutputStream.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/tools/BookKeeperTools.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/LocalBookKeeper.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/Main.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/MathUtils.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/SafeRunnable.java delete mode 100644 src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/StringUtils.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/AsyncLedgerOpsTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BaseTestCase.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieClientTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieFailureTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieReadWriteTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieRecoveryTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/CloseTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/ConcurrentLedgerTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerDeleteTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerRecoveryTest.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LoopbackClient.java delete mode 100644 src/contrib/bookkeeper/test/org/apache/bookkeeper/test/NIOServerFactoryTest.java delete mode 100644 src/contrib/build-contrib.xml delete mode 100644 src/contrib/build.xml delete mode 100644 src/contrib/fatjar/README.txt delete mode 100644 src/contrib/fatjar/build.xml delete mode 100644 src/contrib/fatjar/conf/mainClasses delete mode 100644 src/contrib/fatjar/src/java/org/apache/zookeeper/util/FatJarMain.java delete mode 100644 src/contrib/hedwig/LICENSE.txt delete mode 100644 src/contrib/hedwig/NOTICE.txt delete mode 100644 src/contrib/hedwig/README delete mode 100644 src/contrib/hedwig/client/pom.xml delete mode 100644 src/contrib/hedwig/client/src/main/cpp/Makefile.am delete mode 100644 src/contrib/hedwig/client/src/main/cpp/aminclude.am delete mode 100644 src/contrib/hedwig/client/src/main/cpp/c-doc.Doxyfile delete mode 100644 src/contrib/hedwig/client/src/main/cpp/config.h.in delete mode 100644 src/contrib/hedwig/client/src/main/cpp/configure.ac delete mode 100644 src/contrib/hedwig/client/src/main/cpp/hedwig-0.1.pc.in delete mode 100644 src/contrib/hedwig/client/src/main/cpp/inc/hedwig/callback.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/inc/hedwig/client.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/inc/hedwig/exceptions.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/inc/hedwig/publish.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/inc/hedwig/subscribe.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/Makefile.am delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/channel.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/channel.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/client.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/data.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/data.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/exceptions.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/util.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/lib/util.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/log4cpp.conf delete mode 100644 src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_asio.m4 delete mode 100644 src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_base.m4 delete mode 100644 src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_thread.m4 delete mode 100644 src/contrib/hedwig/client/src/main/cpp/m4/ax_doxygen.m4 delete mode 100644 src/contrib/hedwig/client/src/main/cpp/scripts/log4cpp.conf delete mode 100644 src/contrib/hedwig/client/src/main/cpp/scripts/network-delays.sh delete mode 100644 src/contrib/hedwig/client/src/main/cpp/scripts/server-control.sh delete mode 100644 src/contrib/hedwig/client/src/main/cpp/scripts/tester.sh delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/Makefile.am delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/main.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/publishtest.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/pubsubdatatest.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/pubsubtest.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/servercontrol.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/servercontrol.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/subscribetest.cpp delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/test.sh delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/util.h delete mode 100644 src/contrib/hedwig/client/src/main/cpp/test/utiltest.cpp delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/MessageHandler.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Publisher.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Subscriber.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkPublisher.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkSubscriber.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkUtils.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkWorker.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/HedwigBenchmark.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/conf/ClientConfiguration.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/MessageConsumeData.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/PubSubData.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/TopicSubscriber.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/InvalidSubscriberIdException.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/ServerRedirectLoopException.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/TooManyServerRedirectsException.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/MessageConsumeCallback.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PubSubCallback.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PublishResponseHandler.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeReconnectCallback.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeResponseHandler.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/UnsubscribeResponseHandler.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ClientChannelPipelineFactory.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ConnectCallback.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigClient.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigPublisher.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigSubscriber.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ResponseHandler.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/WriteCallback.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslClientContextFactory.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslContextFactory.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/conf/AbstractConfiguration.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Callback.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/CallbackUtils.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/ConcurrencyUtils.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Either.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/FileUtils.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/HedwigSocketAddress.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Option.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Pair.java delete mode 100644 src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/PathUtils.java delete mode 100644 src/contrib/hedwig/client/src/main/resources/log4j.properties delete mode 100644 src/contrib/hedwig/client/src/test/java/org/apache/hedwig/client/AppTest.java delete mode 100644 src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestFileUtils.java delete mode 100644 src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestHedwigSocketAddress.java delete mode 100644 src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestPathUtils.java delete mode 100644 src/contrib/hedwig/conf/hw_client_sample.conf delete mode 100644 src/contrib/hedwig/conf/hw_server_sample.conf delete mode 100644 src/contrib/hedwig/doc/build.txt delete mode 100644 src/contrib/hedwig/doc/dev.txt delete mode 100644 src/contrib/hedwig/doc/doc.txt delete mode 100644 src/contrib/hedwig/doc/user.txt delete mode 100644 src/contrib/hedwig/formatter.xml delete mode 100644 src/contrib/hedwig/pom.xml delete mode 100644 src/contrib/hedwig/protocol/Makefile delete mode 100644 src/contrib/hedwig/protocol/pom.xml delete mode 100644 src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/exceptions/PubSubException.java delete mode 100644 src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/MessageIdUtils.java delete mode 100644 src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/PubSubResponseUtils.java delete mode 100644 src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/SubscriptionStateUtils.java delete mode 100644 src/contrib/hedwig/protocol/src/main/protobuf/PubSubProtocol.proto delete mode 100644 src/contrib/hedwig/scripts/README.txt delete mode 100755 src/contrib/hedwig/scripts/analyze.py delete mode 100755 src/contrib/hedwig/scripts/hw.bash delete mode 100755 src/contrib/hedwig/scripts/hwServer.sh delete mode 100755 src/contrib/hedwig/scripts/quote delete mode 100644 src/contrib/hedwig/server/lib/README delete mode 100644 src/contrib/hedwig/server/pom.xml delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/AbstractBenchmark.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/BookieBenchmark.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/BookkeeperBenchmark.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/FakeBookie.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/ByteStringInterner.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/ServerConfiguration.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TerminateJVMExceptionHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TopicOpQueuer.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/UnexpectedError.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/ChannelEndPoint.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryCallback.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryEndPoint.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/FIFODeliveryManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/BaseHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ChannelDisconnectListener.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ConsumeHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/Handler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/PublishHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/SubscribeHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/UnsubscribeHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServer.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServerPipelineFactory.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/UmbrellaHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/BookkeeperPersistenceManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheKey.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheValue.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/Factory.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/LocalDBPersistenceManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/MapMethods.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistRequest.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManagerWithRangeScan.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/RangeScanRequest.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ReadAheadCache.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallback.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallbackWithContext.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanRequest.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ChannelTracker.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/HedwigProxy.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConfiguration.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConsumeHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyPublishHander.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStartDeliveryHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStopDeliveryHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxySubscribeHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyUnsubscribeHandler.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClient.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClientFactory.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubSubscriber.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/RegionManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/ssl/SslServerContextFactory.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AbstractSubscriptionManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AllToAllTopologyFilter.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionState.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/MessageFilter.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionEventListener.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/TrueFilter.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/ZkSubscriptionManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/AbstractTopicManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicOwnershipChangeListener.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TrivialOwnAllTopicManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/ZkTopicManager.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsynBKCallback.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncCallback.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncZKCallback.java delete mode 100644 src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/ZkUtils.java delete mode 100644 src/contrib/hedwig/server/src/main/resources/p12.pass delete mode 100644 src/contrib/hedwig/server/src/main/resources/server.p12 delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/HelperMethods.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControl.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControlDaemon.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubCallback.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubScanCallback.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/client/TestPubSubClient.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigHubTestBase.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigRegionTestBase.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/PubSubServerStandAloneTestBase.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/delivery/StubDeliveryManager.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestBaseHandler.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestSubUnsubHandler.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigHub.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigRegion.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/TestPubSubServer.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/WriteRecordingChannel.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/BookKeeperTestBase.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubPersistenceManager.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubScanCallback.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookKeeperPersistenceManagerBlackBox.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookkeeperPersistenceManagerWhiteBox.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestLocalDBPersistenceManagerBlackBox.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestPersistenceManagerBlackBox.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheBlackBox.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheWhiteBox.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/StubSubscriptionManager.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/TestZkSubscriptionManager.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/StubTopicManager.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/TestZkTopicManager.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/TestZkUtils.java delete mode 100644 src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/ZooKeeperTestBase.java delete mode 100644 src/contrib/huebrowser/README delete mode 100644 src/contrib/huebrowser/zkui/Makefile delete mode 100644 src/contrib/huebrowser/zkui/setup.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/__init__.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/forms.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/models.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/rest.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/settings.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/art/line_icons.png delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/art/zkui.png delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/bootstrap.js delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/css/zkui.css delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/help/index.html delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/static/js/package.yml delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/stats.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/clients.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/create.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/edit.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/index.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/shared_components.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/templates/view.mako delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/urls.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/utils.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/views.py delete mode 100644 src/contrib/huebrowser/zkui/src/zkui/windmilltests.py delete mode 100644 src/contrib/loggraph/README.txt delete mode 100755 src/contrib/loggraph/bin/loggraph-dev.sh delete mode 100755 src/contrib/loggraph/bin/loggraph.sh delete mode 100644 src/contrib/loggraph/build.xml delete mode 100644 src/contrib/loggraph/ivy.xml delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterException.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterParser.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/JsonGenerator.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JEntry.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogEntry.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogIterator.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogServer.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSkipList.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSource.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MeasureThroughput.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MergedLogSource.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TransactionEntry.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java delete mode 100644 src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.line.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.pie.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.raphael.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/load-big.gif delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/load.gif delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.css delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.log.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.server.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.session.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.stats.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.ui.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/main.html delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/raphael.js delete mode 100644 src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/yui-min.js delete mode 100644 src/contrib/monitoring/JMX-RESOURCES delete mode 100644 src/contrib/monitoring/README delete mode 100644 src/contrib/monitoring/cacti/README delete mode 100755 src/contrib/monitoring/check_zookeeper.py delete mode 100644 src/contrib/monitoring/ganglia/README delete mode 100644 src/contrib/monitoring/ganglia/Screenshot.png delete mode 100644 src/contrib/monitoring/ganglia/modpython.conf delete mode 100644 src/contrib/monitoring/ganglia/zookeeper.pyconf delete mode 100644 src/contrib/monitoring/ganglia/zookeeper_ganglia.py delete mode 100644 src/contrib/monitoring/nagios/README.txt delete mode 100644 src/contrib/monitoring/nagios/Screenshot-1.png delete mode 100644 src/contrib/monitoring/nagios/Screenshot.png delete mode 100644 src/contrib/monitoring/nagios/hostgroups.cfg delete mode 100644 src/contrib/monitoring/nagios/services.cfg delete mode 100644 src/contrib/monitoring/nagios/zookeeper.cfg delete mode 100755 src/contrib/monitoring/test.py delete mode 100644 src/contrib/rest/NOTICE.txt delete mode 100644 src/contrib/rest/README.txt delete mode 100644 src/contrib/rest/SPEC.txt delete mode 100644 src/contrib/rest/build.xml delete mode 100644 src/contrib/rest/conf/keys/README delete mode 100644 src/contrib/rest/conf/keys/rest.cer delete mode 100644 src/contrib/rest/conf/keys/rest.jks delete mode 100644 src/contrib/rest/conf/log4j.properties delete mode 100644 src/contrib/rest/conf/rest.properties delete mode 100644 src/contrib/rest/ivy.xml delete mode 100644 src/contrib/rest/rest.sh delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/RestMain.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java delete mode 100644 src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java delete mode 100644 src/contrib/rest/src/python/README.txt delete mode 100644 src/contrib/rest/src/python/demo_master_election.py delete mode 100644 src/contrib/rest/src/python/demo_queue.py delete mode 100644 src/contrib/rest/src/python/test.py delete mode 100755 src/contrib/rest/src/python/zk_dump_tree.py delete mode 100644 src/contrib/rest/src/python/zkrest.py delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/Base.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SessionTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SetTest.java delete mode 100644 src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/WadlTest.java delete mode 100755 src/contrib/rest/src/test/zkServer.sh delete mode 100644 src/contrib/zkfuse/Makefile.am delete mode 100644 src/contrib/zkfuse/README.txt delete mode 100644 src/contrib/zkfuse/build.xml delete mode 100644 src/contrib/zkfuse/configure.ac delete mode 100644 src/contrib/zkfuse/src/Makefile.am delete mode 100644 src/contrib/zkfuse/src/blockingqueue.h delete mode 100644 src/contrib/zkfuse/src/doxygen.cfg delete mode 100644 src/contrib/zkfuse/src/event.cc delete mode 100644 src/contrib/zkfuse/src/event.h delete mode 100644 src/contrib/zkfuse/src/log.cc delete mode 100644 src/contrib/zkfuse/src/log.h delete mode 100644 src/contrib/zkfuse/src/log4cxx.properties delete mode 100644 src/contrib/zkfuse/src/mutex.h delete mode 100644 src/contrib/zkfuse/src/thread.cc delete mode 100644 src/contrib/zkfuse/src/thread.h delete mode 100644 src/contrib/zkfuse/src/zkadapter.cc delete mode 100644 src/contrib/zkfuse/src/zkadapter.h delete mode 100644 src/contrib/zkfuse/src/zkfuse.cc delete mode 100644 src/contrib/zkperl/Changes delete mode 100644 src/contrib/zkperl/LICENSE delete mode 100644 src/contrib/zkperl/MANIFEST delete mode 100644 src/contrib/zkperl/Makefile.PL delete mode 100644 src/contrib/zkperl/NOTICE delete mode 100644 src/contrib/zkperl/README delete mode 100644 src/contrib/zkperl/ZooKeeper.pm delete mode 100644 src/contrib/zkperl/ZooKeeper.xs delete mode 100644 src/contrib/zkperl/build.xml delete mode 100644 src/contrib/zkperl/build/check_zk_version.c delete mode 100644 src/contrib/zkperl/build/check_zk_version.h delete mode 100644 src/contrib/zkperl/t/10_invalid.t delete mode 100644 src/contrib/zkperl/t/15_thread.t delete mode 100644 src/contrib/zkperl/t/20_tie.t delete mode 100644 src/contrib/zkperl/t/22_stat_tie.t delete mode 100644 src/contrib/zkperl/t/24_watch_tie.t delete mode 100644 src/contrib/zkperl/t/30_connect.t delete mode 100644 src/contrib/zkperl/t/35_log.t delete mode 100644 src/contrib/zkperl/t/40_basic.t delete mode 100644 src/contrib/zkperl/t/45_class.t delete mode 100644 src/contrib/zkperl/t/50_access.t delete mode 100644 src/contrib/zkperl/t/60_watch.t delete mode 100644 src/contrib/zkperl/t/util.pl delete mode 100644 src/contrib/zkperl/typemap delete mode 100644 src/contrib/zkpython/README delete mode 100644 src/contrib/zkpython/build.xml delete mode 100644 src/contrib/zkpython/src/c/pyzk_docstrings.h delete mode 100644 src/contrib/zkpython/src/c/zookeeper.c delete mode 100644 src/contrib/zkpython/src/examples/README delete mode 100644 src/contrib/zkpython/src/examples/watch_znode_for_changes.py delete mode 100755 src/contrib/zkpython/src/python/setup.py delete mode 100755 src/contrib/zkpython/src/python/zk.py delete mode 100644 src/contrib/zkpython/src/test/acl_test.py delete mode 100644 src/contrib/zkpython/src/test/async_test.py delete mode 100644 src/contrib/zkpython/src/test/callback_test.py delete mode 100755 src/contrib/zkpython/src/test/clientid_test.py delete mode 100644 src/contrib/zkpython/src/test/close_deadlock_test.py delete mode 100755 src/contrib/zkpython/src/test/connection_test.py delete mode 100755 src/contrib/zkpython/src/test/create_test.py delete mode 100755 src/contrib/zkpython/src/test/delete_test.py delete mode 100755 src/contrib/zkpython/src/test/exists_test.py delete mode 100755 src/contrib/zkpython/src/test/get_set_test.py delete mode 100755 src/contrib/zkpython/src/test/run_tests.sh delete mode 100755 src/contrib/zkpython/src/test/zkServer.sh delete mode 100755 src/contrib/zkpython/src/test/zktestbase.py delete mode 100644 src/contrib/zktreeutil/Makefile.am delete mode 100644 src/contrib/zktreeutil/README.txt delete mode 100644 src/contrib/zktreeutil/build.xml delete mode 100644 src/contrib/zktreeutil/configure.ac delete mode 100644 src/contrib/zktreeutil/src/Makefile.am delete mode 100644 src/contrib/zktreeutil/src/SimpleTree.h delete mode 100644 src/contrib/zktreeutil/src/ZkAdaptor.cc delete mode 100644 src/contrib/zktreeutil/src/ZkAdaptor.h delete mode 100644 src/contrib/zktreeutil/src/ZkTreeUtil.cc delete mode 100644 src/contrib/zktreeutil/src/ZkTreeUtil.h delete mode 100644 src/contrib/zktreeutil/src/ZkTreeUtilMain.cc delete mode 100644 src/contrib/zktreeutil/tests/zk_sample.xml delete mode 100644 src/contrib/zooinspector/NOTICE.txt delete mode 100644 src/contrib/zooinspector/README.txt delete mode 100644 src/contrib/zooinspector/build.xml delete mode 100644 src/contrib/zooinspector/config/defaultConnectionSettings.cfg delete mode 100644 src/contrib/zooinspector/config/defaultNodeVeiwers.cfg delete mode 100644 src/contrib/zooinspector/icons/edtsrclkup_co.gif delete mode 100644 src/contrib/zooinspector/icons/file_obj.gif delete mode 100644 src/contrib/zooinspector/icons/fldr_obj.gif delete mode 100644 src/contrib/zooinspector/icons/info_obj.gif delete mode 100644 src/contrib/zooinspector/icons/jspdecl.gif delete mode 100644 src/contrib/zooinspector/icons/launch_run.gif delete mode 100644 src/contrib/zooinspector/icons/launch_stop.gif delete mode 100644 src/contrib/zooinspector/icons/new_con.gif delete mode 100644 src/contrib/zooinspector/icons/refresh.gif delete mode 100644 src/contrib/zooinspector/icons/save_edit.gif delete mode 100644 src/contrib/zooinspector/icons/search_next.gif delete mode 100644 src/contrib/zooinspector/icons/search_prev.gif delete mode 100644 src/contrib/zooinspector/icons/trash.gif delete mode 100644 src/contrib/zooinspector/ivy.xml delete mode 100644 src/contrib/zooinspector/lib/jtoaster-1.0.4.jar delete mode 100644 src/contrib/zooinspector/lib/log4j.properties delete mode 100644 src/contrib/zooinspector/licences/Apache Software Licence v2.0.txt delete mode 100644 src/contrib/zooinspector/licences/epl-v10.html delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/ZooInspector.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorIconResources.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/about.html delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeTreeManager.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java delete mode 100644 src/contrib/zooinspector/src/java/org/apache/zookeeper/retry/ZooKeeperRetry.java delete mode 100755 src/contrib/zooinspector/zooInspector-dev.sh delete mode 100644 src/contrib/zooinspector/zooInspector.cmd delete mode 100755 src/contrib/zooinspector/zooInspector.sh delete mode 100644 src/docs/forrest.properties delete mode 100644 src/docs/src/documentation/README.txt delete mode 100644 src/docs/src/documentation/TODO.txt delete mode 100644 src/docs/src/documentation/classes/CatalogManager.properties delete mode 100644 src/docs/src/documentation/content/xdocs/bookkeeperConfig.xml delete mode 100644 src/docs/src/documentation/content/xdocs/bookkeeperOverview.xml delete mode 100644 src/docs/src/documentation/content/xdocs/bookkeeperProgrammer.xml delete mode 100644 src/docs/src/documentation/content/xdocs/bookkeeperStarted.xml delete mode 100644 src/docs/src/documentation/content/xdocs/bookkeeperStream.xml delete mode 100644 src/docs/src/documentation/content/xdocs/index.xml delete mode 100644 src/docs/src/documentation/content/xdocs/javaExample.xml delete mode 100644 src/docs/src/documentation/content/xdocs/recipes.xml delete mode 100644 src/docs/src/documentation/content/xdocs/releasenotes.xml delete mode 100644 src/docs/src/documentation/content/xdocs/site.xml delete mode 100644 src/docs/src/documentation/content/xdocs/tabs.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperHierarchicalQuorums.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperInternals.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperJMX.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperObservers.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperOtherInfo.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperOver.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperQuotas.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperStarted.xml delete mode 100644 src/docs/src/documentation/content/xdocs/zookeeperTutorial.xml delete mode 100755 src/docs/src/documentation/resources/images/2pc.jpg delete mode 100644 src/docs/src/documentation/resources/images/bk-overview.jpg delete mode 100644 src/docs/src/documentation/resources/images/favicon.ico delete mode 100644 src/docs/src/documentation/resources/images/hadoop-logo.jpg delete mode 100755 src/docs/src/documentation/resources/images/state_dia.dia delete mode 100755 src/docs/src/documentation/resources/images/state_dia.jpg delete mode 100644 src/docs/src/documentation/resources/images/zkarch.jpg delete mode 100644 src/docs/src/documentation/resources/images/zkcomponents.jpg delete mode 100644 src/docs/src/documentation/resources/images/zknamespace.jpg delete mode 100644 src/docs/src/documentation/resources/images/zkperfRW-3.2.jpg delete mode 100644 src/docs/src/documentation/resources/images/zkperfRW.jpg delete mode 100644 src/docs/src/documentation/resources/images/zkperfreliability.jpg delete mode 100644 src/docs/src/documentation/resources/images/zkservice.jpg delete mode 100644 src/docs/src/documentation/resources/images/zookeeper_small.gif delete mode 100644 src/docs/src/documentation/skinconf.xml delete mode 100644 src/docs/status.xml delete mode 100644 src/java/OldChangeLog delete mode 100644 src/java/lib/cobertura/README.txt delete mode 100644 src/java/lib/jdiff/zookeeper_3.1.1.xml delete mode 100644 src/java/lib/jline-0.9.94.LICENSE.txt delete mode 100644 src/java/lib/log4j-1.2.15.LICENSE.txt delete mode 100644 src/java/libtest/accessive.LICENSE.txt delete mode 100644 src/java/libtest/accessive.jar delete mode 100644 src/java/main/org/apache/jute/compiler/CSharpGenerator.java delete mode 100644 src/java/main/org/apache/zookeeper/ClientCnxnSocket.java delete mode 100644 src/java/main/org/apache/zookeeper/ClientCnxnSocketNIO.java delete mode 100644 src/java/main/org/apache/zookeeper/ClientCnxnSocketNetty.java delete mode 100644 src/java/main/org/apache/zookeeper/server/NIOServerCnxnFactory.java delete mode 100644 src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java delete mode 100644 src/java/main/org/apache/zookeeper/server/NettyServerCnxnFactory.java delete mode 100644 src/java/main/org/apache/zookeeper/server/ServerCnxn.java delete mode 100644 src/java/main/org/apache/zookeeper/server/ServerCnxnFactory.java delete mode 100644 src/java/main/org/apache/zookeeper/server/Stats.java delete mode 100644 src/java/systest/README.txt delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/BaseSysTest.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/DuplicateNameException.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/GenerateLoad.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/Instance.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/InstanceContainer.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/InstanceManager.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/NoAssignmentException.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/NoAvailableContainers.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/QuorumPeerInstance.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/SimpleClient.java delete mode 100644 src/java/systest/org/apache/zookeeper/test/system/SimpleSysTest.java delete mode 100644 src/java/test/checkstyle-noframes-sorted.xsl delete mode 100644 src/java/test/checkstyle.xml delete mode 100644 src/java/test/config/findbugsExcludeFile.xml delete mode 100644 src/java/test/data/invalidsnap/version-2/log.1 delete mode 100644 src/java/test/data/invalidsnap/version-2/log.274 delete mode 100644 src/java/test/data/invalidsnap/version-2/log.63b delete mode 100644 src/java/test/data/invalidsnap/version-2/snapshot.0 delete mode 100644 src/java/test/data/invalidsnap/version-2/snapshot.272 delete mode 100644 src/java/test/data/invalidsnap/version-2/snapshot.639 delete mode 100644 src/java/test/data/invalidsnap/version-2/snapshot.83f delete mode 100644 src/java/test/data/upgrade/log.100000001 delete mode 100644 src/java/test/data/upgrade/log.100001bf0 delete mode 100644 src/java/test/data/upgrade/snapshot.100000000 delete mode 100644 src/java/test/data/upgrade/snapshot.100001bec delete mode 100644 src/java/test/org/apache/zookeeper/JUnit4ZKTestRunner.java delete mode 100644 src/java/test/org/apache/zookeeper/PortAssignment.java delete mode 100644 src/java/test/org/apache/zookeeper/TestableZooKeeper.java delete mode 100644 src/java/test/org/apache/zookeeper/ThreadUtil.java delete mode 100644 src/java/test/org/apache/zookeeper/ZKTestCase.java delete mode 100644 src/java/test/org/apache/zookeeper/ZooKeeperTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/CRCTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/DataTreeUnitTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/DeserializationPerfTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/InvalidSnapshotTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/SerializationPerfTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/ToStringTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/ZooKeeperServerMainTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/ZooKeeperServerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java delete mode 100644 src/java/test/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ACLRootTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ACLTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/AsyncHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/AsyncOps.java delete mode 100644 src/java/test/org/apache/zookeeper/test/AsyncOpsTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/AsyncTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/AuthTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ChrootAsyncTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ChrootClientTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ChrootTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ClientBase.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ClientHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ClientPortBindTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ClientRetry.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ClientTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/CnxManagerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/CreateModeTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/DataTreeTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/DisconnectableZooKeeper.java delete mode 100644 src/java/test/org/apache/zookeeper/test/EventTypeTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FLELostMessageTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FLENewEpochTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FLERestartTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FLETest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FLEZeroWeightTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/FourLetterWordsTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/GetChildren2Test.java delete mode 100644 src/java/test/org/apache/zookeeper/test/HierarchicalQuorumTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/IntegrityCheck.java delete mode 100644 src/java/test/org/apache/zookeeper/test/InvalidSnapshotTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/JMXEnv.java delete mode 100644 src/java/test/org/apache/zookeeper/test/KeeperStateTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/LENonTerminateTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/LETest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/MaxCnxnsTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NettyNettySuiteBase.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NettyNettySuiteHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NettyNettySuiteTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NettyNioSuiteBase.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NettyNioSuiteHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NettyNioSuiteTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NioNettySuiteBase.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NioNettySuiteHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NioNettySuiteTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/NullDataTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/OOMTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ObserverHierarchicalQuorumTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ObserverQuorumHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ObserverTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/PurgeTxnTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/QuorumBase.java delete mode 100644 src/java/test/org/apache/zookeeper/test/QuorumHammerTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/QuorumQuotaTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/QuorumTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/QuorumUtil.java delete mode 100644 src/java/test/org/apache/zookeeper/test/QuorumZxidSyncTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/RecoveryTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/RepeatStartupTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/SessionTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/SledgeHammer.java delete mode 100644 src/java/test/org/apache/zookeeper/test/StandaloneTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/StatTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/SyncCallTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/TestHammer.java delete mode 100644 src/java/test/org/apache/zookeeper/test/TruncateTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/UpgradeTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/WatchedEventTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/WatcherFuncTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/WatcherTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ZooKeeperQuotaTest.java delete mode 100644 src/java/test/org/apache/zookeeper/test/ZooKeeperTestClient.java delete mode 100644 src/lastRevision.bat delete mode 100755 src/lastRevision.sh rename src/{java/main => main/java}/org/apache/jute/BinaryInputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/BinaryOutputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/CsvInputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/CsvOutputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/Index.java (100%) rename src/{java/main => main/java}/org/apache/jute/InputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/OutputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/Record.java (100%) rename src/{java/main => main/java}/org/apache/jute/RecordReader.java (100%) rename src/{java/main => main/java}/org/apache/jute/RecordWriter.java (100%) rename src/{java/main => main/java}/org/apache/jute/Utils.java (100%) rename src/{java/main => main/java}/org/apache/jute/XmlInputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/XmlOutputArchive.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/CGenerator.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/CppGenerator.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/JBoolean.java (77%) rename src/{java/main => main/java}/org/apache/jute/compiler/JBuffer.java (97%) rename src/{java/main => main/java}/org/apache/jute/compiler/JByte.java (92%) rename src/{java/main => main/java}/org/apache/jute/compiler/JCompType.java (72%) rename src/{java/main => main/java}/org/apache/jute/compiler/JDouble.java (92%) rename src/{java/main => main/java}/org/apache/jute/compiler/JField.java (69%) rename src/{java/main => main/java}/org/apache/jute/compiler/JFile.java (93%) rename src/{java/main => main/java}/org/apache/jute/compiler/JFloat.java (92%) rename src/{java/main => main/java}/org/apache/jute/compiler/JInt.java (92%) rename src/{java/main => main/java}/org/apache/jute/compiler/JLong.java (93%) rename src/{java/main => main/java}/org/apache/jute/compiler/JMap.java (61%) rename src/{java/main => main/java}/org/apache/jute/compiler/JRecord.java (70%) rename src/{java/main => main/java}/org/apache/jute/compiler/JString.java (93%) rename src/{java/main => main/java}/org/apache/jute/compiler/JType.java (64%) rename src/{java/main => main/java}/org/apache/jute/compiler/JVector.java (60%) rename src/{java/main => main/java}/org/apache/jute/compiler/JavaGenerator.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/ParseException.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/Rcc.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/RccConstants.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/RccTokenManager.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/SimpleCharStream.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/Token.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/TokenMgrError.java (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/package.html (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/generated/rcc.jj (100%) rename src/{java/main => main/java}/org/apache/jute/compiler/package.html (100%) rename src/{java/main => main/java}/org/apache/jute/package.html (100%) rename src/{java/main => main/java}/org/apache/zookeeper/AsyncCallback.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/ClientCnxn.java (57%) rename src/{java/main => main/java}/org/apache/zookeeper/ClientWatchManager.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/CreateMode.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/Environment.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/JLineZNodeCompletor.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/KeeperException.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/Quotas.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/ServerAdminClient.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/StatsTrack.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/Version.java (95%) rename src/{java/main => main/java}/org/apache/zookeeper/WatchedEvent.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/Watcher.java (97%) rename src/{java/main => main/java}/org/apache/zookeeper/ZooDefs.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/ZooKeeper.java (92%) rename src/{java/main => main/java}/org/apache/zookeeper/ZooKeeperMain.java (99%) create mode 100644 src/main/java/org/apache/zookeeper/client/FourLetterWordMain.java rename src/{java/main => main/java}/org/apache/zookeeper/common/PathTrie.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/common/PathUtils.java (100%) create mode 100644 src/main/java/org/apache/zookeeper/data/ACL.java create mode 100644 src/main/java/org/apache/zookeeper/data/Id.java create mode 100644 src/main/java/org/apache/zookeeper/data/Stat.java create mode 100644 src/main/java/org/apache/zookeeper/data/StatPersisted.java create mode 100644 src/main/java/org/apache/zookeeper/data/StatPersistedV1.java rename src/{java/main => main/java}/org/apache/zookeeper/jmx/CommonNames.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/jmx/MBeanRegistry.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/jmx/ManagedUtil.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/jmx/ZKMBeanInfo.java (100%) create mode 100644 src/main/java/org/apache/zookeeper/proto/AuthPacket.java create mode 100644 src/main/java/org/apache/zookeeper/proto/ConnectRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/ConnectResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/CreateRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/CreateResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/DeleteRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/ExistsRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/ExistsResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetACLRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetACLResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetChildren2Request.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetChildren2Response.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetChildrenRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetChildrenResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetDataRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetDataResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetMaxChildrenRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/GetMaxChildrenResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/ReplyHeader.java create mode 100644 src/main/java/org/apache/zookeeper/proto/RequestHeader.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SetACLRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SetACLResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SetDataRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SetDataResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SetMaxChildrenRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SetWatches.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SyncRequest.java create mode 100644 src/main/java/org/apache/zookeeper/proto/SyncResponse.java create mode 100644 src/main/java/org/apache/zookeeper/proto/WatcherEvent.java create mode 100644 src/main/java/org/apache/zookeeper/proto/op_result_t.java rename src/{java/main => main/java}/org/apache/zookeeper/server/ByteBufferInputStream.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ConnectionBean.java (92%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ConnectionMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/DataNode.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/DataTree.java (92%) rename src/{java/main => main/java}/org/apache/zookeeper/server/DataTreeBean.java (84%) rename src/{java/main => main/java}/org/apache/zookeeper/server/DataTreeMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/FinalRequestProcessor.java (92%) rename src/{java/main => main/java}/org/apache/zookeeper/server/LogFormatter.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/NIOServerCnxn.java (50%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ObserverBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/PrepRequestProcessor.java (95%) rename src/{java/main => main/java}/org/apache/zookeeper/server/PurgeTxnLog.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/Request.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/RequestProcessor.java (83%) create mode 100644 src/main/java/org/apache/zookeeper/server/ServerCnxn.java rename src/{java/main => main/java}/org/apache/zookeeper/server/ServerConfig.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ServerStats.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/SessionTracker.java (93%) rename src/{java/main => main/java}/org/apache/zookeeper/server/SessionTrackerImpl.java (94%) rename src/{java/main => main/java}/org/apache/zookeeper/server/SyncRequestProcessor.java (98%) rename src/{java/main => main/java}/org/apache/zookeeper/server/TraceFormatter.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/WatchManager.java (97%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ZKDatabase.java (91%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ZooKeeperServer.java (71%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ZooKeeperServerBean.java (95%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ZooKeeperServerMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ZooKeeperServerMain.java (96%) rename src/{java/main => main/java}/org/apache/zookeeper/server/ZooTrace.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/auth/AuthenticationProvider.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/auth/DigestAuthenticationProvider.java (97%) rename src/{java/main => main/java}/org/apache/zookeeper/server/auth/IPAuthenticationProvider.java (96%) rename src/{java/main => main/java}/org/apache/zookeeper/server/auth/ProviderRegistry.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/package.html (100%) create mode 100644 src/main/java/org/apache/zookeeper/server/persistence/FileHeader.java rename src/{java/main => main/java}/org/apache/zookeeper/server/persistence/FileSnap.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/persistence/FileTxnLog.java (90%) rename src/{java/main => main/java}/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java (78%) rename src/{java/main => main/java}/org/apache/zookeeper/server/persistence/SnapShot.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/persistence/TxnLog.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/persistence/Util.java (98%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/AckRequestProcessor.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/AuthFastLeaderElection.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/CommitProcessor.java (98%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/Election.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/FastLeaderElection.java (99%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/Follower.java (95%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/FollowerBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/FollowerMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java (99%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java (99%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/Leader.java (92%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LeaderBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LeaderElection.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LeaderElectionBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LeaderElectionMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LeaderMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java (97%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/Learner.java (80%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LearnerHandler.java (78%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java (97%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LearnerSyncRequest.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java (96%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LocalPeerBean.java (88%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/LocalPeerMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/Observer.java (98%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/ObserverMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java (99%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/ProposalRequestProcessor.java (88%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumCnxManager.java (91%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumMXBean.java (100%) create mode 100644 src/main/java/org/apache/zookeeper/server/quorum/QuorumPacket.java rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumPeer.java (96%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java (96%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumPeerMain.java (95%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumStats.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/QuorumZooKeeperServer.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/RemotePeerBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/RemotePeerMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/SendAckRequestProcessor.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/ServerBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/ServerMXBean.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/Vote.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/flexible/QuorumHierarchical.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/flexible/QuorumMaj.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/quorum/flexible/QuorumVerifier.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/upgrade/DataNodeV1.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/upgrade/DataTreeV1.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/upgrade/UpgradeMain.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/upgrade/UpgradeSnapShot.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/upgrade/UpgradeSnapShotV1.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/util/Profiler.java (100%) rename src/{java/main => main/java}/org/apache/zookeeper/server/util/SerializeUtils.java (100%) create mode 100644 src/main/java/org/apache/zookeeper/txn/CreateSessionTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/CreateTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/DeleteTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/ErrorTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/SetACLTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/SetDataTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/SetMaxChildrenTxn.java create mode 100644 src/main/java/org/apache/zookeeper/txn/TxnHeader.java rename src/{contrib/bookkeeper/src/java/org/apache/bookkeeper/client/CRC32DigestManager.java => main/java/org/apache/zookeeper/version/Info.java} (54%) rename src/{java/main => main/java}/org/apache/zookeeper/version/util/VerGen.java (72%) delete mode 100644 src/recipes/README.txt delete mode 100644 src/recipes/build-recipes.xml delete mode 100644 src/recipes/build.xml delete mode 100644 src/recipes/lock/README.txt delete mode 100644 src/recipes/lock/build.xml delete mode 100644 src/recipes/lock/src/c/INSTALL delete mode 100644 src/recipes/lock/src/c/LICENSE delete mode 100644 src/recipes/lock/src/c/Makefile.am delete mode 100644 src/recipes/lock/src/c/README.txt delete mode 100644 src/recipes/lock/src/c/acinclude.m4 delete mode 100644 src/recipes/lock/src/c/aminclude.am delete mode 100644 src/recipes/lock/src/c/c-doc.Doxyfile delete mode 100644 src/recipes/lock/src/c/configure.ac delete mode 100644 src/recipes/lock/src/c/include/zoo_lock.h delete mode 100644 src/recipes/lock/src/c/src/zoo_lock.c delete mode 100644 src/recipes/lock/src/c/tests/TestClient.cc delete mode 100644 src/recipes/lock/src/c/tests/TestDriver.cc delete mode 100644 src/recipes/lock/src/c/tests/Util.cc delete mode 100644 src/recipes/lock/src/c/tests/Util.h delete mode 100755 src/recipes/lock/src/c/tests/zkServer.sh delete mode 100644 src/recipes/lock/src/java/org/apache/zookeeper/recipes/lock/LockListener.java delete mode 100644 src/recipes/lock/src/java/org/apache/zookeeper/recipes/lock/ProtocolSupport.java delete mode 100644 src/recipes/lock/src/java/org/apache/zookeeper/recipes/lock/WriteLock.java delete mode 100644 src/recipes/lock/src/java/org/apache/zookeeper/recipes/lock/ZNodeName.java delete mode 100644 src/recipes/lock/src/java/org/apache/zookeeper/recipes/lock/ZooKeeperOperation.java delete mode 100644 src/recipes/lock/test/org/apache/zookeeper/recipes/lock/WriteLockTest.java delete mode 100644 src/recipes/lock/test/org/apache/zookeeper/recipes/lock/ZNodeNameTest.java delete mode 100644 src/recipes/queue/README.txt delete mode 100644 src/recipes/queue/build.xml delete mode 100644 src/recipes/queue/src/c/INSTALL delete mode 100644 src/recipes/queue/src/c/LICENSE delete mode 100644 src/recipes/queue/src/c/Makefile.am delete mode 100644 src/recipes/queue/src/c/README.txt delete mode 100644 src/recipes/queue/src/c/acinclude.m4 delete mode 100644 src/recipes/queue/src/c/aminclude.am delete mode 100644 src/recipes/queue/src/c/c-doc.Doxyfile delete mode 100644 src/recipes/queue/src/c/configure.ac delete mode 100644 src/recipes/queue/src/c/include/zoo_queue.h delete mode 100644 src/recipes/queue/src/c/src/zoo_queue.c delete mode 100644 src/recipes/queue/src/c/tests/TestClient.cc delete mode 100644 src/recipes/queue/src/c/tests/TestDriver.cc delete mode 100644 src/recipes/queue/src/c/tests/Util.cc delete mode 100644 src/recipes/queue/src/c/tests/Util.h delete mode 100755 src/recipes/queue/src/c/tests/zkServer.sh delete mode 100644 src/recipes/queue/src/java/org/apache/zookeeper/recipes/queue/DistributedQueue.java delete mode 100644 src/recipes/queue/test/org/apache/zookeeper/recipes/queue/DistributedQueueTest.java delete mode 100644 src/zookeeper.jute diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index 814ea8f1974..00000000000 --- a/CHANGES.txt +++ /dev/null @@ -1,1369 +0,0 @@ -Trunk - -Non-backward compatible changes: - -BUGFIXES: - -Backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-735. cppunit test testipv6 assumes that the machine is ipv6 - enabled. (mahadev) - - ZOOKEEPER-720. Use zookeeper-{version}-sources.jar instead of - zookeeper-{version}-src.jar to publish sources in the Maven repository - (paolo via phunt) - - ZOOKEEPER-722. zkServer.sh uses sh's builtin echo on BSD, behaves - incorrectly. (Ivan Kelly via phunt) - - ZOOKEEPER-741. root level create on REST proxy fails (phunt) - - ZOOKEEPER-631. zkpython's C code could do with a style clean-up - (henry robinson via phunt) - - ZOOKEEPER-746. learner outputs session id to log in dec (phunt via - henryr) - - ZOOKEEPER-738. zookeeper.jute.h fails to compile with -pedantic - (Jozef Hatala via phunt) - - ZOOKEEPER-734. QuorumPeerTestBase.java and ZooKeeperServerMainTest.java - do not handle windows path correctly (Vishal K via phunt) - - ZOOKEEPER-754. numerous misspellings "succesfully" - (Andrei Savu via phunt) - - ZOOKEEPER-749. OSGi metadata not included in binary only jar (phunt - via henryr) - - ZOOKEEPER-750. move maven artifacts into "dist-maven" subdir of the - release (package target) (phunt via henryr) - - ZOOKEEPER-758. zkpython segfaults on invalid acl with missing key - (Kapil Thangavelu via henryr) - - ZOOKEEPER-737. some 4 letter words may fail with netcat (nc). (mahadev) - - ZOOKEEPER-764. Observer elected leader due to inconsistent voting view - (henry via mahadev) - - ZOOKEEPER-763. Deadlock on close w/ zkpython / c client - (henry via phunt) - - ZOOKEEPER-774. Recipes tests are slightly outdated: they do not compile - against JUnit 4.8 (Sergey Doroshenko via phunt) - - ZOOKEEPER-772. zkpython segfaults when watcher from async get children is - invoked. (henry via phunt) - - ZOOKEEPER-636. configure.ac has instructions which override the contents of - CFLAGS and CXXFLAGS. (Maxim P. Dementiev via phunt) - - ZOOKEEPER-796. zkServer.sh should support an external PIDFILE variable - (Alex Newman via phunt) - - ZOOKEEPER-719. Add throttling to BookKeeper client (fpj via breed) - - ZOOKEEPER-814. monitoring scripts are missing apache license headers - (andrei savu via mahadev) - - ZOOKEEPER-783. committedLog in ZKDatabase is not properly synchronized - (henry via mahadev) - - ZOOKEEPER-790. Last processed zxid set prematurely while establishing - leadership (flavio via mahadev) - - ZOOKEEPER-795. eventThread isn't shutdown after a connection - "session expired" event coming (Sergey Doroshenko and Ben via mahadev) - - ZOOKEEPER-792. zkpython memory leak (Lei Zhang via henryr) - - ZOOKEEPER-854. BookKeeper does not compile due to changes in the ZooKeeper - code (Flavio via mahadev) - - ZOOKEEPER-861. Missing the test SSL certificate used for running junit tests. - (erwin tam via mahadev) - - ZOOKEEPER-867. ClientTest is failing on hudson - fd cleanup (phunt) - - ZOOKEEPER-785. Zookeeper 3.3.1 shouldn't infinite loop if someone creates a - server.0 line (phunt and Andrei Savu via breed) - - ZOOKEEPER-785. Zookeeper 3.3.1 shouldn't infinite loop if someone creates a - server.0 line (part 2) (phunt) - - ZOOKEEPER-870. Zookeeper trunk build broken. (mahadev via phunt) - - ZOOKEEPER-831. BookKeeper: Throttling improved for reads (breed via fpj) - - ZOOKEEPER-846. zookeeper client doesn't shut down cleanly on the close call - (phunt) - - ZOOKEEPER-804. c unit tests failing due to "assertion cptr failed" (michi - mutsuzaki via mahadev) - - ZOOKEEPER-844. handle auth failure in java client - (Camille Fournier via phunt) - - ZOOKEEPER-822. Leader election taking a long time to complete - (Vishal K via phunt) - - ZOOKEEPER-866. Hedwig Server stays in "disconnected" state when connection to ZK dies but gets reconnected (erwin tam via breed) - -IMPROVEMENTS: - ZOOKEEPER-724. Improve junit test integration - log harness information - (phunt via mahadev) - - ZOOKEEPER-766. forrest recipes docs don't mention the lock/queue recipe - implementations available in the release (phunt via mahadev) - - ZOOKEEPER-769: Leader can treat observers as quorum members - (Sergey Doroshenko via henryr) - - ZOOKEEPER-788: Add server id to message logs - (Ivan Kelly via flavio) - - ZOOKEEPER-789. Improve FLE log messages (flavio via phunt) - - ZOOKEEPER-798. Fixup loggraph for FLE changes (Ivan Kelly via phunt) - - ZOOKEEPER-797 c client source with AI_ADDRCONFIG cannot be compiled with - early glibc (Qian Ye via phunt) - - ZOOKEEPER-790. Last processed zxid set prematurely while establishing leadership (fpj via breed) - - ZOOKEEPER-821. Add ZooKeeper version information to zkpython (Rich - Schumacher via mahadev) - - ZOOKEEPER-765. Add python example script (Travis and Andrei via mahadev) - - ZOOKEEPER-809. Improved REST Interface (Andrei Savu via phunt) - - ZOOKEEPER-733. use netty to handle client connections (breed and phunt) - - ZOOKEEPER-853. Make zookeeper.is_unrecoverable return True or False - in zkpython (Andrei Savu via henryr) - - ZOOKEEPER-864. Hedwig C++ client improvements (Ivan Kelly via breed) - -NEW FEATURES: - ZOOKEEPER-729. Java client API to recursively delete a subtree. - (Kay Kay via henry) - - ZOOKEEPER-747. Add C# generation to Jute (Eric Hauser via phunt) - - ZOOKEEPER-464. Need procedure to garbage collect ledgers - (erwin via fpj) - - ZOOKEEPER-773. Log visualisation (Ivan Kelly via phunt) - - ZOOKEEPER-744. Add monitoring four-letter word (Andrei Savu via phunt) - - ZOOKEEPER-712. Bookie recovery. (erwin tam via breed) - - ZOOKEEPER-799. Add tools and recipes for monitoring as a contrib - (Andrei Savu via phunt) - - ZOOKEEPER-808. Web-based Administrative Interface - (Andrei Savu via phunt) - - ZOOKEEPER-775. A large scale pub/sub system (Erwin, Ivan and Ben via - mahadev) - -Release 3.3.0 - 2010-03-24 - -Non-backward compatible changes: - -BUGFIXES: - -Backward compatible changes: - -BUGFIXES: - ZOOKEEPER-59. Synchronized block in NIOServerCnxn (fpj via breed) - - ZOOKEEPER-524. DBSizeTest is not really testing anything (breed) - - ZOOKEEPER-469. make sure CPPUNIT_CFLAGS isn't overwritten - (chris via mahadev) - - ZOOKEEPER-471. update zkperl for 3.2.x branch (chris via mahadev) - - ZOOKEEPER-470. include unistd.h for sleep() in c tests (chris via mahadev) - - ZOOKEEPR-460. bad testRetry in cppunit tests (hudson failure) - (giri via mahadev) - - ZOOKEEPER-467. Change log level in BookieHandle. (flavio via mahadev) - - ZOOKEEPER-482. ignore sigpipe in testRetry to avoid silent immediate - failure. (chris via mahadev) - - ZOOKEEPER-487. setdata on root (/) crashes the servers (mahadev via phunt) - - ZOOKEEPER-457. Make ZookeeperMain public, support for HBase (and other) - embedded clients (ryan rawson via phunt) - - ZOOKEEPER-481. Add lastMessageSent to QuorumCnxManager. (flavio via mahadev) - - ZOOKEEPER-479. QuorumHierarchical does not count groups correctly - (flavio via mahadev) - - ZOOKEEPER-466. crash on zookeeper_close() when using auth with empty cert - (Chris Darroch via phunt) - - ZOOKEEPER-480. FLE should perform leader check when node is not leading and - add vote of follower (flavio via mahadev) - - ZOOKEEPER-491. Prevent zero-weight servers from being elected. - (flavio via mahadev) - - ZOOKEEPER-447. zkServer.sh doesn't allow different config files to be - specified on the command line (henry robinson via phunt) - - ZOOKEEPER-493. patch for command line setquota (steve bendiola via phunt) - - ZOOKEEPER-311. handle small path lengths in zoo_create() - (chris barroch via breed) - - ZOOKEEPER-484. Clients get SESSION MOVED exception when switching from - follower to a leader. (mahadev) - - ZOOKEEPER-490. the java docs for session creation are misleading/incomplete - (phunt) - - ZOOKEEPER-501. CnxManagerTest failed on hudson. (flavio via mahadev) - - ZOOKEEPER-499. electionAlg should default to FLE (3) - regression - (phunt via mahadev) - - ZOOKEEPER-477. zkCleanup.sh is flaky (fernando via mahadev) - - ZOOKEEPER-498. Unending Leader Elections : WAN configuration - (flavio via mahadev) - - ZOOKEEPER-508. proposals and commits for DIFF and Truncate messages from the - leader to the followers is buggy. (mahadev and ben via mahadev) - - ZOOKEEPER-518. DEBUG message for outstanding proposals in leader should be - moved to trace. (phunt) - - ZOOKEEPER-533. ant error running clean twice (phunt via mahadev) - - ZOOKEEPER-535. ivy task does not enjoy being defined twice - (build error) (phunt via mahadev) - - ZOOKEEPER-420. build/test should not require install in zkpython - (henry robinson via phunt) - - ZOOKEEPER-538. zookeeper.async causes python to segfault - (henry robinson via phunt) - - ZOOKEEPER-542. c-client can spin when server unresponsive (Christian - Wiedmann via mahadev) - - ZOOKEEEPER-510. zkpython lumps all exceptions as IOError, needs specialized - exceptions for KeeperException types (henry & pat via mahadev) - - ZOOKEEPER-541. zkpython limited to 256 handles (henry robinson via phunt) - - ZOOKEEPER-554. zkpython can segfault when statting a deleted node - (henry robinson via phunt) - - ZOOKEEPER-512. FLE election fails to elect leader (flavio via mahadev) - - ZOOKEEPER-563. ant test for recipes is broken. (mahadev via phunt) - - ZOOKEEPER-562. c client can flood server with pings if tcp send queue - filled. (ben reed via mahadev) - - ZOOKEEPER-537. The zookeeper jar includes the java source files - (Thomas Dudziak via phunt) - - ZOOKEEPER-551. unnecessary SetWatches message on new session. - (phunt via flavio) - - ZOOKEEPER-566. "reqs" four letter word (command port) returns no information - (phunt via breed) - - ZOOKEEPER-567. javadoc for getchildren2 needs to mention "new in 3.3.0" - (phunt via breed) - - ZOOKEEPER-547. Sanity check in QuorumCnxn Manager and quorum communication - port. (mahadev via breed) - - ZOOKEEPER-532. java compiler should be target Java 1.5 - (hiram chirino and phunt via breed) - - ZOOKEEPER-519. Followerhandler should close the socket if it gets an exception - on a write. (mahadev via breed) - - ZOOKEEPER-570. AsyncHammerTest is broken, callbacks need to validate rc - parameter (phunt via breed) - - ZOOKEEPER-3. syncLimit has slightly different comments in the class header, - and > inline with the variable. (mahadev via breed) - - ZOOKEEPER-576. docs need to be updated for session moved exception and how - to handle it (breed via phunt) - - ZOOKEEPER-582. ZooKeeper can revert to old data when a snapshot is created - outside of normal processing (ben reed and mahadev via mahadev) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (Patrick Hunt via mahadev) - - ZOOKEEPER-598. LearnerHandler is misspelt in the thread's constructor - (Henry Robinson via fpj) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk (take 2) - (breed) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (take 3) (phunt via mahadev) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (take 4) (breed via mahadev) - - ZOOKEEPER-597. ASyncHammerTest is failing intermittently on hudson trunk - (take 5) (mahadev) - - ZOOKEEPER-611. hudson build failiure (mahadev) - - ZOOKEEPER-611. hudson build failure (take 2) (mahadev) - - ZOOKEEPER-615. wrong javadoc for create with a sequence flag - (mahadev via breed) - - ZOOKEEPER-588. remove unnecessary/annoying log of tostring error in - Request.toString() (phunt via breed) - - ZOOKEEPER-587. client should log timeout negotiated with server - (phunt via mahadev) - - ZOOKEEPER-610. cleanup final fields, esp those used for locking - (phunt via henry) - - ZOOKEEPER-614. Improper synchronisation in getClientCnxnCount - (henry via mahadev) - - ZOOKEEPER-609. ObserverTest failure "zk should not be connected expected not - same" (henry robinson via phunt) - - ZOOKEEPER-630. Trunk has duplicate ObserverTest.java files - (henry robinson via phunt) - - ZOOKEEPER-627. zkpython arbitrarily restricts the size of a 'get' to 512 - bytes (henry robinson via mahadev) - - ZOOKEEPER-534. The test target in contib/bookkeeper does not depend on jar - target. (phunt via mahadev) - - ZOOKEEPER-623. ClientBase in bookkeeper.util requires junit (fpj via breed) - - ZOOKEEPER-600. TODO pondering about allocation behavior in zkpython may be - removed (gustavo via mahadev) - - ZOOKEEPER-596. The last logged zxid calculated by zookeeper servers could - cause problems in leader election if data gets corrupted. (mahadev) - - ZOOKEEPER-637. Trunk build is failing (fpj via breed) - - ZOOKEEPER-637. Trunk build is failing - second patch (breed via fpj) - - ZOOKEEPER-644. Nightly build failed on hudson. (pat via mahadev) - - ZOOKEEPER-651: Log exception trace in QuorumCnxManager.SendWorker - (flavio via henry) - - ZOOKEEPER-608. Receipt of ACK from observer should not be logged as ERROR - (henry via mahadev) - - ZOOKEEPER-647. hudson failure in testLeaderShutdown (flavio via mahadev) - - ZOOKEEPER-574. the documentation on snapcount in the admin guide has the - wrong default (phunt via mahadev) - - ZOOKEEPER-656. SledgeHammer test - thread.run() deprecated (kay kay via mahadev) - - ZOOKEEPER-413. two flaws need addressing in the c tests that can cause false - positive failures (phunt via mahadev) - - ZOOKEEPER-495. c client logs an invalid error when zookeeper_init is called - with chroot (phunt via mahadev) - - ZOOKEEPER-589. When create a znode, a NULL ACL parameter cannot be accepted. - (breed via mahadev) - - ZOOKEEPER-673. Fix observer documentation regarding leader election (flavio - via mahadev) - - ZOOKEEPER-672. typo nits across documentation (Kay Kay via mahadev) - - ZOOKEEPER-668. Close method in LedgerInputStream doesn't do anything (flavio - via mahadev) - - ZOOKEEPER-569. Failure of elected leader can lead to never-ending leader - election (henry via flavio) - - ZOOKEEPER-669. watchedevent tostring should clearly output the - state/type/path (phunt via mahadev) - - ZOOKEEPER-683. LogFormatter fails to parse transactional log files (phunt - via mahadev) - - ZOOKEEPER-682. Event is not processed when the watcher is set to watch "/" - if chrooted (Scott Wang via mahadev) - - ZOOKEEPER-687. LENonterminatetest fails on some machines. (mahadev) - - ZOOKEEPER-681. Minor doc issue re unset maxClientCnxns (phunt via mahadev) - - ZOOKEEPER-622. Test for pending watches in send_set_watches should be moved - (ben and steven via mahadev) - - ZOOKEEPER-689. release build broken - ivysettings.xml not copied during - "package" (phunt via mahadev) - - ZOOKEEPER-59. Synchronized block in NIOServerCnxn (flavio via mahadev) - - ZOOKEEPER-691. Interface changed for NIOServer.Factory (breed via mahadev) - - ZOOKEEPER-685. Race in LENonTerminateTest (henry via breed) - - ZOOKEEPER-677. c client doesn't allow ipv6 numeric connect string - (breed & phunt & mahadev via breed) - - ZOOKEEPER-693. TestObserver stuck in tight notification loop in FLE - (flavio via phunt) - - ZOOKEEPER-696. NPE in the hudson logs, seems nioservercnxn closed twice - (phunt via mahadev) - - ZOOKEEPER-511. bad error handling in FollowerHandler.sendPackets - (mahadev via flavio) - - ZOOKEEPER-604. zk needs to prevent export of any symbol not listed in their - api (mahadev) - - ZOOKEEPER-121. SyncRequestProcessor is not closing log stream during - shutdown (mahadev) - - ZOOKEEPER-698. intermittent JMX test failures due to not verifying QuorumPeer - shutdown (phunt) - - ZOOKEEPER-121_2. SyncRequestProcessor is not closing log stream during - shutdown (breed via mahadev) - - ZOOKEEPER-121_3. SyncRequestProcessor is not closing log stream during - shutdown (mahadev via phunt) - - ZOOKEEPER-121_4. SyncRequestProcessor is not closing log stream during - shutdown (mahadev via breed) - - ZOOKEEPER-586. c client does not compile under cygwin (phunt, mahadev, breed via breed) - - ZOOKEEPER-624. The C Client cause core dump when receive error data from - Zookeeper Server (mahadev) - - ZOOKEEPER-591. The C Client cannot exit properly in some situation (mahadev) - - ZOOKEEPER-591_2. The C Client cannot exit properly in some situation - (mahadev via phunt) - - ZOOKEEPER-709. bookkeeper build failing with missing factory - (phunt) - - ZOOKEEPER-708. zkpython failing due to undefined symbol - deallocate_String_vector (mahadev via phunt) - - ZOOKEEPER-436. Bookies should auto register to ZooKeeper (erwin tam & fpj via breed) - - ZOOKEEPER-710. permanent ZSESSIONMOVED error after client app reconnects to zookeeper cluster (phunt via breed) - - ZOOKEEPER-718. the fatjar is missing libraries (ben via mahadev) - - ZOOKEEPER-717. add a preferred list to the instancemanager (breed via - mahadev) - -IMPROVEMENTS: - ZOOKEEPER-473. cleanup junit tests to eliminate false positives due to - "socket reuse" and failure to close client (phunt via mahadev) - - ZOOKEEPER-488. Fix zkServer.sh to add clover.jar in classpath - (Giridharan Kesavan via gkesavan) - - ZOOKEEPER-516. add support for 10 minute test ie "pre-commit" test (phunt) - - ZOOKEEPER-529. Use Ivy to pull dependencies and also generate pom (phunt - via mahadev) - - ZOOKEEPER-530. Memory corruption: Zookeeper c client IPv6 implementation - does not honor struct sockaddr_in6 size (isabel drost via mahadev) - - ZOOKEEPER-549. Refactor Followers and related classes into a Peer->Follower - hierarchy in preparation for Observers (henry robinson via mahadev) - - ZOOKEEPER-472. Making DataNode not instantiate a HashMap when the node is - ephmeral (Erik Holstad via mahadev) - - ZOOKEEPER-425. Add OSGi metadata to zookeeper.jar (david bosschaert via breed) - - ZOOKEEPER-599. Changes to FLE and QuorumCnxManager to support Observers - (fpj via breed) - - ZOOKEEPER-506. QuorumBase should use default leader election (fpj via breed) - - ZOOKEEPER-633. Fetch netty using ivy for bookkeeper (giri via fpj) - - ZOOKEEPER-544. improve client testability - allow test client to access - connected server location (phunt via breed) - - ZOOKEEPER-426. Windows versions of zookeeper scripts - (David Bosschaert via breed) - - ZOOKEEPER-638. upgrade ivy to 2.1.0 final from 2.1.0 release - candidate (phunt via breed) - - ZOOKEEPER-648. Fix releaseaudit warning count to zero (phunt via henry) - - ZOOKEEPER-626. ensure the c/java cli's print xid/sessionid/etc... in hex - (pat via mahadev) - - ZOOKEEPER-655. StringBuffer -> StringBuilder - conversion of references as - necessary (Kay Kay via henry) - - ZOOKEEPER-612. Make Zookeeper C client can be compiled by gcc of early - version (qian via mahadev) - - ZOOKEEPER-456. CREATOR_ALL_ACL has unnecessary PERMS.ADMIN in the - declartion. (phunt via mahadev) - - ZOOKEEPER-593. java client api does not allow client to access negotiated - session timeout (phunt via mahadev) - - ZOOKEEPER-507. BookKeeper client re-write (Utkarsh and ben via mahadev) - - ZOOKEEPER-665. Add BookKeeper streaming documentation (flavio via mahadev) - - ZOOKEEPER-664. BookKeeper API documentation (flavio via mahadev) - - ZOOKEEPER-607. improve bookkeeper overview (flavio via mahadev) - - ZOOKEEPER-485. Need ops documentation that details supervision of ZK server - processes. (phunt via mahadev) - - ZOOKEEPER-658. update forrest docs - AuthFLE no longer supported (flavio via - mahadev) - - ZOOKEEPER-640. make build.xml more configurable to ease packaging for linux - distros (phunt via mahadev) - - ZOOKEEPER-579. zkpython needs more test coverage for ACL code paths (henry - via mahadev) - - ZOOKEEPER-688. explain session expiration better in the docs & faq (phunt - via mahadev) - - ZOOKEEPER-663. hudson failure in ZKDatabaseCorruptionTest (mahadev via henryr) - - ZOOKEEPER-543. Tests for ZooKeeper examples (steven via mahadev) - - ZOOKEEPER-692. upgrade junit to latest version (4.8.1) (phunt via mahadev) - - ZOOKEEPER-601. allow configuration of session timeout min/max bounds (phunt - via mahadev) - -NEW FEATURES: - ZOOKEEPER-539. generate eclipse project via ant target. (phunt via mahadev) - - ZOOKEEPER-555. Add stat information to GetChildrenResponse. (Arni Jonson and - phunt via mahadev) - - ZOOKEEPER-550. Java Queue Recipe. (steven cheng via mahadev) - - ZOOKEEPER-368. Observers: core functionality (henry robinson via mahadev) - - ZOOKEEPER-496. zookeeper-tree utility for export, import and incremental - updates (anirban roy via breed) - - ZOOKEEPER-572. add ability for operator to examine state of watches - currently registered with a server (phunt via mahadev) - - ZOOKEEPER-678. Gui browser application to view and edit the contents of a - zookeeper instance (Colin Goodheart-Smithe via phunt) - - ZOOKEEPER-635. Server supports listening on a specified network address (phunt via breed) - -Release 3.2.0 - 2009-06-30 - -Non-backward compatible changes: - -BUGFIXES: - ZOOKEEPER-444. perms definition for PERMS_ALL differ in C and java (mahadev) - -Backward compatible changes: - -BUGFIXES: - ZOOKEEPER-303. Bin scripts dont work on a Mac. (tom white via mahadev) - - ZOOKEEPER-330. zookeeper standalone server does not startup with just a - port and datadir. (chris darroch and mahadev) - - ZOOKEEPER-319. add locking around auth info in zhandle_t. - (chris darroch via mahadev) - - ZOOKEEPER-320. call auth completion in free_completions(). - (chris darroch via mahadev) - - ZOOKEEPER-334. bookkeeper benchmark (testclient.java) has compiling errors. - (flavio and mahadev) - - ZOOKEEPER-281. autoreconf fails for /zookeeper-3.0.1/src/c/ (phunt) - - ZOOKEEPER-318. remove locking in zk_hashtable.c or add locking in - collect_keys() (chris darroch via mahadev) - - ZOOKEEPER-333. helgrind thread issues identified in mt c client code - (mahadev via phunt) - - ZOOKEEPER-309. core dump using zoo_get_acl() (mahadev via phunt) - - ZOOKEEPER-341. regression in QuorumPeerMain, - tickTime from config is lost, cannot start quorum (phunt via mahadev) - - ZOOKEEPER-360. WeakHashMap in Bookie.java causes NPE (flavio via mahadev) - - ZOOKEEPER-362. Issues with FLENewEpochTest. (fix bug in Fast leader election) - (flavio via mahadev) - - ZOOKEEPER-363. NPE when recovering ledger with no hint. (flavio via mahadev) - - ZOOKEEPER-370. Fix critical problems reported by findbugs. - (flavio via mahadev) - - ZOOKEEPER-347. zkfuse uses non-standard String. (patrick hunt via mahadev) - - ZOOKEEPER-355. make validatePath non public in Zookeeper client api. - (phunt via mahadev) - - ZOOKEEPER-374. Uninitialized struct variable in C causes warning which - is treated as an error (phunt via mahadev) - - ZOOKEEPER-337. improve logging in leader election lookForLeader method when - address resolution fails (phunt via mahadev) - - ZOOKEEPER-367. RecoveryTest failure - "unreasonable length" IOException - (mahadev via phunt) - - ZOOKEEPER-346. remove the kill command fro mthe client port. - (phunt via mahadev) - - ZOOKEEPER-377. running ant cppunit tests, a failure still results in - BUILD SUCCESSFUL (giri via mahadev) - - ZOOKEEPER-382. zookeeper cpp tests fails on 64 bit machines with gcc 4.1.2 - (mahadev via phunt) - - ZOOKEEPER-365. javadoc is wrong for setLast in LedgerHandle - (flavio via phunt) - - ZOOKEEPER-392. Change log4j properties in bookkeeper. (flavio via mahadev) - - ZOOKEEPER-400. Issues with procedure to close ledger. (flavio) - - ZOOKEEPER-405. nullpointer exception in zookeeper java shell. - (mahadev via breed) - - ZOOKEEPER-410. address all findbugs warnings in client/server classes. - (phunt via breed) - - ZOOKEEPER-403. cleanup javac compiler warnings. (flavio via breed) - - ZOOKEEPER-407. address all findbugs warnings in - org.apache.zookeeper.server.quorum.** packages. - (flavio via breed) - - ZOOKEEPER-411. Building zookeeper fails on RHEL 5 64 bit during test-cppunit - (mahadev via phunt) - - ZOOKEEPER-402. zookeeper c library segfaults on data for a node in zookeeper - being null. (mahadev via phunt) - - ZOOKEEPER-415. zookeeper c tests hang. (mahadev via phunt) - - ZOOKEEPER-385. crctest failed on hudson patch test (mahadev via phunt) - - ZOOKEEPER-192. trailing whitespace in config file can cause number format - exceptions (phunt via breed) - - ZOOKEEPER-409. address all findbugs warnings in jute related classes - (phunt via breed) - - ZOOKEEPER-416. bookkeeper jar includes unnnecessary files. - (flavio via mahadev) - - ZOOKEEPER-419. Reference counting bug in Python bindings causes abort errors - (henry robinson via phunt) - - ZOOKEEPER-421. zkpython run_tests.sh is missing #! - (henry robinson via phunt) - - ZOOKEEPER-406. address all findbugs warnings in persistence classes. - (phunt et al via breed) - - ZOOKEEPER-435. allow "super" admin digest based auth to be configurable - (phunt via breed) - - ZOOKEEPER-375. zoo_add_auth only retains most recent auth on re-sync. - (mahadev) - - ZOOKEEPER-433. getacl on root znode (/) fails. (phunt via mahadev) - - ZOOKEEPER-408. address all findbugs warnings in persistence classes. - (phunt, mahadev, flavio via mahadev) - - ZOOKEEPER-427. ZooKeeper server unexpectedly high CPU utilisation - (Sergey Zhuravlev via breed) - - ZOOKEEPER-446. some traces of the host auth scheme left (breed via mahadev) - - ZOOKEEPER-438. addauth fails to register auth on new client that's not yet - connected (breed via mahadev) - - ZOOKEEPER-448. png files do nto work with forrest. (mahadev) - - ZOOKEEPER-417. stray message problem when changing servers - (breed via mahadev) - - ZOOKEEPER-449. sesssionmoved in java code and ZCLOSING in C have the same - value. (mahadev) - - ZOOKEEPER-452. zookeeper performance graph should have percentage of reads - rather than percentage of writes - zkperfRW-3.2.jpg (mahadev) - - ZOOKEEPER-450. emphemeral cleanup not happening with session timeout. - (breed via mahadev) - - ZOOKEEPER-453. Worker is not removed in QuorumCnxManager upon crash. - (flavio via mahadev) - - ZOOKEEPER-454. allow compilation with jdk1.5 (phunt) - - ZOOKEEPER-455. zookeeper c client crashes with chroot specified in the string. - (phunt via mahadev) - - ZOOKEEPER-468. avoid compile warning in send_auth_info(). - -IMPROVEMENTS: - ZOOKEEPER-308. improve the atomic broadcast performance 3x. - (breed via mahadev) - - ZOOKEEPER-326. standalone server ignores tickTime configuration. - (chris darroch via mahadev) - - ZOOKEEPER-279. Allow specialization of quorum config parsing - (e.g. variable expansion in zoo.cfg) (Jean-Daniel Cryans via phunt) - - ZOOKEEPER-351. to run checkstyle (giridharan kesavan via mahadev) - - ZOOKEEPER-350. to run rats for releaseaudit. - (giridharan kesavan via mahadev) - - ZOOKEEPER-352. to add standard ant targets required by test-patch.sh script - (giridharan kesavan via mahadev) - - ZOOKEEPER-353. javadoc warnings needs to be fixed. - (giridharan kesavan via mahadev) - - ZOOKEEPER-354. to fix javadoc warning in the source files. (mahadev) - - ZOOKEEPER-349. to automate patch testing. (giridharan kesavan via mahadev) - - ZOOKEEPER-288. Cleanup and fixes to BookKeeper (flavio via mahadev) - - ZOOKEEPER-305. Replace timers with semaphores in FLENewEpochTest. - (flavio via mahadev) - - ZOOKEEPER-60. Get cppunit tests running as part of Hudson CI. - (girish via mahadev) - - ZOOKEEPER-343. add tests that specifically verify the zkmain and - qpmain classes. (phunt via mahadev) - - ZOOKEEPER-361. integrate cppunit testing as part of hudson patch process. - (giri via mahadev) - - ZOOKEEPER-373. One thread per bookie (flavio via mahadev) - - ZOOKEEPER-384. keeper exceptions missing path (phunt via mahadev) - - ZOOKEEPER-380. bookkeeper should have a streaming api so that its easier to - store checpoints/snapshots in bookkeeper. (mahadev via flavio) - - ZOOKEEPER-389. add help/usage to the c shell cli.c (phunt via mahadev) - - ZOOKEEPER-376. ant test target re-compiles cppunit code every time - (phunt via mahadev) - - ZOOKEEPER-391. bookeeper mainline code should not be calling - printStackTrace. (flavio via mahadev) - - ZOOKEEPER-300. zk jmx code is calling printStackTrace when creating bean - name (should not be) (phunt via mahadev) - - ZOOKEEPER-94. JMX tests are needed to verify that the JMX MBeans work - properly (phunt via mahadev) - - ZOOKEEPER-404. nightly build failed on hudson. - (henry robinson and pat via mahadev) - - ZOOKEEPER-345. the CLIs should allow addAuth to be invoked. - (henry robinson via breed) - - ZOOKEEPER-292. commit configure scripts (autotools) to svn for c projects and - include in release (phunt via breed) - - ZOOKEEPER-383. Asynchronous version of createLedger(). (flavio via mahadev) - - ZOOKEEPER-358. Throw exception when ledger does not exist. (flavio via breed) - - ZOOKEEPER-431. Expose methods to ease ZK integration. (Jean-Daniel via breed) - - ZOOKEEPER-396. race condition in zookeeper client library between - zookeeper_close and zoo_synchronous api. (mahadev) - - ZOOKEEPER-196. doxygen comment for state argument of watcher_fn typedef and - implementation differ ("...one of the *_STATE constants, otherwise -1") - (breed via mahadev) - - ZOOKEEPER-336. single bad client can cause server to stop accepting - connections (henry robinson via breed) - - ZOOKEEPER-434. the java shell should indicate connection status on command - prompt (henry robinson via breed) - - ZOOKEEPER-437. Variety of Documentation Updates (grant via mahadev) - - ZOOKEEPER-443. trace logging in watch notification not wrapped with - istraceneabled - inefficient (pat via mahadev) - - ZOOKEEPER-432. Various improvements to zkpython bindings. - (henry via mahadev) - - ZOOKEEPER-428. logging should be makred as warn rathen than error in - NIOServerCnxn. (phunt via mahadev) - - ZOOKEEPER-422. Java CLI should support ephemeral and sequential node creation - (henry via breed) - - ZOOKEEPER-315. add forrest docs for bookkeeper. (flavio via mahadev) - - ZOOKEEPER-329. document how to integrate 3rd party authentication into ZK - server ACLs. (breed via mahadev) - - ZOOKEEPER-356. Masking bookie failure during writes to a ledger - (flavio via breed) - - ZOOKEEPER-327. document effects (latency) of storing large amounts of data - in znodes. (breed via mahadev) - - ZOOKEEPER-264. docs should include a state transition diagram for client - state (breed via mahadev) - - ZOOKEEPER-440. update the performance documentation in forrest - (breed via phunt) - -NEW FEATURES: - - ZOOKEEPER-371. jdiff documentation included in build/release (giri via phunt) - - ZOOKEEPER-78. added a high level protocol/feature - for easy Leader - Election or exclusive Write Lock creation (mahadev via phunt) - - ZOOKEEPER-29. Flexible quorums (flavio via mahadev) - - ZOOKEEPER-378. perl binding for zookeeper (chris darroch via mahadev) - - ZOOKEEPER-386. improve java cli shell. (henry robinson via mahadev) - - ZOOKEEPER-36. REST access to ZooKeeper (phunt via mahadev) - - ZOOKEEPER-395. Python bindings. (henry robinson via mahadev) - - ZOOKEEPER-237. Add a Chroot request (phunt and mahadev) - - -Release 3.1.0 - 2009-02-06 - -Non-backward compatible changes: - -BUGFIXES: - - ZOOKEEPER-255. zoo_set() api does not return stat datastructure. - (avery ching via mahadev) - - ZOOKEEPER-246. review error code definition in both source and docs. - (pat via mahadev) - -Backward compatible changes: - -BUGFIXES: - ZOOKEEPER-211. Not all Mock tests are working (ben via phunt) - - ZOOKEEPER-223. change default level in root logger to INFO. - (pat via mahadev) - - ZOOKEEPER-212. fix the snapshot to be asynchronous. (mahadev and ben) - - ZOOKEEPER-213. fix programmer guide C api docs to be in sync with latest - zookeeper.h (pat via mahadev) - - ZOOKEEPER-219. fix events.poll timeout in watcher test to be longer. - (pat via mahadev) - - ZOOKEEPER-217. Fix errors in config to be thrown as Exceptions. (mahadev) - - ZOOKEEPER-228. fix apache header missing in DBTest. (mahadev) - - ZOOKEEPER-218. fix the error in the barrier example code. (pat via mahadev) - - ZOOKEEPER-206. documentation tab should contain the version number and - other small site changes. (pat via mahadev) - - ZOOKEEPER-226. fix exists calls that fail on server if node has null data. - (mahadev) - - ZOOKEEPER-204. SetWatches needs to be the first message after auth - messages to the server (ben via mahadev) - - ZOOKEEPER-208. Zookeeper C client uses API that are not thread safe, - causing crashes when multiple instances are active. - (austin shoemaker, chris daroch and ben reed via mahadev) - - ZOOKEEPER-227. gcc warning from recordio.h (chris darroch via mahadev) - - ZOOKEEPER-232. fix apache licence header in TestableZookeeper (mahadev) - - ZOOKEEPER-249. QuorumPeer.getClientPort() always returns -1. - (nitay joffe via mahadev) - - ZOOKEEPER-248. QuorumPeer should use Map interface instead of HashMap - implementation. (nitay joffe via mahadev) - - ZOOKEEPER-241. Build of a distro fails after clean target is run. - (patrick hunt via mahadev) - - ZOOKEEPER-245. update readme/quickstart to be release tar, rather than - source, based (patrick hunt via mahadev) - - ZOOKEEPER-251. NullPointerException stopping and starting Zookeeper servers - (mahadev via phunt) - - ZOOKEEPER-250. isvalidsnapshot should handle the case of 0 snapshot - files better. (mahadev via phunt) - - ZOOKEEPER-265. remove (deprecate) unused NoSyncConnected from KeeperState. - (phunt via mahadev) - - ZOOKEEPER-273. Zookeeper c client build should not depend on CPPUNIT. (pat -and runping via mahadev) - - ZOOKEEPER-268. tostring on jute generated objects can cause NPE. (pat via mahadev) - - ZOOKEEPER-267. java client incorrectly generating syncdisconnected event when in disconnected state. (pat via breed) - - ZOOKEEPER-263. document connection host:port as comma separated list in forrest docs (pat via breed) - - ZOOKEEPER-275. Bug in FastLeaderElection. (flavio via mahadev) - - ZOOKEEPER-272. getchildren can fail for large number of children. (mahadev) - - ZOOKEEPER-16. Need to do path validation. (pat, mahadev) - - ZOOKEEPER-252. PurgeTxnLog is not handling the new dataDir directory - structure (mahadev via phunt) - - ZOOKEEPER-291. regression for legacy code using KeeperException.Code - constants (due to 246). (pat via mahadev) - - ZOOKEEPER-255. zoo_set() api does not return stat datastructure. - (avery ching via mahadev) - - ZOOKEEPER-293. zoo_set needs to be abi compatible (3.1 changed the -signature), fix this by adding zoo_set2 (pat via mahadev) - - ZOOKEEPER-302. Quote values in JMX objectnames. (tom and pat via mahadev) - -IMPROVEMENTS: - - ZOOKEEPER-64. Log system env information when initializing server and - client (pat via mahadev) - - ZOOKEEPER-243. add SEQUENCE flag documentation to the programming guide. - (patrick hunt via mahadev) - - ZOOKEEPER-161. Content needed: "Designing a ZooKeeper Deployment" - (breed via phunt) - - ZOOKEEPER-247. fix formatting of C API in ACL section of programmer guide. - (patrick hunt via mahadev) - - ZOOKEEPER-230. Improvements to FLE. (Flavio via mahadev) - - ZOOKEEPER-225. c client should log an info message in zookeeper_init - detailing connection parameters. (pat via mahadev) - - ZOOKEEPER-222. print C client log message timestamp in human readable - form. (pat via mahadev) - - ZOOKEEPER-256. support use of JMX to manage log4j configuration at runtime. - (pat via mahadev) - - ZOOKEEPER-214. add new "stat reset" command to server admin port. - (pat via mahadev) - - ZOOKEEPER-258. docs incorrectly state max client timeout as 60 seconds - (it's based on server ticktime). (phunt via mahadev) - - ZOOKEEPER-135. Fat jar build target. (phunt and breed via mahadev) - - ZOOKEEPER-234. Eliminate using statics to initialize the sever. Should - allow server to be more embeddable in OSGi enviorments. (phunt) - - ZOOKEEPER-259. cleanup the logging levels used (use the correct level) - and messages generated. (phunt via breed) - - ZOOKEEPER-210. Require Java 6. (phunt via breed) - - ZOOKEEPER-177. needed: docs for JMX (phunt via mahadev) - - ZOOKEEPER-253. documentation of DataWatcher state transition is misleading -regarding auto watch reset on reconnect. (phunt via mahadev) - - ZOOKEEPER-269. connectionloss- add more documentation to detail. (phunt and -flavio via mahadev) - - ZOOKEEPER-260. document the recommended values for server id's - (mahadev via phunt) - - ZOOKEEPER-215. expand system test environment (breed via phunt) - - ZOOKEEPER-229. improve documentation regarding user's responsibility to - cleanup datadir (snaps/logs) (mahadev via phunt) - - ZOOKEEPER-69. ZooKeeper logo - - ZOOKEEPER-286. Make GenerateLoad use InstanceContainers. (breed via mahadev) - - ZOOKEEPER-220. programming guide watches section should clarify - server/clientlib role in data/child watch maint. (breed via phunt) - - ZOOKEEPER-289. add debug messages to nioserver select loop. (mahadev) - -NEW FEATURES: - - ZOOKEEPER-276. Bookkeeper contribution (Flavio and Luca Telloli via mahadev) - - ZOOKEEPER-231. Quotas in ZooKeeper. (mahadev) - -Release 3.0.0 - 2008-10-21 - -Non-backward compatible changes: - - ZOOKEEPER-43. Server side of auto reset watches. (breed via mahadev) - - ZOOKEEPER-132. Create Enum to replace CreateFlag in ZooKepper.create - method (Jakob Homan via phunt) - - ZOOKEEPER-139. Create Enums for WatcherEvent's KeeperState and EventType - (Jakob Homan via phunt) - - ZOOKEEPER-18. keeper state inconsistency (Jakob Homan via phunt) - - ZOOKEEPER-38. headers (version+) in log/snap files (Andrew Kornev and Mahadev - Konar via breed) - - ZOOKEEPER-8. Stat enchaned to include num of children and size - (phunt) - - ZOOKEEPER-6. List of problem identifiers in zookeeper.h - (phunt) - - ZOOKEEPER-7. Use enums rather than ints for types and state - (Jakob Homan via mahadev) - - ZOOKEEPER-27. Unique DB identifiers for servers and clients - (mahadev) - - ZOOKEEPER-32. CRCs for ZooKeeper data - (mahadev) - - ZOOKEEPER-33. Better ACL management - (mahadev) - -Backward compatible changes: - - BUGFIXES: - - ZOOKEEPER-203. fix datadir typo in releasenotes (phunt) - - ZOOKEEPER-145. write detailed release notes for users migrating from 2.x - to 3.0 (phunt) - - ZOOKEEPER-23. Auto reset of watches on reconnect (breed via phunt) - - ZOOKEEPER-191. forrest docs for upgrade. (mahadev via phunt) - - ZOOKEEPER-201. validate magic number when reading snapshot and transaction - logs (mahadev via phunt) - - ZOOKEEPER-200. the magic number for snapshot and log must be different - (currently same) (phunt) - - ZOOKEEPER-199. fix log messages in persistence code (mahadev via phunt) - - ZOOKEEPER-197. create checksums for snapshots (mahadev via phunt) - - ZOOKEEPER-198. apache license header missing from FollowerSyncRequest.java - (phunt) - - ZOOKEEPER-5. Upgrade Feature in Zookeeper server. (mahadev via phunt) - - ZOOKEEPER-194. Fix terminology in zookeeperAdmin.xml - (Flavio Paiva Junqueira) - - ZOOKEEPER-151. Document change to server configuration - (Flavio Paiva Junqueira) - - ZOOKEEPER-193. update java example doc to compile with latest zookeeper - (phunt) - - ZOOKEEPER-187. CreateMode api docs missing (phunt) - - ZOOKEEPER-186. add new "releasenotes.xml" to forrest documentation - (phunt) - - ZOOKEEPER-190. Reorg links to docs and navs to docs into related sections - (robbie via phunt) - - ZOOKEEPER-189. forrest build not validated xml of input documents - (robbie via phunt) - - ZOOKEEPER-188. Check that election port is present for all servers - (Flavio Paiva Junqueira via phunt) - - ZOOKEEPER-185. Improved version of FLETest (Flavio Paiva Junqueira) - - ZOOKEEPER-184. tests: An explicit include derective is needed for the usage - of memcpy(), memset(), strlen(), strdup() and free() functions - (Maxim P. Dementiev via phunt) - - ZOOKEEPER-183. Array subscript is above array bounds in od_completion(), - src/cli.c. (Maxim P. Dementiev via phunt) - - ZOOKEEPER-182. zookeeper_init accepts empty host-port string and returns - valid pointer to zhandle_t. (Maxim P. Dementiev via phunt) - - ZOOKEEPER-17. zookeeper_init doc needs clarification (phunt) - - ZOOKEEPER-181. Some Source Forge Documents did not get moved over: - javaExample, zookeeperTutorial, zookeeperInternals (robbie via phunt) - - ZOOKEEPER-180. Placeholder sections needed in document for new topics that - the umbrella jira discusses (robbie via phunt) - - ZOOKEEPER-179. Programmer's Guide "Basic Operations" section is missing - content (robbie via phunt) - - ZOOKEEPER-178. FLE test. (Flavio Paiva Junqueira) - - ZOOKEEPER-159. Cover two corner cases of leader election - (Flavio Paiva Junqueira via phunt) - - ZOOKEEPER-156. update programmer guide with acl details from old wiki page - (phunt) - - ZOOKEEPER-154. reliability graph diagram in overview doc needs context - (phunt) - - ZOOKEEPER-157. Peer can't find existing leader (Flavio Paiva Junqueira) - - ZOOKEEPER-155. improve "the zookeeper project" section of overview doc - (phunt) - - ZOOKEEPER-140. Deadlock in QuorumCnxManager (Flavio Paiva Junqueira) - - ZOOKEEPER-147. This is version of the documents with most of the [tbd...] - scrubbed out (robbie via phunt) - - ZOOKEEPER-150. zookeeper build broken (mahadev via phunt) - - ZOOKEEPER-136. sync causes hang in all followers of quorum. (breed) - - ZOOKEEPER-134. findbugs cleanup (phunt) - - ZOOKEEPER-133. hudson tests failing intermittently (phunt) - - ZOOKEEPER-144. add tostring support for watcher event, and enums for event - type/state (Jakob Homan via phunt) - - ZOOKEEPER-21. Improve zk ctor/watcher (state transition) docs (phunt) - - ZOOKEEPER-142. Provide Javadoc as to the maximum size of the data byte - array that may be stored within a znode (Jakob Homan via phunt) - - ZOOKEEPER-93. Create Documentation for Zookeeper (phunt) - - ZOOKEEPER-117. threading issues in Leader election (fpj via breed) - - ZOOKEEPER-137. client watcher objects can lose events (phunt via breed) - - ZOOKEEPER-131. Old leader election can elect a dead leader over and over - again (breed via mahadev) - - ZOOKEEPER-130. update build.xml to support apache release process - (phunt via mahadev) - - ZOOKEEPER-118. findbugs flagged switch statement in - followerrequestprocessor.run() (Flavio Paiva Junqueira via phunt) - - ZOOKEEPER-115. Potential NPE in QuorumCnxManager - (Flavio Paiva Junqueira) - - ZOOKEEPER-114. cleanup ugly event messages in zookeeper client - (Jakob Homan) - - ZOOKEEPER-112. src/java/main ZooKeeper.java has test code embedded into it. - (phunt) - - ZOOKEEPER-39. Use Watcher objects rather than boolean on read operations. - (Andrew Kornev) - - ZOOKEEPER-97. supports optional output directory in code generator. (Hiram - Chirino via phunt) - - ZOOKEEPER-101. Integrate ZooKeeper with "violations" feature on hudson - (phunt) - - ZOOKEEPER-105. Catch Zookeeper exceptions and print on the stderr. - (Anthony Urso via Mahadev) - - ZOOKEEPER-42. Change Leader Election to fast tcp. (Flavio Paiva Junqueira - via phunt) - - ZOOKEEPER-48. auth_id now handled correctly when no auth ids present - (Benjamin Reed via phunt) - - ZOOKEEPER-44. Create sequence flag children with prefixes of 0's so that - they can be lexicographically sorted. (Jakob Homan via mahadev) - - ZOOKEEPER-108. Fix sync operation reordering on a Quorum. - (Flavio Paiva Junqueira via Mahadev) - - ZOOKEEPER-25. Fuse module for Zookeeper. (Swee Lim, Bart, Patrick Hunt and - Andrew Kornev via Mahadev) - - ZOOKEEPER-58. Race condition on ClientCnxn.java (breed) - - ZOOKEEPER-56. Add clover support to build.xml. (Patrick Hunt via mahadev) - - ZOOKEEPER-75. register the ZooKeeper mailing lists with nabble.com (phunt) - - ZOOKEEPER-54. remove sleeps in the tests. (phunt) - - ZOOKEEPER-55. build.xml failes to retrieve a release number from SVN and - the ant target "dist" fails (Andrew Kornev) - - ZOOKEEPER-89. invoke WhenOwnerListener.whenNotOwner() when the ZK - connection fails (james strachan) - - ZOOKEEPER-90. invoke WhenOwnerListener.whenNotOwner() when the ZK - session expires and the znode is the leader (james strachan) - - ZOOKEEPER-82. Make the ZooKeeperServer more DI friendly. (Hiram Chirino via - mahadev) - - ZOOKEEPER-110. Build script relies on svnant, which is not compatible - with subversion 1.5 working copies (Jakob Homan) - - ZOOKEEPER-111. Significant cleanup of existing tests. (Patrick Hunt via - mahadev) - - ZOOKEEPER-122. Fix NPE in jute's Utils.toCSVString. (Anthony Urso via - mahadev) - - ZOOKEEPER-123. Fix the wrong class is specified for the logger. (Jakob Homan - via mahadev) - - ZOOKEEPER-2. Fix synchronization issues in QuorumPeer and FastLeader - election. (Flavio Paiva Junqueira via mahadev) - - ZOOKEEPER-125. Remove unwanted class declaration in FastLeaderElection. - (Flavio Paiva Junqueira via mahadev) - - ZOOKEEPER-61. Address (remove) use of sleep(#) in client/server test cases. - (phunt) - - ZOOKEEPER-75. cleanup the library directory (phunt) - - ZOOKEEPER-109. cleanup of NPE and Resource issue nits found by static - analysis (phunt) - - ZOOKEEPER-76. Commit 677109 removed the cobertura library, but not the - build targets. (phunt) - - ZOOKEEPER-63. Race condition in client close() operation. (phunt via breed) - - ZOOKEEPER-70. Add skeleton forrest doc structure for ZooKeeper (phunt) - - ZOOKEEPER-79. Document jacob's leader election on the wiki recipes page - (Flavio Junqueira) - - ZOOKEEPER-73. Move ZK wiki from SourceForge to Apache (phunt) - - ZOOKEEPER-72. Initial creation/setup of ZooKeeper ASF site. (phunt) - - ZOOKEEPER-71. Determine what to do re ZooKeeper Changelog(s) (mahadev) - - ZOOKEEPER-68. parseACLs in ZooKeeper.java fails to parse elements of ACL, - should be lastIndexOf rather than IndexOf (mahadev) - - ZOOKEEPER-130. update build.xml to support apache release process. - (phunt via mahadev) - - ZOOKEEPER-131. Fix Old leader election can elect a dead leader over and over - again. (breed via mahadev) - - ZOOKEEPER-137. client watcher objects can lose events (Patrick Hunt via breed) - - ZOOKEEPER-117. threading issues in Leader election (Flavio Junqueira and - Patrick Hunt via breed) - - ZOOKEEPER-128. test coverage on async client operations needs to be improved - (phunt) - - ZOOKEEPER-127. Use of non-standard election ports in config breaks services - (Mark Harwood and Flavio Junqueira via breed) - - ZOOKEEPER-53. tests failing on solaris. (phunt) - - ZOOKEEPER-172. FLE Test (Flavio Junqueira via breed) - - ZOOKEEPER-41. Sample startup script (mahadev) - - ZOOKEEPER-33. Better ACL management (Mahadev Konar) - - ZOOKEEPER-49. SetACL does not work (breed) - - ZOOKEEPER-20. Child watches are not triggered when the node is deleted - (phunt) - - ZOOKEEPER-15. handle failure better in build.xml:test (phunt) - - ZOOKEEPER-11. ArrayList is used instead of List (phunt) - - ZOOKEEPER-45. Restructure the SVN repository after initial import (phunt) - - ZOOKEEPER-1. Initial ZooKeeper code contribution from Yahoo! (phunt) diff --git a/NOTICE.txt b/NOTICE.txt deleted file mode 100644 index 9c9e95a1d85..00000000000 --- a/NOTICE.txt +++ /dev/null @@ -1,5 +0,0 @@ -Apache ZooKeeper -Copyright 2009 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). diff --git a/README.txt b/README.txt deleted file mode 100644 index 3495f47cb41..00000000000 --- a/README.txt +++ /dev/null @@ -1,36 +0,0 @@ -For the latest information about ZooKeeper, please visit our website at: - - http://hadoop.apache.org/zookeeper/ - -and our wiki, at: - - http://wiki.apache.org/hadoop/ZooKeeper - -Full documentation for this release can also be found in docs/index.html - ---------------------------- -Packaging/release artifacts - -The release artifact contains the following jar file at the toplevel: - -zookeeper-.jar - legacy jar file which contains all classes - and source files. Prior to version 3.3.0 this - was the only jar file available. It has the - benefit of having the source included (for - debugging purposes) however is also larger as - a result - -The release artifact contains the following jar files in "dist-maven" directory: - -zookeeper-.jar - bin (binary) jar - contains only class (*.class) files -zookeeper--sources.jar - contains only src (*.java) files -zookeeper--javadoc.jar - contains only javadoc files - -These bin/src/javadoc jars were added specifically to support Maven/Ivy which have -the ability to pull these down automatically as part of your build process. -The content of the legacy jar and the bin+sources jar are the same. - -As of version 3.3.0 bin/sources/javadoc jars contained in dist-maven directory -are deployed to the Apache Maven repository after the release has been accepted -by Apache: - http://people.apache.org/repo/m2-ibiblio-rsync-repository/ diff --git a/bin/README.txt b/bin/README.txt deleted file mode 100644 index 7924dd6df9a..00000000000 --- a/bin/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -This directory contain scripts that allow easy access (classpath in particular) -to the ZooKeeper server and command line client. - -Files ending in .sh are unix and cygwin compatible - -Files ending in .cmd are msdos/windows compatible diff --git a/bin/zkCleanup.sh b/bin/zkCleanup.sh deleted file mode 100755 index 1622ed9fc77..00000000000 --- a/bin/zkCleanup.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This script cleans up old transaction logs and snapshots -# - -# -# If this scripted is run out of /usr/bin or some other system bin directory -# it should be linked to and not copied. Things like java jar files are found -# relative to the canonical path of this script. -# - -# Only follow symlinks if readlink supports it -if readlink -f "$0" > /dev/null 2>&1 -then - ZOOBIN=`readlink -f "$0"` -else - ZOOBIN="$0" -fi -ZOOBINDIR=`dirname "$ZOOBIN"` - -. "$ZOOBINDIR"/zkEnv.sh - -ZOODATADIR=$(grep '^dataDir=' "$ZOOCFG" | sed -e 's/.*=//') -ZOODATALOGDIR=$(grep '^dataLogDir=' "$ZOOCFG" | sed -e 's/.*=//') - -if [ "x$ZOODATALOGDIR" = "x" ] -then -echo java "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.PurgeTxnLog "$ZOODATADIR" $* -else -echo java "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.PurgeTxnLog "$ZOODATALOGDIR" "$ZOODATADIR" $* -fi diff --git a/bin/zkCli.cmd b/bin/zkCli.cmd deleted file mode 100644 index 3ee9d6d569b..00000000000 --- a/bin/zkCli.cmd +++ /dev/null @@ -1,24 +0,0 @@ -@echo off -REM Licensed to the Apache Software Foundation (ASF) under one or more -REM contributor license agreements. See the NOTICE file distributed with -REM this work for additional information regarding copyright ownership. -REM The ASF licenses this file to You under the Apache License, Version 2.0 -REM (the "License"); you may not use this file except in compliance with -REM the License. You may obtain a copy of the License at -REM -REM http://www.apache.org/licenses/LICENSE-2.0 -REM -REM Unless required by applicable law or agreed to in writing, software -REM distributed under the License is distributed on an "AS IS" BASIS, -REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -REM See the License for the specific language governing permissions and -REM limitations under the License. - -setlocal -call "%~dp0zkEnv.cmd" - -set ZOOMAIN=org.apache.zookeeper.ZooKeeperMain -java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% %* - -endlocal - diff --git a/bin/zkCli.sh b/bin/zkCli.sh deleted file mode 100755 index 05704f530f7..00000000000 --- a/bin/zkCli.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# This script cleans up old transaction logs and snapshots -# - -# -# If this scripted is run out of /usr/bin or some other system bin directory -# it should be linked to and not copied. Things like java jar files are found -# relative to the canonical path of this script. -# - -# Only follow symlinks if readlink supports it -if readlink -f "$0" > /dev/null 2>&1 -then - ZOOBIN=`readlink -f "$0"` -else - ZOOBIN="$0" -fi -ZOOBINDIR=`dirname "$ZOOBIN"` - -. "$ZOOBINDIR"/zkEnv.sh - -java "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.ZooKeeperMain $@ diff --git a/bin/zkEnv.cmd b/bin/zkEnv.cmd deleted file mode 100644 index 5fda353302f..00000000000 --- a/bin/zkEnv.cmd +++ /dev/null @@ -1,34 +0,0 @@ -@echo off -REM Licensed to the Apache Software Foundation (ASF) under one or more -REM contributor license agreements. See the NOTICE file distributed with -REM this work for additional information regarding copyright ownership. -REM The ASF licenses this file to You under the Apache License, Version 2.0 -REM (the "License"); you may not use this file except in compliance with -REM the License. You may obtain a copy of the License at -REM -REM http://www.apache.org/licenses/LICENSE-2.0 -REM -REM Unless required by applicable law or agreed to in writing, software -REM distributed under the License is distributed on an "AS IS" BASIS, -REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -REM See the License for the specific language governing permissions and -REM limitations under the License. - -set ZOOCFGDIR=%~dp0%..\conf -set ZOO_LOG_DIR=%~dp0%.. -set ZOO_LOG4J_PROP=INFO,CONSOLE - -REM for sanity sake assume Java 1.6 -REM see: http://java.sun.com/javase/6/docs/technotes/tools/windows/java.html - -REM add the zoocfg dir to classpath -set CLASSPATH=%ZOOCFGDIR% - -REM make it work in the release -SET CLASSPATH=%~dp0..\*;%~dp0..\lib\*;%CLASSPATH% - -REM make it work for developers -SET CLASSPATH=%~dp0..\build\classes;%~dp0..\build\lib\*;%CLASSPATH% - -set ZOOCFG=%ZOOCFGDIR%\zoo.cfg - diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh deleted file mode 100755 index 4a5ebeae7be..00000000000 --- a/bin/zkEnv.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script should be sourced into other zookeeper -# scripts to setup the env variables - -# We use ZOOCFGDIR if defined, -# otherwise we use /etc/zookeeper -# or the conf directory that is -# a sibling of this script's directory -if [ "x$ZOOCFGDIR" = "x" ] -then - if [ -d "/etc/zookeeper" ] - then - ZOOCFGDIR="/etc/zookeeper" - else - ZOOCFGDIR="$ZOOBINDIR/../conf" - fi -fi - -if [ "x$ZOOCFG" = "x" ] -then - ZOOCFG="zoo.cfg" -fi - -ZOOCFG="$ZOOCFGDIR/$ZOOCFG" - -if [ -e "$ZOOCFGDIR/java.env" ] -then - . "$ZOOCFGDIR/java.env" -fi - -if [ "x${ZOO_LOG_DIR}" = "x" ] -then - ZOO_LOG_DIR="." -fi - -if [ "x${ZOO_LOG4J_PROP}" = "x" ] -then - ZOO_LOG4J_PROP="INFO,CONSOLE" -fi - -#add the zoocfg dir to classpath -CLASSPATH="$ZOOCFGDIR:$CLASSPATH" - -for i in "$ZOOBINDIR"/../src/java/lib/*.jar -do - CLASSPATH="$i:$CLASSPATH" -done - -#make it work in the release -for i in "$ZOOBINDIR"/../lib/*.jar -do - CLASSPATH="$i:$CLASSPATH" -done - -#make it work in the release -for i in "$ZOOBINDIR"/../zookeeper-*.jar -do - CLASSPATH="$i:$CLASSPATH" -done - -#make it work for developers -for d in "$ZOOBINDIR"/../build/lib/*.jar -do - CLASSPATH="$d:$CLASSPATH" -done - -#make it work for developers -CLASSPATH="$ZOOBINDIR/../build/classes:$CLASSPATH" - -case "`uname`" in - CYGWIN*) cygwin=true ;; - *) cygwin=false ;; -esac - -if $cygwin -then - CLASSPATH=`cygpath -wp "$CLASSPATH"` -fi - -#echo "CLASSPATH=$CLASSPATH" diff --git a/bin/zkServer.cmd b/bin/zkServer.cmd deleted file mode 100644 index 1c2061f62f7..00000000000 --- a/bin/zkServer.cmd +++ /dev/null @@ -1,25 +0,0 @@ -@echo off -REM Licensed to the Apache Software Foundation (ASF) under one or more -REM contributor license agreements. See the NOTICE file distributed with -REM this work for additional information regarding copyright ownership. -REM The ASF licenses this file to You under the Apache License, Version 2.0 -REM (the "License"); you may not use this file except in compliance with -REM the License. You may obtain a copy of the License at -REM -REM http://www.apache.org/licenses/LICENSE-2.0 -REM -REM Unless required by applicable law or agreed to in writing, software -REM distributed under the License is distributed on an "AS IS" BASIS, -REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -REM See the License for the specific language governing permissions and -REM limitations under the License. - -setlocal -call "%~dp0zkEnv.cmd" - -set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain -echo on -java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* - -endlocal - diff --git a/bin/zkServer.sh b/bin/zkServer.sh deleted file mode 100755 index 9bca4cbf326..00000000000 --- a/bin/zkServer.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# If this scripted is run out of /usr/bin or some other system bin directory -# it should be linked to and not copied. Things like java jar files are found -# relative to the canonical path of this script. -# - -# See the following page for extensive details on setting -# up the JVM to accept JMX remote management: -# http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html -# by default we allow local JMX connections -if [ "x$JMXLOCALONLY" = "x" ] -then - JMXLOCALONLY=false -fi - -if [ "x$JMXDISABLE" = "x" ] -then - echo "JMX enabled by default" - # for some reason these two options are necessary on jdk6 on Ubuntu - # accord to the docs they are not necessary, but otw jconsole cannot - # do a local attach - ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain" -else - echo "JMX disabled by user request" - ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" -fi - -# Only follow symlinks if readlink supports it -if readlink -f "$0" > /dev/null 2>&1 -then - ZOOBIN=`readlink -f "$0"` -else - ZOOBIN="$0" -fi -ZOOBINDIR=`dirname "$ZOOBIN"` - -. "$ZOOBINDIR"/zkEnv.sh - -if [ "x$2" != "x" ] -then - ZOOCFG="$ZOOCFGDIR/$2" -fi - -if $cygwin -then - ZOOCFG=`cygpath -wp "$ZOOCFG"` - # cygwin has a "kill" in the shell itself, gets confused - KILL=/bin/kill -else - KILL=kill -fi - -echo "Using config: $ZOOCFG" - -if [ -z $ZOOPIDFILE ] - then ZOOPIDFILE=$(grep dataDir "$ZOOCFG" | sed -e 's/.*=//')/zookeeper_server.pid -fi - - -case $1 in -start) - echo "Starting zookeeper ... " - java "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" & - /bin/echo -n $! > "$ZOOPIDFILE" - echo STARTED - ;; -stop) - echo "Stopping zookeeper ... " - if [ ! -f "$ZOOPIDFILE" ] - then - echo "error: could not find file $ZOOPIDFILE" - exit 1 - else - $KILL -9 $(cat "$ZOOPIDFILE") - rm "$ZOOPIDFILE" - echo STOPPED - fi - ;; -upgrade) - shift - echo "upgrading the servers to 3.*" - java "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.server.upgrade.UpgradeMain ${@} - echo "Upgrading ... " - ;; -restart) - shift - "$0" stop ${@} - sleep 3 - "$0" start ${@} - ;; -status) - STAT=`echo stat | nc localhost $(grep clientPort "$ZOOCFG" | sed -e 's/.*=//') 2> /dev/null| grep Mode` - if [ "x$STAT" = "x" ] - then - echo "Error contacting service. It is probably not running." - else - echo $STAT - fi - ;; -*) - echo "Usage: $0 {start|stop|restart|status}" >&2 - -esac diff --git a/build.xml b/build.xml deleted file mode 100644 index 533b2cb10a4..00000000000 --- a/build.xml +++ /dev/null @@ -1,1347 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ') -} - - -function SwitchMenu(obj, thePath) -{ -var open = 'url("'+thePath + 'images/chapter_open.gif")'; -var close = 'url("'+thePath + 'images/chapter.gif")'; - if(document.getElementById) { - var el = document.getElementById(obj); - var title = document.getElementById(obj+'Title'); - - if(el.style.display != "block"){ - title.style.backgroundImage = open; - el.style.display = "block"; - }else{ - title.style.backgroundImage = close; - el.style.display = "none"; - } - }// end - if(document.getElementById) -}//end - function SwitchMenu(obj) diff --git a/docs/skin/images/README.txt b/docs/skin/images/README.txt deleted file mode 100644 index e0932f4a46d..00000000000 --- a/docs/skin/images/README.txt +++ /dev/null @@ -1 +0,0 @@ -The images in this directory are used if the current skin lacks them. diff --git a/docs/skin/images/add.jpg b/docs/skin/images/add.jpg deleted file mode 100644 index 06831eeb3ddf196c72e23557e0ed23ab5eebef14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1142 zcmex=8LQWSUGXZw=a9;-CH1opy?hi@Re9OjXnTmIP&sRNi|KfA6jNG!RuT!@9YtHw( z9erp2Zq<%rjVqhBF6#`j(Nz7-sP^lFKTm1Gh0D`qPDac0FaMRdT;F`zq?kW-W@pz% ze5s1wt~F!ht0dJ-6Wtq2zbVbV=fB~?vunDX+mkr&9obTxINM8acHwgGD`nz_>c8~X z#64k4<|uMwTb-nlGND71x8e8h0CS_Z??Mj^qF+we3cIj#)xArPJbFc z1J^&EzSnzGz5a8z`w3oY3k27{($?ztW&09(=ivnpp{KI7WqXY09TH4GKIhxFx{~WE za;gV-Un(Aaa&M}6?~U3j!7KUi>iU;l*gowzla$PHmbcrJ<^Kv-hZ^4yNZ?w%@WRrg z9hKMiif_BPt>>(?=HJZAb?S+a3uMc;RsFTId)L4BO>y9_%h9vu@!Dqx|QqvyOz9SIJ(F^=e;T^m(oCzw|lXRk=EsYqq%^ zT`d)L>*v{G>#v@6=iH9&5}xL?Y5R3H&Bwh9=Y-v2RZ~9aIrWlLlqS2Jh$?@c!KtzFq_@~|twFFteiZSS44jqO(VSv>5s zSk$Z3r4sR|%QU3V;-b@%!bJ@$Iu>ZHYFM=ZnC>fE$}45(-kf`P=H0v9*1LrwXLhOr z(Tqu+Dw97yVxO<^>3%?xVY_#66#K#toTr`}oRP~sdOKTC=ER@XAKDdr{M$Kf7M%Ki l%F17_Xw@h6ZS#V!{>|Nxc>kE diff --git a/docs/skin/images/built-with-forrest-button.png b/docs/skin/images/built-with-forrest-button.png deleted file mode 100644 index 4a787abe4dc7831124c5a3105d328309d671654e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1936 zcmeAS@N?(olHy`uVBq!ia0vp^5kM@@!3-or#C}}@QVJ!m5hWp+B{``KPNkVSB?{%4 zB^e5i1&PTSsS0lSMMbH_C5p!XSU`qIg48;emSp4?F%*}imZj$9IV6Hr>g5-upPn-{ z7N|%Aq{unHpt2}4J)?vn1ZWJ@wBY=-lJdkNkg=tCDTyVS`FXqD1Vn&(#K3xrQo&*h zA(^?U42FjK2B!K3Mr$wBh5%Ktmw5WRvR~q672y_j(|Vu{6yhxKh%9Dc;Q9c)(aAfd*^e>N}8ml8GDLnbh$Vh8yg>#P$;PF zC>QelbcQQ9twO^-_C801qj=2GblKSS!fj&e!5;2=cvDr4f*v|q3raa`n#2k(0Hfotk0b1J=Zc zVix-wb&f*FNxFAB>YkC+NOHE=$eo>CQlF6FW+Y*C$UweP(%~&@q>7f$#X#{A z;Q~QbwWpj0XSnm#bgimu&2m(IKJ4T((08AzspZ9!kdUPob@ zhUPNf9CfQG1}XglS^Z_=`jsLf7uek{k1Lkt=SCSE_Dw?nen%L4B7i^>@v~C&d=nR^FdJVP^0Cp33}tc?Gq$O(FfPpv_~AIPICFic;5W(Z>PHY@&p}`TbikKkV444Jk93Se-mtf*V{`vIwng zXu9Mqxh&@_YxVKtE1Q|E&EFj=C=gTZUe?$exv?(b$cHuUKkhedO?dx=U+Qq@<*v^rKUcg+uGXdSD-c1$LjDs$D3&%&n#_h{2#BhC!i@(`b;D9TORRq zdb5xI_xrb_^#0kXwa*@ycsr?PYU?avxGJe9GyUl7*!OiS zyFPrgF>;P=eC379dw#zycy~ibF5adl<@V%<|Ac&Q-%p5$abw8I1(|q!`K$Bs-$Ufi zn-NYN)w4{zw?yhbQXIC#@eD+l6OwziP6C9qB=6m^)Bp&s4|Xb_eY#M_>}{ahob-+F#617?*~$q0=_@T*^V)U2LV1m;&(&(TU%TRD_D(n) zkt5*A;KlgCw)x%rA4f%Q=dtWM=lnF0i|2Y{YvH!Kii;L@-PURI*lQP0-t8T_&{^nm zamG&RnUcY0o~~6qWBy(4eNK5~Ox(I2w%NP3-IJSYEYXC!` B3(^1p diff --git a/docs/skin/images/chapter_open.gif b/docs/skin/images/chapter_open.gif deleted file mode 100644 index eecce18b50a0146d663334eb5b38691fa40b4d5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49 zcmZ?wbhEHbWM^PuXkcJiy6xEi|Nj+#vM@3*Ff!;c00Bsbfr+Dqzfm%aH&TPa8UR61 B3ljhU diff --git a/docs/skin/images/current.gif b/docs/skin/images/current.gif deleted file mode 100644 index fd82c082012faa682ecd4e68d2eedef9ae2e6231..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54 zcmZ?wbhEHbdvgf9JlRm%f*ED|^)zR->+Co862^^pk!u5$EP!3fjzw8XU#}8Il-2 zEI`Z*Zh{lXkg>50Py~%EL5*|HpdHu^uz_@DjRE$?+OB>2cHigy_~CiltzA2q(dY-R zE0ws?)uol-wpiR0>Zwh8gwrE{IE4kjFcsa zE<)u6=*0rOQdlXmQsSg~b2v>_e&*;)omVU%Jn>|M%;yddYu#v-EI}s0b+HN;3oue* zQpwmBX(`VSO8MC}-CeIu#fheb@BCT0%4VJ)QmWBvMb)yUR4#8pr71Q^aZ=)y#HSK3 zi^EBYPE(AO)UWyiAN=;Wkbp|%%%IeP9kb@tPsPeiHEF9Vw+tJ-#7Rk}u8!&z%Kp{@mlFk3&u$Ct;y9Gvwjj5D*FIBO$!^50-oB?3Lr$5Eqo7T}kG=xr4bXpv< z*R5s2hBg2OdwSTmay3J5pCob)Pe6dk!R?o=fVuO#q_s7>Q>ALh8Y@Tzqa&PbKSJ$w z&E#upxTXuR!K0ET*c*tT*NLtS0W6A@QMF#>Raf`zl9=%LrG z(NhYzXXUD;0X283s6e9Dd0|~m^4KvDJaG4riJ9HtH#%5 zaygn>T9_F{)I}5FE|f$7HUxB-XnqrXz$VE+aIOd*cRo4pZbnIJuD_l|_iaJMF{b2v z6}7djx&K}&m8Sc#-9!PrU_5gRxDe1l5`6!(6;Mgig`AkVPSWw?R@KtNqOJE6WwThV z+4@nHho0%-^oygxpzfTX|wxJzZvc-fOu>C=>| zWlLH7!>vTQ9Nsy0-SQ1y|IGoCW5@W#+E(;vf`}qM|Il_CZf<3SfDQp$f?ERp-nl;S z+NTw;=$B7>lP1rNXI_F=E^cP=_6LZ{%5c{5*sUA+`*Xi$P8>5giWz;mgJ)WAMvsmX z#WAaY@?#plw2I^+xCzkb{lQLarr!d9k z_v$o7%i?F%JCApceRpoAeb3$ra3{b8hy|QJc!>RN8!%Ghyr<7QhJ!~2&s)|WI_2C8 zlX+9#2cBNKd`5+_uVl4q)Ka1AW^ksWoPTt7QWr&B9mSL(CUm+3ObDbQ5M6r>eco}V zyPH{2Ou_rXE^AhFTHXDg1OV)8Y+6!n%%QwCGo>*Mc!vy7E+AWiy9vi^%ya@W1crjg z0tN47w>4ipX7%r<;#~Gh_Wc?4%W8Z$P%Xp)AHtZQ<1PX^iU#9K^J87Fg6E{OgWcAy ze`OjtOmQE$IQ}S;cbYPp{R$ccVo@xF@LXyzMb|4n8(_dYPB{Cg6VBe)X-&uF81MO7 zeoIC54K*UVy+Vi^D}^YFgdmi%H3SkLIPab7_5R>lZ+EWYIZTJA00000NkvXXu0mjf DMgv2X diff --git a/docs/skin/images/external-link.gif b/docs/skin/images/external-link.gif deleted file mode 100644 index ff2f7b2a0edcd149b45744fdcb0868e37143710c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71 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 P00000NkvXXu0mjfVyU~g diff --git a/docs/skin/images/hack.jpg b/docs/skin/images/hack.jpg deleted file mode 100644 index f38d50fe4e87cb4ede05ce4b6f124460b77f2a0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 743 zcmex=i3*BJ!6k`h{6D}T z$iX1OAi~Tj$iO7X$SlbC{|JKw10w@7BLfm(XJ%pJ0LrKcFfc&?GgOv|nURH6kWENf zkwJ-FL|MfMs7f5@Q$`g1tjr8-KnXQL1_q#~fqGzOGcpM(GO!prCNi@M1qv57PW)i> z{}uxePyv%5vmk>#!z=Hw)gfzchs_dwb)6~u-lc{J9p#OZ6SG*{l#7i&yEVyNe`52m z?8<(|$z{?4i%i3(tlGKv6RT2AO_XoH_ze@T4q^E^|1Do4E?duNoY48XDn;^jy+5zl z+XU5E*B@I8^sa2*)?&wgVr`M0ezaz8!s?*grt`V(_w>!37OKV~GNofv@IAJ)s`t@z z^FlA$8!_%?Z7SXJL&p2U66xZ5uJx0j=v`Qu{B@(m?4Vh@_!qo9xtn^QNZp*i{dALZZD-PQj)&8J`hMPbAhvS`-8>JA6F4^r=zKIWe^-BH${BtgXXyz|z4k0IlcQSn1w>fm t4@cY%T^+SFHFVX2^CbaS}9ZZyUVQ>#nGL-7*pWflJ-z4MzO#pq+?lJ%X diff --git a/docs/skin/images/header_white_line.gif b/docs/skin/images/header_white_line.gif deleted file mode 100644 index 369cae8dcf2da7d30c4ed7a8781b6bdf95a022d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37 pcmZ?wbh9u|6k%XwXkcLY|NsA?3y*XdfB+=Iz{JwhKa-Wg8UV|E3E%(# diff --git a/docs/skin/images/info.png b/docs/skin/images/info.png deleted file mode 100644 index 2e53447e8c2446ea0f6a56fec811e1786e691d49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1320 zcmWlXZ&1?)7>7&1q8${icL>E5?6e0xt=eJLszo9c2$*4!0MS*JoAHQRSf*8T8ZdHE zdRib#rv*B$t7TU0vk_F6krFk# z{0M25qh^EI4M??m+775mn6kUQeL~QvCalpgYqHn@xlXRFsY7J;T8#q3zT}0 zu;LUag($-0k|C^~pi$aJ+k6t3L20L!v>^lo$Y9Rx2UH}^x-^6ZHrfE0PN-#QtCz1f z$hAb9uTRgoFxDjmDTUq~4U%GrF>-CTwtj@536oo*Mj+BAL(FDpH_5djlwFBglzL7_ zImJ4Y0yT+YMx>=BIuoEW@TH&(ra*%YrCmY|X|i|o6{wD~Tikw$3IUXOG{hP$UI}P` zNV`bOptuoZ-C~#(g27uJi_6d2y8*c#P~aNE3LEVTJtu+~HExxooWbm&tX?T%mZ4_8 znv4$qX=ww*Y5_<~btZ&z;;h@(_dw6OL7bDotl8-=iC<6|G$~}44$kAP9Ua1hVMbYI z(YHL_C!&vp+epy%d z_3j;s5joeyGfxrfn#PmK8C#pN@SD3&FD!}K(X#nWD%6oXH({C}Y6y*4m3X)(buAR@ z4_u-yOwl?l(jk_j&-{tbZdh$!_x1T+6Jg2bX;OMh!&bv=a;{VYmguFkd>YejL z(yTq&UT55&C3U5Wc1=z%)jo#Yy zen~|sWUbU>aWPdDk^bhLbiZ$$`@YGsl+Uv~3ilO$Jt!N&UyaY^nfEtyNxtf*>aZ0P zYs$CH`_7_B`7<&$#o^RQSmo zVQlJ$#Y0d3y_u4%5I*UAefd&)*3;{b6J4v+)5D0SPOme`?20|p-rCw|0gnzQ=TAF# z^l^V6aX8}eo%wKHvLh@tv_@JJd7i&)WJBOUUKy47jkq_-)i|*w;aVUrH|R!ZNY~pb U&-nRQwxE@~taLcd4Hh&10}nn9o^tnS&|T3i>qxFe$? f}PJadT6$68(tDnm{r-UW|L@jfU diff --git a/docs/skin/images/label.gif b/docs/skin/images/label.gif deleted file mode 100644 index c83a3893c5526efb52f2edab685a1b0d7a1ecc74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54 zcmZ?wbhEHbdhCYGe8D3oWGWGIAWq$;?3`UWs4{$yd~ fVqj#@VE_V<76vBXp8m=M4RQ+_jm%nu85yhr=NMS;ykzBV zGV@+Ib>8iG+0i+5o5%e;$DOmCbGDtGouBno)Pp@O`xkod^YDHAe&7q={KCBSjEXQs zia-R}v;dr7tR!PTU@gZjorczO*Y4(!3udEUXj^5nt6CcbI=M{9lA3*v!R*V{5Xk12*s*W`tyJQzcOyP zZ|4nGYCwl+^~R>oQ9yIziSOk@uI9c7MHdfo+O=dPhNjo`t2TAlmN1bw7PY!& zb8wiMUN#=~j?AJaqD?aaD8@$vs`?(gvBH&H+cp7{2^cJn$wdq^+uv$+z|MKKt+?hr z*QsL5i(y(cUzD}JiFa$qb^-fga2lYfPWKeWPkU;dXzH zDz+;0UGiO>WV7RjIU=PTtqnMrXIV?F-0jLxCcN%Rd*oPrek$=zU&fPf)RHo>wLjZ` v{OiZh;9_~M^1Bpx^As`Ho2BPVXIo0*@-_bK23aA9FY1)%eyY46N3#9^6k+17 diff --git a/docs/skin/images/poddoc.png b/docs/skin/images/poddoc.png deleted file mode 100644 index a393df7372349f6df300eacf8d62987dba6f85aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 856 zcmV-e1E>6nP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006syVXTxM00006 zVoOIv0RI600RN!9r;`8x010qNS#tmY4#WTe4#WYKD-Ig~00O*8L_t(|+QpVTNE|^J z$A2fM1T`LdK8|#PiddK?TFD{e3)I2~C^izoQX5IcN=2)r5d^`Af`y%hg(!sB1jJZ~ zhhQQ2xbuN2jD|xCJ-y4!uDkAD*4y(U^S#%Bffg1Pd~Ubu+KBq}=IwO^pm2rcF!(l?9kaD>;J^+QI_c}?CwAUL!v{63b zMSwIAqZURm`P~-C@AoSl%Ul|ean#u3@mK?mz^4>(UJ5^_rrx+pOJh1dEH4pbk^qc| zxl{Vd?dDxqS8R>E@IiO~%8JU*q-C#{P-mxZ#<)zE2x%c6UoZ>}L4H2$?ZMa>l$5Az zCnqp40PXEiQ={%(Ux)d5xVqAT?1CXlpEWmw&j&3nP+JR&i!e6_d3i883OPBDoD7?r z(Ax`*jr!5wmzQj8Y$$vp9wq^w1`@Agd>pp7p{ECymW1(_Rc*9OkB?zu0%m5QzFt52 zIwONp5k!(jWwFu7;ZU_IFNgDUI6PF>PfsBw#e8aa7qYWWK9VNW%oiI-Qd5&^mWvCx zzE;=L)BgraO_f<|84UJ5lBVnKhSgPATT?+LwYI{+f%#Nf8EkDmS@Y~Q(i21YCNmR; zhoPb2F}ErzV0IP?3QYMH3+f~e}F{5Y&DV_64k;u-pUk+1@tdkX!Dz!8_URaM9J0C iRWA_nQh{&$5BdZ3oY`MtoEnh;0000axdUs$VmXNZB}bC%VxwSp6_waJ)d|kVIc)zPRqeIZZsP!%PWP&!rJ{ccrcVef_824V*rcNe`YK8X6u^&TVfiYDOC!)l_wikn`gg#xkmuz4&!! zCzsZ~Q+eXo?}^E*8nm^kOS`apx&1SHP xOSk{*ch5;mUqxD8)P4*PsMYcJGhc`tpL?Kv*ou)#zg(Cser$hB&OAcs)<55`ex3jT diff --git a/docs/skin/images/rc-b-l-15-1body-2menu-3menu.png b/docs/skin/images/rc-b-l-15-1body-2menu-3menu.png deleted file mode 100644 index cdb460a1da7304fcc2d5e326db43e27c6fa9b025..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^{2|H(?D8gCb5n0T@z;^_M8K-LV zNdpBhc)B=-RNPAba`NT>^A2hWNr{PR3em>=;?_TBi7s_Hc|B+4`ZEU(9`l-)7A&jU zw*UXXiOVBh8rmIW1DhHR$*SJK%G8XnD!U=Fb1;@b0XlLnh)q2r=G4T4?@%xY8I4J~G88*bRV z@j)-^8Y2b$lol(aFVkF%GBiAm42+EveLZx3%wg?nJ=YY_W-h?yH diff --git a/docs/skin/images/rc-b-r-15-1body-2menu-3menu.png b/docs/skin/images/rc-b-r-15-1body-2menu-3menu.png deleted file mode 100644 index 3eff254fd179852394e8bd960eb1f610f05f2d4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^{2|H(?D8gCb5n0T@z;^_M8K-LV zNdpBpd%8G=RNPAba`NT>^A2hWO5w`f+{Qm=?YwdG#C08QO_zCTW@ch+#j>hKxom73 zTjv>7&YHT!RZ_*3)#Lh^W2X*H5PRiN6|!;D27?YTi6X6M9-jv*7;6_w9) zZgF9{q@i@+@P#?TM-Ci3*X5yc#3IzEqkW;KHHXLNh0Bbb8ks*SpICXq?9mk-FNPk6 z6&k9CCJ5ymU$9Q0)4KMskfp|2uWoLm2qtCuDGUxDJvbN`4z&r&mdK II;Vst0FZQVxBvhE diff --git a/docs/skin/images/rc-b-r-5-1header-2tab-selected-3tab-selected.png b/docs/skin/images/rc-b-r-5-1header-2tab-selected-3tab-selected.png deleted file mode 100644 index b175f27b16e19464241853fd16c8030d649b6d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G(@^*J&_z!{$_AZ|c6yYrJh%9Dc;5!1sj8nDw zq=AAqo-U3d6}OVVoP7EJd;{Bz3#=?Zb_B4_oH;{c&DT)DCIv=EySz4*dTBPX4PtS6 mY}~b?ZBH1Y+4#!B*chy;Ss!r=G{ytXVeoYIb6Mw<&;$T7sy6BX diff --git a/docs/skin/images/rc-t-l-5-1header-2searchbox-3searchbox.png b/docs/skin/images/rc-t-l-5-1header-2searchbox-3searchbox.png deleted file mode 100644 index e9f4440d1f58d53fa16a2ea141bd04cb69f0a595..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G(@^*J&_z!{$_AZ|c6yYrJh%9Dc;5!1sj8nDw zq=ACgo-U3d6}OWA@U#7wu&b{+{^9NK^tSr?dgBcZEcf=*o%!(mygu6k9cB)L#t;SW l23E(Y39JX26pSx0GKAXcJpW&R>>bb;22WQ%mvv4FO#q7bJP`l@ diff --git a/docs/skin/images/rc-t-l-5-1header-2tab-selected-3tab-selected.png b/docs/skin/images/rc-t-l-5-1header-2tab-selected-3tab-selected.png deleted file mode 100644 index f1e015b36c399a2d3a5879d69c3f31a82ee9f7bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G(@^*J&_z!{$_AZ|c6yYrJh%9Dc;5!1sj8nDw zq=AC2o-U3d6}OWA@Uz(0{Y#73_gCfc!(wyExc&dn9AFaCcpHE6!;`1hZT9v53>z4P wJ&s(UHx3vIVCg!0D03wUjP6A diff --git a/docs/skin/images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png b/docs/skin/images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png deleted file mode 100644 index e9f4440d1f58d53fa16a2ea141bd04cb69f0a595..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G(@^*J&_z!{$_AZ|c6yYrJh%9Dc;5!1sj8nDw zq=ACgo-U3d6}OWA@U#7wu&b{+{^9NK^tSr?dgBcZEcf=*o%!(mygu6k9cB)L#t;SW l23E(Y39JX26pSx0GKAXcJpW&R>>bb;22WQ%mvv4FO#q7bJP`l@ diff --git a/docs/skin/images/rc-t-r-15-1body-2menu-3menu.png b/docs/skin/images/rc-t-r-15-1body-2menu-3menu.png deleted file mode 100644 index 29388b5efc9155a12ec75c7ea12940bf45d5b3fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^{2|H(?D8gCb5n0T@z;^_M8K-LV zNdpCcdAc};RNPAba`NT>^M?+dIdJscF$HbSBMB>)A4*uZ_=JL{)&U2O4GkUk_5X}& zetn+%@YUSqJlo~>r9|v_$Osmj3zsmE%8e hpTGaab)Hi`4ELR~XEsLn)B!_*!PC{xWt~$(69Dz9se1qb diff --git a/docs/skin/images/rc-t-r-5-1header-2searchbox-3searchbox.png b/docs/skin/images/rc-t-r-5-1header-2searchbox-3searchbox.png deleted file mode 100644 index 944ed73333d834f3509955d65fecc604393bf626..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G(@^*J&_z!{$_AZ|c6yYrJh%9Dc;5!1sj8nDw zq=AB7o-U3d6}OUWem!|spO%)!#^)EuW@jhJ=H{lxrpBi5bP0l+XkK DqZdXo diff --git a/docs/skin/images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png b/docs/skin/images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png deleted file mode 100644 index 944ed73333d834f3509955d65fecc604393bf626..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G(@^*J&_z!{$_AZ|c6yYrJh%9Dc;5!1sj8nDw zq=AB7o-U3d6}OUWem!|spO%)!#^)EuW@jhJ=H{lxrpBi5TL z>B-E7X2&P~a4`6jwh^=64%N$KvYWpfx`zxWxwdqUOnSC!Nz1SzY(ekLziD#x^ zN5c#+_UIeyY-pcs;P~|OA~k0Xi4!{~wTsAVnY=KnKKB0f`iUPuesXv@{Yci~8^Yz; ze$Gr6yrcD=n6CQspW#+>&)4KPoVTPS*P1y_xgl9G>4Qerv-iJzj6I!?I`hUW=Uv;# zHL>yT)$${4S$`XkoKG^9y{xuxQw?uJf!VPYKQ*7-)s9v(dui5j;p*Lw-?h_ZE}fNP znwi!fr^&uU)A`}8>{ko!=Ix#T_|WcYS)p$px382xAu?rN%}Uqp&Q)t>SDty!{&wjU zF$1}Yb2`GEzRw9aTyRJxLO(N-d%~h~3;r!CocN-C`qZPZEBp0d?V0X;_S@7qkG}EW zu$cJqO5oK~FE(#jvTvWQ_qU8sn~sKLX5RGeejKvAxN5rQtVLh$3bizMgenJ$X;1#o zkZV6ctbo!DryG5`U%cNA- z37SIO>Jn2uPEOf&tRRquW%=UskTnw*lL4j4_oQFx5`5PDln65ExUE?CQL56{W=O6>4%t1lHGeC6k3=o{392#=l uSM0X)>rLCYt$Mw3&7xT=7q(o#=zPoVy6suxF44RH-vj{rcK4+K diff --git a/docs/skin/images/rss.png b/docs/skin/images/rss.png deleted file mode 100644 index f0796ac886201eb43b7a15113e2deb1b32fa9a09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 360 zcmV-u0hj)XP)1pTfh($;sL0vc}u{{{FPIw4gub**+Xunj;hMg-Rk@N{o&!^ z{r&#_{{B{et%#+@{r~@)vdh1}zle=^0GR*(m;g35HkdZcHkixXHq)$8FWQhbW?9;ba!ELWdKKV zVQ^?^V?%Flc4cyOWpXuLNbUdt06|GaK~#90Vz9#p80;8u1CXpOE?Whx%fLW2AK{k88_xIu00030{{sMfLr(Xb|5zme0000&bhz z5bnJPc@Iw>rR$Xk0NnAP0=%b=G`t?HCJ;1;sS0GUaR^Poy(d)4L1!Z`B|uSw*8!8D z1>h1*q8RGKdru$=?;A%ZB_OKlg75{_YH&hNW(a$3KZtet`*CCTW|RqnCt6&9fL-$C zDR`VAOJA7@%7*R5r%~t_!pzTQ{PyECD&+v42uz$ZeUl8ZOTM%Yg!+h+nIH~LchAyC zG1xhRv*%9Z$h&VKs1PWGgNRZBQXUX%?&{cdP2ocD!(ST!+#jwNkiW-tkatpx$iKuDWS zjlmkan~AW!xEI~IUYz>&BtH857{(?B(L3A$QGsv>7uHrX&|ttObr0cOZ8RGP9>iGh zW?Z>CgO?8P$BOj@EL+Aj1LI!1pvMp{xF|nlMi(MNrlpRA51c)0HD~J1>JbH7( z*fz2os)`p6?T2-jQQXvrT;4%gtC0&SgfvxylmLTh?0CT8?*3i4Z}>^vxCq=Vl_457 z8uA{>z{6wDAe+hI*zu$IVd@*KzOx_Q16hbz2<5t&szK8dNK^(d^O(B&9kQ7m9^1SR zf1R5}xjbKue%s33Sk|=)znq`Kn@3;8=%zjl6!U;Xl=3)o3uh}POm;hk;QLFTVS4T& zio@gBw(1E?U$};#5}??<0e3CmiTO$ylP^Du!bld&*B6kIP)Q#b9id!3)r>hIrOymP`xd*0hyA5F0Rtdmn&^ z(AIAF^ryEm`_E;F2oAh533QiH+|Y+yzAY7X^0y||7ta8M)C4T?H$3d~#Ec+Y!r@Q$ z;qn>6)bFP;zI6~?{q0bRQyfmEhD^j|AWeM?nw22KO4S2}k3K(J8&|GC zzL0}Mv$`ud-Gm^L?u5<0hT@~sizf;X5;RL-Q6**~3r$2Rwa|#lH|K-! zJXbqLQ6z+jtJ|p&kODTAfegZ0Su_KGoV_;b1KLAGIq$uZC+9sG5jkNsC3UKnRklpk za8aLUQc+eDGAE)?GAFDiY)(aeP~zi_yj$be_!rlyryjz7fr004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m_e01m_fl`9S#0007K zNklQ>j$aI!>~6BuS6Q;~j%&nubQBVJyz=J&@KuRzjD; zcW(vZmNxJrKpeyxg%ynaLZD)?XsFw9aUiSZ8VZG+fzrUYH*7w2ey^>)m&eCl6R*)C z%=R)sowX&(lcM0`^0IqGG2rt<`VS6F^(fg`42{LbhhyyM=(-V3aWNTYXHDf22mq(2 z!0IZnumH@>0oT`${)-DBowfmq$k1#2*6To_01OTSmzTiN5im0YjEwi0pxkefcYS?`FcOI) za+39RU|;~)-#0-7IU)dleBQ_~Gz2yi2!}nB!COS^Uf}Q$n3@725l=O*b0CUlauQfs zG1<4Y;LWU?LD?PHl9k7t-wA_?`Mj}fOG*Fq!Zx#ulu6&o$RJ{P>DS%_VhWrCUF3V*)!aYF% O0000`WXCjGV$EqLPXcGp+%Z zNis4pF{2v5&dkcj0+dn`WB}@CL6QbCSy&m^1clgvCWsmRzs0}<)XOBuEXZKbu&nmZ zUEkd+zBN|q$1Ixqrz7BO-I}$>PfA|TKK6C0m9FK+cPkQQjN+DUeXu4u@MF*FfGpz| z8`AsaC+fJb;VhfwY3f*Z%PzR)$hD1?A!ldYs+8Y+P^~-c^*$2O#Z#!G>X}ouT=R2Pc+d@(I!`-o_U*_tEEUB#I-?h1ST9V+k zI{VU>v(|fR-Q8VoVYScfxqV08!yC;RNj^=v(@svZJL)?1=<1$Hzg3pkD(!u`W^Jmq zq~WGkfvK;05)NDre`1gmxMs`dZA!UZ7oHRysABC~;c?pY)vx`JQzm!&aUF{m4Rhs5 zdK>CJP0VJAle6=@^)ogG9&Fz#nj>qy%3IC8JJ*rb?aa5nealbC$aFi_cy83=n6g=U z_lkGjUv!q|+;`>nIhuN9+BOx>^4qS5`wNVpC^$^b3Q2I~6Fng)S{-w=EHJ+_>pw&4 z>sqT;#+&Tg{b{Q$r0 zcjPwoN^N?+#M#1Z;pwWy3EJV(Ic&E?RC2Z^U(MN|Em16H;~rq;EcQAajAv$N9{om-s!5r##+I#Y}qkqPB1#0`7CuktZhxU=!4ohn_gbm zlDj{ppYNW{A@7V3(=UI13-HQKP`M&)Y;@vdl5X=hrHIwv9t##XtHe!Kcxhr7`+BO# gv;PeKu`GXNFaKw_))oI_o&UPMIeaoE+5c|>0A~eb6#xJL diff --git a/docs/skin/images/valid-html401.png b/docs/skin/images/valid-html401.png deleted file mode 100644 index 3855210c6c3c85c56f90221b3247fa664374b6bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2948 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 IbxsLQ074N#oB#j- diff --git a/docs/skin/images/warning.png b/docs/skin/images/warning.png deleted file mode 100644 index b81b2ce8a12f88ec3a4c64a910809e667cc20480..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1215 zcmV;w1VH7dsKvk!Vn>-EJ6yV zwCFYvz63#Z5fw@Zda#RSA<;vFP*!L?BpDNvInA8C*8kSS-e=CJT~KEP!3B%8_kLL4 z@Bgjuzu3U_ajgzJvSY3teW9oSl{HhZWS{V|#1F14p?K^260|%Xb}o4LqmAX@ssdp0 zUE9>h(~J_78VifNmT%d7)c|bY(i;$zA5)*e$RT8h=H})-Up#tG+f@O;v?Wi93nmyj zfE~aIijlQ-%YLasU~${N`TCPAP68# zVDBJ4fl`8UjAi|~DI3QG0G;=3m#WP$%7iI_v4gR%0|-ONfyqJ0A>ahf1-x`%!=%a; z0odD%TTRM4)9Ojsg*+1b7c;cXY7<=ZmtPi&3{?$$ME(^f+FT2Hjew!v$jJCga`&kyke7*t94E7%E69^?x z@(}XKsSlfSw)ed?rQD@E5KerChbTW{IoGdV@d=F65Qb3%;nV>omx((Fd#DN;W1Hi} zwqFu}?OVD~eY8P+Qn#G4{-^Y;-RbWD04Glz2TU#yC#WjK()}9aVAq=~iyi-@6<;LY z+XkTJIUaN&*r)9#lwLeS=^1;Eb6>uNBZoi4p<^d7qYM;4P!fnLQ~^;y&7cYtB9!6~ zuL4-}{|-!=vrdMw$f%zBTKJ68lnXh;>e1nU!rD&5qNzYB5SVuaeL1(%za=ta?rF|O9j)JzSq8m0RY&`2UZo! z|L#EhoUJmbGnEn;*AowR&`emeUJobVhPbr@;jDY{`7eyKiH(Tss5u&@AqqPBtF~*j z>)%D@1aZ(Q3PtBuiino zSCl44DCYwxF1B9H>X|Sg3K0M4^eF)A&I360;~#y0X?{qUOU48h593svYn8J=)WDi< zN&*yOz$c}9Eu7Nlo*Mi9!MkxNQ@AS0uq@dhKq^O!~}xz;K@Xd7Y-~+5Tgkg z4;n8VKqrH)>yRyD!_v`xTg$fo_}cDk={CBt?%=_L7Y}CQKjF!DdgqtC!5i1Rxzm5QpK z2~!*=8j6+^RFjFz5M@|438BhMFucKv>1;3p-TwHTham%D8D%61LJ^iMt7uu;wz&}% zK~fJ;qBoRUaQh&XWhh(LZlSnqH_b3b)m(6g zK+=dNG@3;&+^-;m=LJ!M4B%C*Af?S^ax==QAzA<_v`7S4HpL}He-Nm8UeOD#i~wLVu%rwe1afI z+(Y0VFOHKq?)B^mK}2Y#{GWePpygL(#X+o$wPAbweFC$Gv2yh($4v7Cwy&zTbtKo@ zS6N-()=14cpC71f8JKBz_S+7fXusd+8r?r-Yq~s+)%9=Mo#%$$cR9~YzG`T@8>qGK z*j_hwKHy!W`Klw%?kC@KLIfOb?t4*F=a_#vd)D!`^$tBheQa@fZeX@^a;V|_ZNk1q z_HxHB+zX9YjJ3S(?HG%_>v&vs^W?>+HoR}+)0>Z%9+|NZ*9J$LdY=6#2)?P|(^nss We^=By_~!n}n&~UIh@aF1*7FC9)cB|X diff --git a/docs/skin/menu.js b/docs/skin/menu.js deleted file mode 100644 index 06ea471dc57..00000000000 --- a/docs/skin/menu.js +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/** - * This script, when included in a html file, can be used to make collapsible menus - * - * Typical usage: - * - */ - -if (document.getElementById){ - document.write('') -} - -function SwitchMenu(obj) -{ - if(document.getElementById) { - var el = document.getElementById(obj); - var title = document.getElementById(obj+'Title'); - - if(obj.indexOf("_selected_")==0&&el.style.display == ""){ - el.style.display = "block"; - title.className = "pagegroupselected"; - } - - if(el.style.display != "block"){ - el.style.display = "block"; - title.className = "pagegroupopen"; - } - else{ - el.style.display = "none"; - title.className = "pagegroup"; - } - }// end - if(document.getElementById) -}//end - function SwitchMenu(obj) diff --git a/docs/skin/note.txt b/docs/skin/note.txt deleted file mode 100644 index d34c8db5ef4..00000000000 --- a/docs/skin/note.txt +++ /dev/null @@ -1,50 +0,0 @@ -Notes for developer: - ---Legend------------------- -TODO -> blocker -DONE -> blocker -ToDo -> enhancement bug -done -> enhancement bug - ---Issues------------------- -- the corner images should be rendered through svg with the header color. --> DONE --> ToDo: get rid of the images and use only divs! - -- the menu points should be displayed "better". --> DONE --- Use the krysalis-site menu approach for the overall menu display. --> DONE --- Use the old lenya innermenu approch to further enhance the menu . --> DONE - -- the content area needs some attention. --> DONE --- introduce the heading scheme from krysalis () --> DONE --> ToDo: make box with round corners --> done: make underlined with variable border height --> ToDo: make underline with bottom round corner --- introduce the toc for each html-page --> DONE --- introduce the external-link-images. --> DONE - -- the publish note should be where now only a border is. -Like
--> DONE -, but make it configurable. --> DONE -- footer needs some attention --> DONE --- the footer do not have the color profile! Enable it! --> DONE --- the footer should as well contain a feedback link. -See http://issues.apache.org/eyebrowse/ReadMsg?listName=forrest-user@xml.apache.org&msgNo=71 --> DONE - -- introduce credits alternativ location --> DONE - -- border for published / breadtrail / menu /tab divs --> ToDo \ No newline at end of file diff --git a/docs/skin/print.css b/docs/skin/print.css deleted file mode 100644 index aaa99319acd..00000000000 --- a/docs/skin/print.css +++ /dev/null @@ -1,54 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -body { - font-family: Georgia, Palatino, serif; - font-size: 12pt; - background: white; -} - -#tabs, -#menu, -#content .toc { - display: none; -} - -#content { - width: auto; - padding: 0; - float: none !important; - color: black; - background: inherit; -} - -a:link, a:visited { - color: #336699; - background: inherit; - text-decoration: underline; -} - -#top .logo { - padding: 0; - margin: 0 0 2em 0; -} - -#footer { - margin-top: 4em; -} - -acronym { - border: 0; -} \ No newline at end of file diff --git a/docs/skin/profile.css b/docs/skin/profile.css deleted file mode 100644 index eefaa88735d..00000000000 --- a/docs/skin/profile.css +++ /dev/null @@ -1,168 +0,0 @@ - - -/* ==================== aural ============================ */ - -@media aural { - h1, h2, h3, h4, h5, h6 { voice-family: paul, male; stress: 20; richness: 90 } - h1 { pitch: x-low; pitch-range: 90 } - h2 { pitch: x-low; pitch-range: 80 } - h3 { pitch: low; pitch-range: 70 } - h4 { pitch: medium; pitch-range: 60 } - h5 { pitch: medium; pitch-range: 50 } - h6 { pitch: medium; pitch-range: 40 } - li, dt, dd { pitch: medium; richness: 60 } - dt { stress: 80 } - pre, code, tt { pitch: medium; pitch-range: 0; stress: 0; richness: 80 } - em { pitch: medium; pitch-range: 60; stress: 60; richness: 50 } - strong { pitch: medium; pitch-range: 60; stress: 90; richness: 90 } - dfn { pitch: high; pitch-range: 60; stress: 60 } - s, strike { richness: 0 } - i { pitch: medium; pitch-range: 60; stress: 60; richness: 50 } - b { pitch: medium; pitch-range: 60; stress: 90; richness: 90 } - u { richness: 0 } - - :link { voice-family: harry, male } - :visited { voice-family: betty, female } - :active { voice-family: betty, female; pitch-range: 80; pitch: x-high } -} - -a.external { - padding: 0 20px 0px 0px; - display:inline; - background-repeat: no-repeat; - background-position: center right; - background-image: url(images/external-link.gif); -} - -#top { background-color: #FFFFFF;} - -#top .header .current { background-color: #4C6C8F;} -#top .header .current a:link { color: #ffffff; } -#top .header .current a:visited { color: #ffffff; } -#top .header .current a:hover { color: #ffffff; } - -#tabs li { background-color: #E5E4D9 ;} -#tabs li a:link { color: #000000; } -#tabs li a:visited { color: #000000; } -#tabs li a:hover { color: #000000; } - -#level2tabs a.selected { background-color: #4C6C8F ;} -#level2tabs a:link { color: #ffffff; } -#level2tabs a:visited { color: #ffffff; } -#level2tabs a:hover { color: #ffffff; } - -#level2tabs { background-color: #E5E4D9;} -#level2tabs a.unselected:link { color: #000000; } -#level2tabs a.unselected:visited { color: #000000; } -#level2tabs a.unselected:hover { color: #000000; } - -.heading { background-color: #E5E4D9;} - -.boxed { background-color: #E5E4D9;} -.underlined_5 {border-bottom: solid 5px #E5E4D9;} -.underlined_10 {border-bottom: solid 10px #E5E4D9;} -table caption { -background-color: #E5E4D9; -color: #000000; -} - -#feedback { -color: #FFFFFF; -background: #4C6C8F; -text-align: center; -} -#feedback #feedbackto { -color: #FFFFFF; -} - -#publishedStrip { -color: #FFFFFF; -background: #4C6C8F; -} - -#publishedStrip { -color: #000000; -background: #E5E4D9; -} - -#menu .menupagetitle { background-color: #CFDCED; - color: #000000;} - -#menu { border-color: #999999;} -#menu .menupagetitle { border-color: #999999;} -#menu .menupageitemgroup { border-color: #999999;} - -#menu { background-color: #4C6C8F;} -#menu { color: #ffffff;} -#menu a:link { color: #ffffff;} -#menu a:visited { color: #ffffff;} -#menu a:hover { -background-color: #4C6C8F; -color: #ffffff;} - -#menu h1 { -color: #000000; -background-color: #cfdced; -} - -#top .searchbox { -background-color: #E5E4D9 ; -color: #000000; -} - -#menu .menupageitemgroup { -background-color: #E5E4D9; -} -#menu .menupageitem { -color: #000000; -} -#menu .menupageitem a:link { color: #000000;} -#menu .menupageitem a:visited { color: #000000;} -#menu .menupageitem a:hover { -background-color: #E5E4D9; -color: #000000; -} - -body{ -background-color: #ffffff; -color: #000000; -} -a:link { color:#0000ff} -a:visited { color:#009999} -a:hover { color:#6587ff} - - -.ForrestTable { background-color: #ccc;} - -.ForrestTable td { background-color: #ffffff;} - -.highlight { background-color: #ffff00;} - -.fixme { border-color: #c60;} - -.note { border-color: #069;} - -.warning { border-color: #900;} - -.code { border-color: #a5b6c6;} - -#footer { background-color: #E5E4D9;} -/* extra-css */ - - p.quote { - margin-left: 2em; - padding: .5em; - background-color: #f0f0f0; - font-family: monospace; - } - - pre.code { - margin-left: 0em; - padding: 0.5em; - background-color: #f0f0f0; - font-family: monospace; - } - - - - \ No newline at end of file diff --git a/docs/skin/prototype.js b/docs/skin/prototype.js deleted file mode 100644 index ed7d920cb5f..00000000000 --- a/docs/skin/prototype.js +++ /dev/null @@ -1,1257 +0,0 @@ -/* Prototype JavaScript framework, version 1.4.0_pre4 - * (c) 2005 Sam Stephenson - * - * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff - * against the source tree, available from the Prototype darcs repository. - * - * Prototype is freely distributable under the terms of an MIT-style license. - * - * For details, see the Prototype web site: http://prototype.conio.net/ - * -/*--------------------------------------------------------------------------*/ - -var Prototype = { - Version: '1.4.0_pre4', - - emptyFunction: function() {}, - K: function(x) {return x} -} - -var Class = { - create: function() { - return function() { - this.initialize.apply(this, arguments); - } - } -} - -var Abstract = new Object(); - -Object.extend = function(destination, source) { - for (property in source) { - destination[property] = source[property]; - } - return destination; -} - -Function.prototype.bind = function(object) { - var __method = this; - return function() { - return __method.apply(object, arguments); - } -} - -Function.prototype.bindAsEventListener = function(object) { - var __method = this; - return function(event) { - return __method.call(object, event || window.event); - } -} - -Number.prototype.toColorPart = function() { - var digits = this.toString(16); - if (this < 16) return '0' + digits; - return digits; -} - -var Try = { - these: function() { - var returnValue; - - for (var i = 0; i < arguments.length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) {} - } - - return returnValue; - } -} - -/*--------------------------------------------------------------------------*/ - -var PeriodicalExecuter = Class.create(); -PeriodicalExecuter.prototype = { - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.callback(); - } finally { - this.currentlyExecuting = false; - } - } - } -} - -/*--------------------------------------------------------------------------*/ - -function $() { - var elements = new Array(); - - for (var i = 0; i < arguments.length; i++) { - var element = arguments[i]; - if (typeof element == 'string') - element = document.getElementById(element); - - if (arguments.length == 1) - return element; - - elements.push(element); - } - - return elements; -} - -if (!Array.prototype.push) { - Array.prototype.push = function() { - var startLength = this.length; - for (var i = 0; i < arguments.length; i++) - this[startLength + i] = arguments[i]; - return this.length; - } -} - -if (!Function.prototype.apply) { - // Based on code from http://www.youngpup.net/ - Function.prototype.apply = function(object, parameters) { - var parameterStrings = new Array(); - if (!object) object = window; - if (!parameters) parameters = new Array(); - - for (var i = 0; i < parameters.length; i++) - parameterStrings[i] = 'parameters[' + i + ']'; - - object.__apply__ = this; - var result = eval('object.__apply__(' + - parameterStrings.join(', ') + ')'); - object.__apply__ = null; - - return result; - } -} - -Object.extend(String.prototype, { - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); - }, - - escapeHTML: function() { - var div = document.createElement('div'); - var text = document.createTextNode(this); - div.appendChild(text); - return div.innerHTML; - }, - - unescapeHTML: function() { - var div = document.createElement('div'); - div.innerHTML = this.stripTags(); - return div.childNodes[0].nodeValue; - }, - - parseQuery: function() { - var str = this; - if (str.substring(0,1) == '?') { - str = this.substring(1); - } - var result = {}; - var pairs = str.split('&'); - for (var i = 0; i < pairs.length; i++) { - var pair = pairs[i].split('='); - result[pair[0]] = pair[1]; - } - return result; - } -}); - - -var _break = new Object(); -var _continue = new Object(); - -var Enumerable = { - each: function(iterator) { - var index = 0; - try { - this._each(function(value) { - try { - iterator(value, index++); - } catch (e) { - if (e != _continue) throw e; - } - }); - } catch (e) { - if (e != _break) throw e; - } - }, - - all: function(iterator) { - var result = true; - this.each(function(value, index) { - if (!(result &= (iterator || Prototype.K)(value, index))) - throw _break; - }); - return result; - }, - - any: function(iterator) { - var result = true; - this.each(function(value, index) { - if (result &= (iterator || Prototype.K)(value, index)) - throw _break; - }); - return result; - }, - - collect: function(iterator) { - var results = []; - this.each(function(value, index) { - results.push(iterator(value, index)); - }); - return results; - }, - - detect: function (iterator) { - var result; - this.each(function(value, index) { - if (iterator(value, index)) { - result = value; - throw _break; - } - }); - return result; - }, - - findAll: function(iterator) { - var results = []; - this.each(function(value, index) { - if (iterator(value, index)) - results.push(value); - }); - return results; - }, - - grep: function(pattern, iterator) { - var results = []; - this.each(function(value, index) { - var stringValue = value.toString(); - if (stringValue.match(pattern)) - results.push((iterator || Prototype.K)(value, index)); - }) - return results; - }, - - include: function(object) { - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw _break; - } - }); - return found; - }, - - inject: function(memo, iterator) { - this.each(function(value, index) { - memo = iterator(memo, value, index); - }); - return memo; - }, - - invoke: function(method) { - var args = $A(arguments).slice(1); - return this.collect(function(value) { - return value[method].apply(value, args); - }); - }, - - max: function(iterator) { - var result; - this.each(function(value, index) { - value = (iterator || Prototype.K)(value, index); - if (value >= (result || value)) - result = value; - }); - return result; - }, - - min: function(iterator) { - var result; - this.each(function(value, index) { - value = (iterator || Prototype.K)(value, index); - if (value <= (result || value)) - result = value; - }); - return result; - }, - - partition: function(iterator) { - var trues = [], falses = []; - this.each(function(value, index) { - ((iterator || Prototype.K)(value, index) ? - trues : falses).push(value); - }); - return [trues, falses]; - }, - - pluck: function(property) { - var results = []; - this.each(function(value, index) { - results.push(value[property]); - }); - return results; - }, - - reject: function(iterator) { - var results = []; - this.each(function(value, index) { - if (!iterator(value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator) { - return this.collect(function(value, index) { - return {value: value, criteria: iterator(value, index)}; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - }, - - toArray: function() { - return this.collect(Prototype.K); - }, - - zip: function() { - var iterator = Prototype.K, args = $A(arguments); - if (typeof args.last() == 'function') - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - iterator(value = collections.pluck(index)); - return value; - }); - } -} - -Object.extend(Enumerable, { - map: Enumerable.collect, - find: Enumerable.detect, - select: Enumerable.findAll, - member: Enumerable.include, - entries: Enumerable.toArray -}); - -$A = Array.from = function(iterable) { - var results = []; - for (var i = 0; i < iterable.length; i++) - results.push(iterable[i]); - return results; -} - -Object.extend(Array.prototype, { - _each: function(iterator) { - for (var i = 0; i < this.length; i++) - iterator(this[i]); - }, - - first: function() { - return this[0]; - }, - - last: function() { - return this[this.length - 1]; - } -}); - -Object.extend(Array.prototype, Enumerable); - - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')}, - function() {return new XMLHttpRequest()} - ) || false; - } -} - -Ajax.Base = function() {}; -Ajax.Base.prototype = { - setOptions: function(options) { - this.options = { - method: 'post', - asynchronous: true, - parameters: '' - } - Object.extend(this.options, options || {}); - }, - - responseIsSuccess: function() { - return this.transport.status == undefined - || this.transport.status == 0 - || (this.transport.status >= 200 && this.transport.status < 300); - }, - - responseIsFailure: function() { - return !this.responseIsSuccess(); - } -} - -Ajax.Request = Class.create(); -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - -Ajax.Request.prototype = Object.extend(new Ajax.Base(), { - initialize: function(url, options) { - this.transport = Ajax.getTransport(); - this.setOptions(options); - this.request(url); - }, - - request: function(url) { - var parameters = this.options.parameters || ''; - if (parameters.length > 0) parameters += '&_='; - - try { - if (this.options.method == 'get') - url += '?' + parameters; - - this.transport.open(this.options.method, url, - this.options.asynchronous); - - if (this.options.asynchronous) { - this.transport.onreadystatechange = this.onStateChange.bind(this); - setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); - } - - this.setRequestHeaders(); - - var body = this.options.postBody ? this.options.postBody : parameters; - this.transport.send(this.options.method == 'post' ? body : null); - - } catch (e) { - } - }, - - setRequestHeaders: function() { - var requestHeaders = - ['X-Requested-With', 'XMLHttpRequest', - 'X-Prototype-Version', Prototype.Version]; - - if (this.options.method == 'post') { - requestHeaders.push('Content-type', - 'application/x-www-form-urlencoded'); - - /* Force "Connection: close" for Mozilla browsers to work around - * a bug where XMLHttpReqeuest sends an incorrect Content-length - * header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType) - requestHeaders.push('Connection', 'close'); - } - - if (this.options.requestHeaders) - requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); - - for (var i = 0; i < requestHeaders.length; i += 2) - this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState != 1) - this.respondToReadyState(this.transport.readyState); - }, - - respondToReadyState: function(readyState) { - var event = Ajax.Request.Events[readyState]; - - if (event == 'Complete') - (this.options['on' + this.transport.status] - || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(this.transport); - - (this.options['on' + event] || Prototype.emptyFunction)(this.transport); - - /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ - if (event == 'Complete') - this.transport.onreadystatechange = Prototype.emptyFunction; - } -}); - -Ajax.Updater = Class.create(); -Ajax.Updater.ScriptFragment = '(?:)((\n|.)*?)(?:<\/script>)'; - -Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { - initialize: function(container, url, options) { - this.containers = { - success: container.success ? $(container.success) : $(container), - failure: container.failure ? $(container.failure) : - (container.success ? null : $(container)) - } - - this.transport = Ajax.getTransport(); - this.setOptions(options); - - var onComplete = this.options.onComplete || Prototype.emptyFunction; - this.options.onComplete = (function() { - this.updateContent(); - onComplete(this.transport); - }).bind(this); - - this.request(url); - }, - - updateContent: function() { - var receiver = this.responseIsSuccess() ? - this.containers.success : this.containers.failure; - - var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); - var response = this.transport.responseText.replace(match, ''); - var scripts = this.transport.responseText.match(match); - - if (receiver) { - if (this.options.insertion) { - new this.options.insertion(receiver, response); - } else { - receiver.innerHTML = response; - } - } - - if (this.responseIsSuccess()) { - if (this.onComplete) - setTimeout((function() {this.onComplete( - this.transport)}).bind(this), 10); - } - - if (this.options.evalScripts && scripts) { - match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); - setTimeout((function() { - for (var i = 0; i < scripts.length; i++) - eval(scripts[i].match(match)[1]); - }).bind(this), 10); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(); -Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { - initialize: function(container, url, options) { - this.setOptions(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = 1; - - this.updater = {}; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Ajax.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(request) { - if (this.options.decay) { - this.decay = (request.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = request.responseText; - } - this.timer = setTimeout(this.onTimerEvent.bind(this), - this.decay * this.frequency * 1000); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); - -document.getElementsByClassName = function(className) { - var children = document.getElementsByTagName('*') || document.all; - var elements = new Array(); - - for (var i = 0; i < children.length; i++) { - var child = children[i]; - var classNames = child.className.split(' '); - for (var j = 0; j < classNames.length; j++) { - if (classNames[j] == className) { - elements.push(child); - break; - } - } - } - - return elements; -} - -/*--------------------------------------------------------------------------*/ - -if (!window.Element) { - var Element = new Object(); -} - -Object.extend(Element, { - toggle: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = - (element.style.display == 'none' ? '' : 'none'); - } - }, - - hide: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = 'none'; - } - }, - - show: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = ''; - } - }, - - remove: function(element) { - element = $(element); - element.parentNode.removeChild(element); - }, - - getHeight: function(element) { - element = $(element); - return element.offsetHeight; - }, - - hasClassName: function(element, className) { - element = $(element); - if (!element) - return; - var a = element.className.split(' '); - for (var i = 0; i < a.length; i++) { - if (a[i] == className) - return true; - } - return false; - }, - - addClassName: function(element, className) { - element = $(element); - Element.removeClassName(element, className); - element.className += ' ' + className; - }, - - removeClassName: function(element, className) { - element = $(element); - if (!element) - return; - var newClassName = ''; - var a = element.className.split(' '); - for (var i = 0; i < a.length; i++) { - if (a[i] != className) { - if (i > 0) - newClassName += ' '; - newClassName += a[i]; - } - } - element.className = newClassName; - }, - - // removes whitespace-only text node children - cleanWhitespace: function(element) { - var element = $(element); - for (var i = 0; i < element.childNodes.length; i++) { - var node = element.childNodes[i]; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) - Element.remove(node); - } - } -}); - -var Toggle = new Object(); -Toggle.display = Element.toggle; - -/*--------------------------------------------------------------------------*/ - -Abstract.Insertion = function(adjacency) { - this.adjacency = adjacency; -} - -Abstract.Insertion.prototype = { - initialize: function(element, content) { - this.element = $(element); - this.content = content; - - if (this.adjacency && this.element.insertAdjacentHTML) { - this.element.insertAdjacentHTML(this.adjacency, this.content); - } else { - this.range = this.element.ownerDocument.createRange(); - if (this.initializeRange) this.initializeRange(); - this.fragment = this.range.createContextualFragment(this.content); - this.insertContent(); - } - } -} - -var Insertion = new Object(); - -Insertion.Before = Class.create(); -Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { - initializeRange: function() { - this.range.setStartBefore(this.element); - }, - - insertContent: function() { - this.element.parentNode.insertBefore(this.fragment, this.element); - } -}); - -Insertion.Top = Class.create(); -Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { - initializeRange: function() { - this.range.selectNodeContents(this.element); - this.range.collapse(true); - }, - - insertContent: function() { - this.element.insertBefore(this.fragment, this.element.firstChild); - } -}); - -Insertion.Bottom = Class.create(); -Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { - initializeRange: function() { - this.range.selectNodeContents(this.element); - this.range.collapse(this.element); - }, - - insertContent: function() { - this.element.appendChild(this.fragment); - } -}); - -Insertion.After = Class.create(); -Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { - initializeRange: function() { - this.range.setStartAfter(this.element); - }, - - insertContent: function() { - this.element.parentNode.insertBefore(this.fragment, - this.element.nextSibling); - } -}); - -var Field = { - clear: function() { - for (var i = 0; i < arguments.length; i++) - $(arguments[i]).value = ''; - }, - - focus: function(element) { - $(element).focus(); - }, - - present: function() { - for (var i = 0; i < arguments.length; i++) - if ($(arguments[i]).value == '') return false; - return true; - }, - - select: function(element) { - $(element).select(); - }, - - activate: function(element) { - $(element).focus(); - $(element).select(); - } -} - -/*--------------------------------------------------------------------------*/ - -var Form = { - serialize: function(form) { - var elements = Form.getElements($(form)); - var queryComponents = new Array(); - - for (var i = 0; i < elements.length; i++) { - var queryComponent = Form.Element.serialize(elements[i]); - if (queryComponent) - queryComponents.push(queryComponent); - } - - return queryComponents.join('&'); - }, - - getElements: function(form) { - var form = $(form); - var elements = new Array(); - - for (tagName in Form.Element.Serializers) { - var tagElements = form.getElementsByTagName(tagName); - for (var j = 0; j < tagElements.length; j++) - elements.push(tagElements[j]); - } - return elements; - }, - - getInputs: function(form, typeName, name) { - var form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) - return inputs; - - var matchingInputs = new Array(); - for (var i = 0; i < inputs.length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || - (name && input.name != name)) - continue; - matchingInputs.push(input); - } - - return matchingInputs; - }, - - disable: function(form) { - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - element.blur(); - element.disabled = 'true'; - } - }, - - enable: function(form) { - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - element.disabled = ''; - } - }, - - focusFirstElement: function(form) { - var form = $(form); - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - if (element.type != 'hidden' && !element.disabled) { - Field.activate(element); - break; - } - } - }, - - reset: function(form) { - $(form).reset(); - } -} - -Form.Element = { - serialize: function(element) { - var element = $(element); - var method = element.tagName.toLowerCase(); - var parameter = Form.Element.Serializers[method](element); - - if (parameter) - return encodeURIComponent(parameter[0]) + '=' + - encodeURIComponent(parameter[1]); - }, - - getValue: function(element) { - var element = $(element); - var method = element.tagName.toLowerCase(); - var parameter = Form.Element.Serializers[method](element); - - if (parameter) - return parameter[1]; - } -} - -Form.Element.Serializers = { - input: function(element) { - switch (element.type.toLowerCase()) { - case 'submit': - case 'hidden': - case 'password': - case 'text': - return Form.Element.Serializers.textarea(element); - case 'checkbox': - case 'radio': - return Form.Element.Serializers.inputSelector(element); - } - return false; - }, - - inputSelector: function(element) { - if (element.checked) - return [element.name, element.value]; - }, - - textarea: function(element) { - return [element.name, element.value]; - }, - - select: function(element) { - var value = ''; - if (element.type == 'select-one') { - var index = element.selectedIndex; - if (index >= 0) - value = element.options[index].value || element.options[index].text; - } else { - value = new Array(); - for (var i = 0; i < element.length; i++) { - var opt = element.options[i]; - if (opt.selected) - value.push(opt.value || opt.text); - } - } - return [element.name, value]; - } -} - -/*--------------------------------------------------------------------------*/ - -var $F = Form.Element.getValue; - -/*--------------------------------------------------------------------------*/ - -Abstract.TimedObserver = function() {} -Abstract.TimedObserver.prototype = { - initialize: function(element, frequency, callback) { - this.frequency = frequency; - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - this.registerCallback(); - }, - - registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - onTimerEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - } -} - -Form.Element.Observer = Class.create(); -Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(); -Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = function() {} -Abstract.EventObserver.prototype = { - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - var elements = Form.getElements(this.element); - for (var i = 0; i < elements.length; i++) - this.registerCallback(elements[i]); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - element.target = this; - element.prev_onclick = element.onclick || Prototype.emptyFunction; - element.onclick = function() { - this.prev_onclick(); - this.target.onElementEvent(); - } - break; - case 'password': - case 'text': - case 'textarea': - case 'select-one': - case 'select-multiple': - element.target = this; - element.prev_onchange = element.onchange || Prototype.emptyFunction; - element.onchange = function() { - this.prev_onchange(); - this.target.onElementEvent(); - } - break; - } - } - } -} - -Form.Element.EventObserver = Class.create(); -Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(); -Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { - getValue: function() { - return Form.serialize(this.element); - } -}); - - -if (!window.Event) { - var Event = new Object(); -} - -Object.extend(Event, { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - - element: function(event) { - return event.target || event.srcElement; - }, - - isLeftClick: function(event) { - return (((event.which) && (event.which == 1)) || - ((event.button) && (event.button == 1))); - }, - - pointerX: function(event) { - return event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)); - }, - - pointerY: function(event) { - return event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)); - }, - - stop: function(event) { - if (event.preventDefault) { - event.preventDefault(); - event.stopPropagation(); - } else { - event.returnValue = false; - } - }, - - // find the first node with the given tagName, starting from the - // node the event was triggered on; traverses the DOM upwards - findElement: function(event, tagName) { - var element = Event.element(event); - while (element.parentNode && (!element.tagName || - (element.tagName.toUpperCase() != tagName.toUpperCase()))) - element = element.parentNode; - return element; - }, - - observers: false, - - _observeAndCache: function(element, name, observer, useCapture) { - if (!this.observers) this.observers = []; - if (element.addEventListener) { - this.observers.push([element, name, observer, useCapture]); - element.addEventListener(name, observer, useCapture); - } else if (element.attachEvent) { - this.observers.push([element, name, observer, useCapture]); - element.attachEvent('on' + name, observer); - } - }, - - unloadCache: function() { - if (!Event.observers) return; - for (var i = 0; i < Event.observers.length; i++) { - Event.stopObserving.apply(this, Event.observers[i]); - Event.observers[i][0] = null; - } - Event.observers = false; - }, - - observe: function(element, name, observer, useCapture) { - var element = $(element); - useCapture = useCapture || false; - - if (name == 'keypress' && - ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - || element.attachEvent)) - name = 'keydown'; - - this._observeAndCache(element, name, observer, useCapture); - }, - - stopObserving: function(element, name, observer, useCapture) { - var element = $(element); - useCapture = useCapture || false; - - if (name == 'keypress' && - ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - || element.detachEvent)) - name = 'keydown'; - - if (element.removeEventListener) { - element.removeEventListener(name, observer, useCapture); - } else if (element.detachEvent) { - element.detachEvent('on' + name, observer); - } - } -}); - -/* prevent memory leaks in IE */ -Event.observe(window, 'unload', Event.unloadCache, false); - -var Position = { - - // set to true if needed, warning: firefox performance problems - // NOT neeeded for page scrolling, only if draggable contained in - // scrollable elements - includeScrollOffsets: false, - - // must be called before calling withinIncludingScrolloffset, every time the - // page is scrolled - prepare: function() { - this.deltaX = window.pageXOffset - || document.documentElement.scrollLeft - || document.body.scrollLeft - || 0; - this.deltaY = window.pageYOffset - || document.documentElement.scrollTop - || document.body.scrollTop - || 0; - }, - - realOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return [valueL, valueT]; - }, - - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return [valueL, valueT]; - }, - - // caches x/y coordinate pair to use with overlap - within: function(element, x, y) { - if (this.includeScrollOffsets) - return this.withinIncludingScrolloffsets(element, x, y); - this.xcomp = x; - this.ycomp = y; - this.offset = this.cumulativeOffset(element); - - return (y >= this.offset[1] && - y < this.offset[1] + element.offsetHeight && - x >= this.offset[0] && - x < this.offset[0] + element.offsetWidth); - }, - - withinIncludingScrolloffsets: function(element, x, y) { - var offsetcache = this.realOffset(element); - - this.xcomp = x + offsetcache[0] - this.deltaX; - this.ycomp = y + offsetcache[1] - this.deltaY; - this.offset = this.cumulativeOffset(element); - - return (this.ycomp >= this.offset[1] && - this.ycomp < this.offset[1] + element.offsetHeight && - this.xcomp >= this.offset[0] && - this.xcomp < this.offset[0] + element.offsetWidth); - }, - - // within must be called directly before - overlap: function(mode, element) { - if (!mode) return 0; - if (mode == 'vertical') - return ((this.offset[1] + element.offsetHeight) - this.ycomp) / - element.offsetHeight; - if (mode == 'horizontal') - return ((this.offset[0] + element.offsetWidth) - this.xcomp) / - element.offsetWidth; - }, - - clone: function(source, target) { - source = $(source); - target = $(target); - target.style.position = 'absolute'; - var offsets = this.cumulativeOffset(source); - target.style.top = offsets[1] + 'px'; - target.style.left = offsets[0] + 'px'; - target.style.width = source.offsetWidth + 'px'; - target.style.height = source.offsetHeight + 'px'; - } -} diff --git a/docs/skin/screen.css b/docs/skin/screen.css deleted file mode 100644 index c6084f81df3..00000000000 --- a/docs/skin/screen.css +++ /dev/null @@ -1,587 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -body { margin: 0px 0px 0px 0px; font-family: Verdana, Helvetica, sans-serif; } - -h1 { font-size : 160%; margin: 0px 0px 0px 0px; padding: 0px; } -h2 { font-size : 140%; margin: 1em 0px 0.8em 0px; padding: 0px; font-weight : bold;} -h3 { font-size : 130%; margin: 0.8em 0px 0px 0px; padding: 0px; font-weight : bold; } -.h3 { margin: 22px 0px 3px 0px; } -h4 { font-size : 120%; margin: 0.7em 0px 0px 0px; padding: 0px; font-weight : normal; text-align: left; } -.h4 { margin: 18px 0px 0px 0px; } -h4.faq { font-size : 120%; margin: 18px 0px 0px 0px; padding: 0px; font-weight : bold; text-align: left; } -h5 { font-size : 100%; margin: 14px 0px 0px 0px; padding: 0px; font-weight : normal; text-align: left; } - -/** -* table -*/ -table .title { background-color: #000000; } -.ForrestTable { - color: #ffffff; - background-color: #7099C5; - width: 100%; - font-size : 100%; - empty-cells: show; -} -table caption { - padding-left: 5px; - color: white; - text-align: left; - font-weight: bold; - background-color: #000000; -} -.ForrestTable td { - color: black; - background-color: #f0f0ff; -} -.ForrestTable th { text-align: center; } -/** - * Page Header - */ - -#top { - position: relative; - float: left; - width: 100%; - background: #294563; /* if you want a background in the header, put it here */ -} - -#top .breadtrail { - background: #CFDCED; - color: black; - border-bottom: solid 1px white; - padding: 3px 10px; - font-size: 75%; -} -#top .breadtrail a { color: black; } - -#top .header { - float: left; - width: 100%; - background: url("images/header_white_line.gif") repeat-x bottom; -} - -#top .grouplogo { - padding: 7px 0 10px 10px; - float: left; - text-align: left; -} -#top .projectlogo { - padding: 7px 0 10px 10px; - float: left; - width: 33%; - text-align: right; -} -#top .projectlogoA1 { - padding: 7px 0 10px 10px; - float: right; -} -html>body #top .searchbox { - bottom: 0px; -} -#top .searchbox { - position: absolute; - right: 10px; - height: 42px; - font-size: 70%; - white-space: nowrap; - text-align: right; - color: white; - background-color: #000000; - z-index:0; - background-image: url(images/rc-t-l-5-1header-2searchbox-3searchbox.png); - background-repeat: no-repeat; - background-position: top left; - bottom: -1px; /* compensate for IE rendering issue */ -} - -#top .searchbox form { - padding: 5px 10px; - margin: 0; -} -#top .searchbox p { - padding: 0 0 2px 0; - margin: 0; -} -#top .searchbox input { - font-size: 100%; -} - -#tabs { - clear: both; - padding-left: 10px; - margin: 0; - list-style: none; -} -/* background: #CFDCED url("images/tab-right.gif") no-repeat right top;*/ -#tabs li { - float: left; - background-image: url(images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png); - background-repeat: no-repeat; - background-position: top right; - background-color: #000000; - margin: 0 3px 0 0; - padding: 0; -} - -/*background: url("images/tab-left.gif") no-repeat left top;*/ -#tabs li a { - float: left; - display: block; - font-family: verdana, arial, sans-serif; - text-decoration: none; - color: black; - white-space: nowrap; - background-image: url(images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png); - background-repeat: no-repeat; - background-position: top left; - padding: 5px 15px 4px; - width: .1em; /* IE/Win fix */ -} - -#tabs li a:hover { - - cursor: pointer; - text-decoration:underline; -} - -#tabs > li a { width: auto; } /* Rest of IE/Win fix */ - -/* Commented Backslash Hack hides rule from IE5-Mac \*/ -#tabs a { float: none; } -/* End IE5-Mac hack */ - -#top .header .current { - background-color: #4C6C8F; - background-image: url(images/rc-t-r-5-1header-2tab-selected-3tab-selected.png); - background-repeat: no-repeat; - background-position: top right; -} -#top .header .current a { - font-weight: bold; - padding-bottom: 5px; - color: white; - background-image: url(images/rc-t-l-5-1header-2tab-selected-3tab-selected.png); - background-repeat: no-repeat; - background-position: top left; -} -#publishedStrip { - padding-right: 10px; - padding-left: 20px; - padding-top: 3px; - padding-bottom:3px; - color: #ffffff; - font-size : 60%; - font-weight: bold; - background-color: #4C6C8F; - text-align:right; -} - -#level2tabs { -margin: 0; -float:left; -position:relative; - -} - - - -#level2tabs a:hover { - - cursor: pointer; - text-decoration:underline; - -} - -#level2tabs a{ - - cursor: pointer; - text-decoration:none; - background-image: url('images/chapter.gif'); - background-repeat: no-repeat; - background-position: center left; - padding-left: 6px; - margin-left: 6px; -} - -/* -* border-top: solid #4C6C8F 15px; -*/ -#main { - position: relative; - background: white; - clear:both; -} -#main .breadtrail { - clear:both; - position: relative; - background: #CFDCED; - color: black; - border-bottom: solid 1px black; - border-top: solid 1px black; - padding: 0px 180px; - font-size: 75%; - z-index:10; -} -/** -* Round corner -*/ -#roundtop { - background-image: url(images/rc-t-r-15-1body-2menu-3menu.png); - background-repeat: no-repeat; - background-position: top right; -} - -#roundbottom { - background-image: url(images/rc-b-r-15-1body-2menu-3menu.png); - background-repeat: no-repeat; - background-position: top right; -} - -img.corner { - width: 15px; - height: 15px; - border: none; - display: block !important; -} - -.roundtopsmall { - background-image: url(images/rc-t-r-5-1header-2searchbox-3searchbox.png); - background-repeat: no-repeat; - background-position: top right; -} - -#roundbottomsmall { - background-image: url(images/rc-b-r-5-1header-2tab-selected-3tab-selected.png); - background-repeat: no-repeat; - background-position: top right; -} - -img.cornersmall { - width: 5px; - height: 5px; - border: none; - display: block !important; -} -/** - * Side menu - */ -#menu a { font-weight: normal; text-decoration: none;} -#menu a:visited { font-weight: normal; } -#menu a:active { font-weight: normal; } -#menu a:hover { font-weight: normal; text-decoration:underline;} - -#menuarea { width:10em;} -#menu { - position: relative; - float: left; - width: 160px; - padding-top: 0px; - top:-18px; - left:10px; - z-index: 20; - background-color: #f90; - font-size : 70%; - -} - -.menutitle { - cursor:pointer; - padding: 3px 12px; - margin-left: 10px; - background-image: url('images/chapter.gif'); - background-repeat: no-repeat; - background-position: center left; - font-weight : bold; - - -} - -.menutitle:hover{text-decoration:underline;cursor: pointer;} - -#menu .menuitemgroup { - margin: 0px 0px 6px 8px; - padding: 0px; - font-weight : bold; } - -#menu .selectedmenuitemgroup{ - margin: 0px 0px 0px 8px; - padding: 0px; - font-weight : normal; - - } - -#menu .menuitem { - padding: 2px 0px 1px 13px; - background-image: url('images/page.gif'); - background-repeat: no-repeat; - background-position: center left; - font-weight : normal; - margin-left: 10px; -} - -#menu .menupage { - margin: 2px 0px 1px 10px; - padding: 0px 3px 0px 12px; - background-image: url('images/page.gif'); - background-repeat: no-repeat; - background-position: center left; - font-style : normal; -} -#menu .menupagetitle { - padding: 0px 0px 0px 1px; - font-style : normal; - border-style: solid; - border-width: 1px; - margin-right: 10px; - -} -#menu .menupageitemgroup { - padding: 3px 0px 4px 6px; - font-style : normal; - border-bottom: 1px solid ; - border-left: 1px solid ; - border-right: 1px solid ; - margin-right: 10px; -} -#menu .menupageitem { - font-style : normal; - font-weight : normal; - border-width: 0px; - font-size : 90%; -} -#menu #credit { - text-align: center; -} -#menu #credit2 { - text-align: center; - padding: 3px 3px 3px 3px; - background-color: #ffffff; -} -#menu .searchbox { - text-align: center; -} -#menu .searchbox form { - padding: 3px 3px; - margin: 0; -} -#menu .searchbox input { - font-size: 100%; -} - -#content { - padding: 20px 20px 20px 180px; - margin: 0; - font : small Verdana, Helvetica, sans-serif; - font-size : 80%; -} - -#content ul { - margin: 0; - padding: 0 25px; -} -#content li { - padding: 0 5px; -} -#feedback { - color: black; - background: #CFDCED; - text-align:center; - margin-top: 5px; -} -#feedback #feedbackto { - font-size: 90%; - color: black; -} -#footer { - clear: both; - position: relative; /* IE bugfix (http://www.dracos.co.uk/web/css/ie6floatbug/) */ - width: 100%; - background: #CFDCED; - border-top: solid 1px #4C6C8F; - color: black; -} -#footer .copyright { - position: relative; /* IE bugfix cont'd */ - padding: 5px; - margin: 0; - width: 45%; -} -#footer .lastmodified { - position: relative; /* IE bugfix cont'd */ - float: right; - width: 45%; - padding: 5px; - margin: 0; - text-align: right; -} -#footer a { color: white; } - -#footer #logos { - text-align: left; -} - - -/** - * Misc Styles - */ - -acronym { cursor: help; } -.boxed { background-color: #a5b6c6;} -.underlined_5 {border-bottom: solid 5px #4C6C8F;} -.underlined_10 {border-bottom: solid 10px #4C6C8F;} -/* ==================== snail trail ============================ */ - -.trail { - position: relative; /* IE bugfix cont'd */ - font-size: 70%; - text-align: right; - float: right; - margin: -10px 5px 0px 5px; - padding: 0; -} - -#motd-area { - position: relative; /* IE bugfix cont'd */ - float: right; - width: 35%; - background-color: #f0f0ff; - border-top: solid 1px #4C6C8F; - border-bottom: solid 1px #4C6C8F; - margin-bottom: 15px; - margin-left: 15px; - margin-right: 10%; - padding-bottom: 5px; - padding-top: 5px; -} - -#minitoc-area { - border-top: solid 1px #4C6C8F; - border-bottom: solid 1px #4C6C8F; - margin: 15px 10% 5px 15px; - /* margin-bottom: 15px; - margin-left: 15px; - margin-right: 10%;*/ - padding-bottom: 7px; - padding-top: 5px; -} -.minitoc { - list-style-image: url('images/current.gif'); - font-weight: normal; -} - -li p { - margin: 0; - padding: 0; -} - -.pdflink { - position: relative; /* IE bugfix cont'd */ - float: right; - margin: 0px 5px; - padding: 0; -} -.pdflink br { - margin-top: -10px; - padding-left: 1px; -} -.pdflink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} - -.pdflink img { - display: block; - height: 16px; - width: 16px; -} -.xmllink { - position: relative; /* IE bugfix cont'd */ - float: right; - margin: 0px 5px; - padding: 0; -} -.xmllink br { - margin-top: -10px; - padding-left: 1px; -} -.xmllink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} - -.xmllink img { - display: block; - height: 16px; - width: 16px; -} -.podlink { - position: relative; /* IE bugfix cont'd */ - float: right; - margin: 0px 5px; - padding: 0; -} -.podlink br { - margin-top: -10px; - padding-left: 1px; -} -.podlink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} - -.podlink img { - display: block; - height: 16px; - width: 16px; -} - -.printlink { - position: relative; /* IE bugfix cont'd */ - float: right; -} -.printlink br { - margin-top: -10px; - padding-left: 1px; -} -.printlink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} -.printlink img { - display: block; - height: 16px; - width: 16px; -} - -p.instruction { - display: list-item; - list-style-image: url('../images/instruction_arrow.png'); - list-style-position: outside; - margin-left: 2em; -} \ No newline at end of file diff --git a/docs/zookeeperAdmin.html b/docs/zookeeperAdmin.html deleted file mode 100644 index 438a66eaf6c..00000000000 --- a/docs/zookeeperAdmin.html +++ /dev/null @@ -1,1682 +0,0 @@ - - - - - - - -ZooKeeper Administrator's Guide - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper Administrator's Guide

-

A Guide to Deployment and Administration

- - - - - - - - - -

Deployment

-
-

This section contains information about deploying Zookeeper and - covers these topics:

- -

The first two sections assume you are interested in installing - ZooKeeper in a production environment such as a datacenter. The final - section covers situations in which you are setting up ZooKeeper on a - limited basis - for evaluation, testing, or development - but not in a - production environment.

- -

System Requirements

- -

Supported Platforms

-
    - -
  • - -

    GNU/Linux is supported as a development and production - platform for both server and client.

    - -
  • - -
  • - -

    Sun Solaris is supported as a development and production - platform for both server and client.

    - -
  • - -
  • - -

    FreeBSD is supported as a development and production - platform for clients only. Java NIO selector support in - the FreeBSD JVM is broken.

    - -
  • - -
  • - -

    Win32 is supported as a development - platform only for both server and client.

    - -
  • - -
  • - -

    MacOSX is supported as a development - platform only for both server and client.

    - -
  • - -
- -

Required Software

-

ZooKeeper runs in Java, release 1.6 or greater (JDK 6 or - greater). It runs as an ensemble of - ZooKeeper servers. Three ZooKeeper servers is the minimum - recommended size for an ensemble, and we also recommend that - they run on separate machines. At Yahoo!, ZooKeeper is - usually deployed on dedicated RHEL boxes, with dual-core - processors, 2GB of RAM, and 80GB IDE hard drives.

- -

Clustered (Multi-Server) Setup

-

For reliable ZooKeeper service, you should deploy ZooKeeper in a - cluster known as an ensemble. As long as a majority - of the ensemble are up, the service will be available. Because Zookeeper - requires a majority, it is best to use an - odd number of machines. For example, with four machines ZooKeeper can - only handle the failure of a single machine; if two machines fail, the - remaining two machines do not constitute a majority. However, with five - machines ZooKeeper can handle the failure of two machines.

-

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

-
    - -
  1. - -

    Install the Java JDK. You can use the native packaging system - for your system, or download the JDK from:

    - - -

    -http://java.sun.com/javase/downloads/index.jsp -

    - -
  2. - - -
  3. - -

    Set the Java heap size. This is very important to avoid - swapping, which will seriously degrade ZooKeeper performance. To - determine the correct value, use load tests, and make sure you are - well below the usage limit that would cause you to swap. Be - conservative - use a maximum heap size of 3GB for a 4GB - machine.

    - -
  4. - - -
  5. - -

    Install the ZooKeeper Server Package. It can be downloaded - from: -

    - -

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

    - -
  6. - - -
  7. - -

    Create a configuration file. This file can be called anything. - Use the following settings as a starting point:

    - - -
    -tickTime=2000
    -dataDir=/var/zookeeper/
    -clientPort=2181
    -initLimit=5
    -syncLimit=2
    -server.1=zoo1:2888:3888
    -server.2=zoo2:2888:3888
    -server.3=zoo3:2888:3888
    - - -

    You can find the meanings of these and other configuration - settings in the section Configuration Parameters. A word - though about a few here:

    - - -

    Every machine that is part of the ZooKeeper ensemble should know - about every other machine in the ensemble. You accomplish this with - the series of lines of the form server.id=host:port:port. The parameters host and port are straightforward. You attribute the - server id to each machine by creating a file named - myid, one for each server, which resides in - that server's data directory, as specified by the configuration file - parameter dataDir.

    -
  8. - - -
  9. -

    The myid file - consists of a single line containing only the text of that machine's - id. So myid of server 1 would contain the text - "1" and nothing else. The id must be unique within the - ensemble and should have a value between 1 and 255.

    - -
  10. - - -
  11. - -

    If your configuration file is set up, you can start a - ZooKeeper server:

    - - -

    -$ java -cp zookeeper.jar:lib/log4j-1.2.15.jar:conf \ - org.apache.zookeeper.server.quorum.QuorumPeerMain zoo.cfg - -

    - - -

    QuorumPeerMain starts a ZooKeeper server, - JMX - management beans are also registered which allows - management through a JMX management console. - The ZooKeeper JMX - document contains details on managing ZooKeeper with JMX. -

    - - -

    See the script bin/zkServer.sh, - which is included in the release, for an example - of starting server instances.

    - - -
  12. - - -
  13. - -

    Test your deployment by connecting to the hosts:

    - - -
      - -
    • - -

      In Java, you can run the following command to execute - simple operations:

      - - -

      -$ java -cp zookeeper.jar:src/java/lib/log4j-1.2.15.jar:conf:src/java/lib/jline-0.9.94.jar \ - org.apache.zookeeper.ZooKeeperMain -server 127.0.0.1:2181 -

      - -
    • - - -
    • - -

      In C, you can compile either the single threaded client or - the multithreaded client: or n the c subdirectory in the - ZooKeeper sources. This compiles the single threaded - client:

      - - -

      -$ make cli_st -

      - - -

      And this compiles the mulithreaded client:

      - - -

      -$ make cli_mt -

      - -
    • - -
    - - -

    Running either program gives you a shell in which to execute - simple file-system-like operations. To connect to ZooKeeper with the - multithreaded client, for example, you would run:

    - - -

    -$ cli_mt 127.0.0.1:2181 -

    - -
  14. - -
- -

Single Server and Developer Setup

-

If you want to setup ZooKeeper for development purposes, you will - probably want to setup a single server instance of ZooKeeper, and then - install either the Java or C client-side libraries and bindings on your - development machine.

-

The steps to setting up a single server instance are the similar - to the above, except the configuration file is simpler. You can find the - complete instructions in the Installing and - Running ZooKeeper in Single Server Mode section of the ZooKeeper Getting Started - Guide.

-

For information on installing the client side libraries, refer to - the Bindings - section of the ZooKeeper - Programmer's Guide.

-
- - - -

Administration

-
-

This section contains information about running and maintaining - ZooKeeper and covers these topics:

- - -

Designing a ZooKeeper Deployment

-

The reliablity of ZooKeeper rests on two basic assumptions.

-
    - -
  1. -

    Only a minority of servers in a deployment - will fail. Failure in this context - means a machine crash, or some error in the network that - partitions a server off from the majority.

    - -
  2. - -
  3. -

    Deployed machines operate correctly. To - operate correctly means to execute code correctly, to have - clocks that work properly, and to have storage and network - components that perform consistently.

    - -
  4. - -
-

The sections below contain considerations for ZooKeeper - administrators to maximize the probability for these assumptions - to hold true. Some of these are cross-machines considerations, - and others are things you should consider for each and every - machine in your deployment.

- -

Cross Machine Requirements

-

For the ZooKeeper service to be active, there must be a - majority of non-failing machines that can communicate with - each other. To create a deployment that can tolerate the - failure of F machines, you should count on deploying 2xF+1 - machines. Thus, a deployment that consists of three machines - can handle one failure, and a deployment of five machines can - handle two failures. Note that a deployment of six machines - can only handle two failures since three machines is not a - majority. For this reason, ZooKeeper deployments are usually - made up of an odd number of machines.

-

To achieve the highest probability of tolerating a failure - you should try to make machine failures independent. For - example, if most of the machines share the same switch, - failure of that switch could cause a correlated failure and - bring down the service. The same holds true of shared power - circuits, cooling systems, etc.

- -

Single Machine Requirements

-

If ZooKeeper has to contend with other applications for - access to resourses like storage media, CPU, network, or - memory, its performance will suffer markedly. ZooKeeper has - strong durability guarantees, which means it uses storage - media to log changes before the operation responsible for the - change is allowed to complete. You should be aware of this - dependency then, and take great care if you want to ensure - that ZooKeeper operations aren’t held up by your media. Here - are some things you can do to minimize that sort of - degradation: -

-
    - -
  • - -

    ZooKeeper's transaction log must be on a dedicated - device. (A dedicated partition is not enough.) ZooKeeper - writes the log sequentially, without seeking Sharing your - log device with other processes can cause seeks and - contention, which in turn can cause multi-second - delays.

    - -
  • - - -
  • - -

    Do not put ZooKeeper in a situation that can cause a - swap. In order for ZooKeeper to function with any sort of - timeliness, it simply cannot be allowed to swap. - Therefore, make certain that the maximum heap size given - to ZooKeeper is not bigger than the amount of real memory - available to ZooKeeper. For more on this, see - Things to Avoid - below.

    - -
  • - -
- -

Provisioning

-

- -

Things to Consider: ZooKeeper Strengths and Limitations

-

- -

Administering

-

- -

Maintenance

-

Little long term maintenance is required for a ZooKeeper - cluster however you must be aware of the following:

- -

Ongoing Data Directory Cleanup

-

The ZooKeeper Data - Directory contains files which are a persistent copy - of the znodes stored by a particular serving ensemble. These - are the snapshot and transactional log files. As changes are - made to the znodes these changes are appended to a - transaction log, occasionally, when a log grows large, a - snapshot of the current state of all znodes will be written - to the filesystem. This snapshot supercedes all previous - logs. -

-

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

-

The PurgeTxnLog utility implements a simple retention - policy that administrators can use. The API docs contains details on - calling conventions (arguments, etc...). -

-

In the following example the last count snapshots and - their corresponding logs are retained and the others are - deleted. The value of <count> should typically be - greater than 3 (although not required, this provides 3 backups - in the unlikely event a recent log has become corrupted). This - can be run as a cron job on the ZooKeeper server machines to - clean up the logs daily.

-
 java -cp zookeeper.jar:log4j.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>
- -

Debug Log Cleanup (log4j)

-

See the section on logging in this document. It is - expected that you will setup a rolling file appender using the - in-built log4j feature. The sample configuration file in the - release tar's conf/log4j.properties provides an example of - this. -

- -

Supervision

-

You will want to have a supervisory process that manages - each of your ZooKeeper server processes (JVM). The ZK server is - designed to be "fail fast" meaning that it will shutdown - (process exit) if an error occurs that it cannot recover - from. As a ZooKeeper serving cluster is highly reliable, this - means that while the server may go down the cluster as a whole - is still active and serving requests. Additionally, as the - cluster is "self healing" the failed server once restarted will - automatically rejoin the ensemble w/o any manual - interaction.

-

Having a supervisory process such as daemontools or - SMF - (other options for supervisory process are also available, it's - up to you which one you would like to use, these are just two - examples) managing your ZooKeeper server ensures that if the - process does exit abnormally it will automatically be restarted - and will quickly rejoin the cluster.

- -

Monitoring

-

The ZooKeeper service can be monitored in one of two - primary ways; 1) the command port through the use of 4 letter words and 2) JMX. See the appropriate section for - your environment/requirements.

- -

Logging

-

ZooKeeper uses log4j version 1.2 as - its logging infrastructure. The ZooKeeper default log4j.properties - file resides in the conf directory. Log4j requires that - log4j.properties either be in the working directory - (the directory from which ZooKeeper is run) or be accessible from the classpath.

-

For more information, see - Log4j Default Initialization Procedure - of the log4j manual.

- -

Troubleshooting

-
- -
- Server not coming up because of file corruption -
-
-

A server might not be able to read its database and fail to come up because of - some file corruption in the transaction logs of the ZooKeeper server. You will - see some IOException on loading ZooKeeper database. In such a case, - make sure all the other servers in your ensemble are up and working. Use "stat" - command on the command port to see if they are in good health. After you have verified that - all the other servers of the ensemble are up, you can go ahead and clean the database - of the corrupt server. Delete all the files in datadir/version-2 and datalogdir/version-2/. - Restart the server. -

-
- -
- -

Configuration Parameters

-

ZooKeeper's behavior is governed by the ZooKeeper configuration - file. This file is designed so that the exact same file can be used by - all the servers that make up a ZooKeeper server assuming the disk - layouts are the same. If servers use different configuration files, care - must be taken to ensure that the list of servers in all of the different - configuration files match.

- -

Minimum Configuration

-

Here are the minimum configuration keywords that must be defined - in the configuration file:

-
- -
-clientPort -
-
-

the port to listen for client connections; that is, the - port that clients attempt to connect to.

-
- - -
-dataDir -
-
-

the location where ZooKeeper will store the in-memory - database snapshots and, unless specified otherwise, the - transaction log of updates to the database.

-
-
Note
-
- -

Be careful where you put the transaction log. A - dedicated transaction log device is key to consistent good - performance. Putting the log on a busy device will adversely - effect performance.

- -
-
-
- - -
-tickTime -
-
-

the length of a single tick, which is the basic time unit - used by ZooKeeper, as measured in milliseconds. It is used to - regulate heartbeats, and timeouts. For example, the minimum - session timeout will be two ticks.

-
- -
- -

Advanced Configuration

-

The configuration settings in the section are optional. You can - use them to further fine tune the behaviour of your ZooKeeper servers. - Some can also be set using Java system properties, generally of the - form zookeeper.keyword. The exact system - property, when available, is noted below.

-
- -
-dataLogDir -
-
-

(No Java system property)

-

This option will direct the machine to write the - transaction log to the dataLogDir rather than the dataDir. This allows a dedicated log - device to be used, and helps avoid competition between logging - and snaphots.

-
-
Note
-
- -

Having a dedicated log device has a large impact on - throughput and stable latencies. It is highly recommened to - dedicate a log device and set dataLogDir to point to a directory on - that device, and then make sure to point dataDir to a directory - not residing on that device.

- -
-
-
- - -
-globalOutstandingLimit -
-
-

(Java system property: zookeeper.globalOutstandingLimit.)

-

Clients can submit requests faster than ZooKeeper can - process them, especially if there are a lot of clients. To - prevent ZooKeeper from running out of memory due to queued - requests, ZooKeeper will throttle clients so that there is no - more than globalOutstandingLimit outstanding requests in the - system. The default limit is 1,000.

-
- - -
-preAllocSize -
-
-

(Java system property: zookeeper.preAllocSize)

-

To avoid seeks ZooKeeper allocates space in the - transaction log file in blocks of preAllocSize kilobytes. The - default block size is 64M. One reason for changing the size of - the blocks is to reduce the block size if snapshots are taken - more often. (Also, see snapCount).

-
- - -
-snapCount -
-
-

(Java system property: zookeeper.snapCount)

-

ZooKeeper logs transactions to a transaction - log. After snapCount transactions are written to a log - file a snapshot is started and a new transaction log - file is created. The default snapCount is - 100,000.

-
- - -
-traceFile -
-
-

(Java system property: requestTraceFile)

-

If this option is defined, requests will be will logged to - a trace file named traceFile.year.month.day. Use of this option - provides useful debugging information, but will impact - performance. (Note: The system property has no zookeeper prefix, - and the configuration variable name is different from the system - property. Yes - it's not consistent, and it's annoying.)

-
- - -
-maxClientCnxns -
-
-

(No Java system property)

-

Limits the number of concurrent connections (at the socket - level) that a single client, identified by IP address, may make - to a single member of the ZooKeeper ensemble. This is used to - prevent certain classes of DoS attacks, including file - descriptor exhaustion. The default is 10. Setting this to 0 - entirely removes the limit on concurrent connections.

-
- - -
-clientPortBindAddress -
-
-

-New in 3.3.0: the - address (ipv4, ipv6 or hostname) to listen for client - connections; that is, the address that clients attempt - to connect to. This is optional, by default we bind in - such a way that any connection to the clientPort for any - address/interface/nic on the server will be - accepted.

-
- - -
-minSessionTimeout -
-
-

(No Java system property)

-

-New in 3.3.0: the - minimum session timeout in milliseconds that the server - will allow the client to negotiate. Defaults to 2 times - the tickTime.

-
- - -
-maxSessionTimeout -
-
-

(No Java system property)

-

-New in 3.3.0: the - maximum session timeout in milliseconds that the server - will allow the client to negotiate. Defaults to 20 times - the tickTime.

-
- -
- -

Cluster Options

-

The options in this section are designed for use with an ensemble - of servers -- that is, when deploying clusters of servers.

-
- -
-electionAlg -
-
-

(No Java system property)

-

Election implementation to use. A value of "0" corresponds - to the original UDP-based version, "1" corresponds to the - non-authenticated UDP-based version of fast leader election, "2" - corresponds to the authenticated UDP-based version of fast - leader election, and "3" corresponds to TCP-based version of - fast leader election. Currently, algorithm 3 is the default

-
-
Note
-
- -

The implementations of leader election - 1 and 2 are currently not supported, and we have the intention - of deprecating them in the near future. Implementations 0 and 3 are - currently supported, and we plan to keep supporting them in the near future. - To avoid having to support multiple versions of leader election unecessarily, - we may eventually consider deprecating algorithm 0 as well, but we will plan - according to the needs of the community. -

- -
-
-
- - -
-initLimit -
-
-

(No Java system property)

-

Amount of time, in ticks (see tickTime), to allow followers to - connect and sync to a leader. Increased this value as needed, if - the amount of data managed by ZooKeeper is large.

-
- - -
-leaderServes -
-
-

(Java system property: zookeeper.leaderServes)

-

Leader accepts client connections. Default value is "yes". - The leader machine coordinates updates. For higher update - throughput at thes slight expense of read throughput the leader - can be configured to not accept clients and focus on - coordination. The default to this option is yes, which means - that a leader will accept client connections.

-
-
Note
-
- -

Turning on leader selection is highly recommended when - you have more than three ZooKeeper servers in an ensemble.

- -
-
-
- - -
-server.x=[hostname]:nnnnn[:nnnnn], etc -
-
-

(No Java system property)

-

servers making up the ZooKeeper ensemble. When the server - starts up, it determines which server it is by looking for the - file myid in the data directory. That file - contains the server number, in ASCII, and it should match - x in server.x in the left hand side of this - setting.

-

The list of servers that make up ZooKeeper servers that is - used by the clients must match the list of ZooKeeper servers - that each ZooKeeper server has.

-

There are two port numbers nnnnn. - The first followers use to connect to the leader, and the second is for - leader election. The leader election port is only necessary if electionAlg - is 1, 2, or 3 (default). If electionAlg is 0, then the second port is not - necessary. If you want to test multiple servers on a single machine, then - different ports can be used for each server.

-
- - -
-syncLimit -
-
-

(No Java system property)

-

Amount of time, in ticks (see tickTime), to allow followers to sync - with ZooKeeper. If followers fall too far behind a leader, they - will be dropped.

-
- - -
-group.x=nnnnn[:nnnnn] -
-
-

(No Java system property)

-

Enables a hierarchical quorum construction."x" is a group identifier - and the numbers following the "=" sign correspond to server identifiers. - The left-hand side of the assignment is a colon-separated list of server - identifiers. Note that groups must be disjoint and the union of all groups - must be the ZooKeeper ensemble.

-

You will find an example here - -

-
- - -
-weight.x=nnnnn -
-
-

(No Java system property)

-

Used along with "group", it assigns a weight to a server when - forming quorums. Such a value corresponds to the weight of a server - when voting. There are a few parts of ZooKeeper that require voting - such as leader election and the atomic broadcast protocol. By default - the weight of server is 1. If the configuration defines groups, but not - weights, then a value of 1 will be assigned to all servers. -

-

You will find an example here - -

-
- -
-

- -

Authentication & Authorization Options

-

The options in this section allow control over - authentication/authorization performed by the service.

-
- -
-zookeeper.DigestAuthenticationProvider.superDigest -
-
-

(Java system property only: zookeeper.DigestAuthenticationProvider.superDigest)

-

By default this feature is disabled -

-

-New in 3.2: - Enables a ZooKeeper ensemble administrator to access the - znode hierarchy as a "super" user. In particular no ACL - checking occurs for a user authenticated as - super.

-

org.apache.zookeeper.server.auth.DigestAuthenticationProvider - can be used to generate the superDigest, call it with - one parameter of "super:<password>". Provide the - generated "super:<data>" as the system property value - when starting each server of the ensemble.

-

When authenticating to a ZooKeeper server (from a - ZooKeeper client) pass a scheme of "digest" and authdata - of "super:<password>". Note that digest auth passes - the authdata in plaintext to the server, it would be - prudent to use this authentication method only on - localhost (not over the network) or over an encrypted - connection.

-
- -
- -

Unsafe Options

-

The following options can be useful, but be careful when you use - them. The risk of each is explained along with the explanation of what - the variable does.

-
- -
-forceSync -
-
-

(Java system property: zookeeper.forceSync)

-

Requires updates to be synced to media of the transaction - log before finishing processing the update. If this option is - set to no, ZooKeeper will not require updates to be synced to - the media.

-
- - -
-jute.maxbuffer: -
-
-

(Java system property: - jute.maxbuffer)

-

This option can only be set as a Java system property. - There is no zookeeper prefix on it. It specifies the maximum - size of the data that can be stored in a znode. The default is - 0xfffff, or just under 1M. If this option is changed, the system - property must be set on all servers and clients otherwise - problems will arise. This is really a sanity check. ZooKeeper is - designed to store data on the order of kilobytes in size.

-
- - -
-skipACL -
-
-

(Java system property: zookeeper.skipACL)

-

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

-
- -
- -

ZooKeeper Commands: The Four Letter Words

-

ZooKeeper responds to a small set of commands. Each command is - composed of four letters. You issue the commands to ZooKeeper via telnet - or nc, at the client port.

-

Three of the more interesting commands: "stat" gives some - general information about the server and connected clients, - while "srvr" and "cons" give extended details on server and - connections respectively.

-
- -
-conf -
-
-

-New in 3.3.0: Print - details about serving configuration.

-
- - -
-cons -
-
-

-New in 3.3.0: List - full connection/session details for all clients connected - to this server. Includes information on numbers of packets - received/sent, session id, operation latencies, last - operation performed, etc...

-
- - -
-crst -
-
-

-New in 3.3.0: Reset - connection/session statistics for all connections.

-
- - -
-dump -
-
-

Lists the outstanding sessions and ephemeral nodes. This - only works on the leader.

-
- - -
-envi -
-
-

Print details about serving environment

-
- - -
-ruok -
-
-

Tests if server is running in a non-error state. The server - will respond with imok if it is running. Otherwise it will not - respond at all.

-

A response of "imok" does not necessarily indicate that the - server has joined the quorum, just that the server process is active - and bound to the specified client port. Use "stat" for details on - state wrt quorum and client connection information.

-
- - -
-srst -
-
-

Reset server statistics.

-
- - -
-srvr -
-
-

-New in 3.3.0: Lists - full details for the server.

-
- - -
-stat -
-
-

Lists brief details for the server and connected - clients.

-
- - -
-wchs -
-
-

-New in 3.3.0: Lists - brief information on watches for the server.

-
- - -
-wchc -
-
-

-New in 3.3.0: Lists - detailed information on watches for the server, by - session. This outputs a list of sessions(connections) - with associated watches (paths). Note, depending on the - number of watches this operation may be expensive (ie - impact server performance), use it carefully.

-
- - -
-wchp -
-
-

-New in 3.3.0: Lists - detailed information on watches for the server, by path. - This outputs a list of paths (znodes) with associated - sessions. Note, depending on the number of watches this - operation may be expensive (ie impact server performance), - use it carefully.

-
- -
-

Here's an example of the ruok - command:

-
$ echo ruok | nc 127.0.0.1 5111
-imok
-
- -

Data File Management

-

ZooKeeper stores its data in a data directory and its transaction - log in a transaction log directory. By default these two directories are - the same. The server can (and should) be configured to store the - transaction log files in a separate directory than the data files. - Throughput increases and latency decreases when transaction logs reside - on a dedicated log devices.

- -

The Data Directory

-

This directory has two files in it:

-
    - -
  • - -

    -myid - contains a single integer in - human readable ASCII text that represents the server id.

    - -
  • - - -
  • - -

    -snapshot.<zxid> - holds the fuzzy - snapshot of a data tree.

    - -
  • - -
-

Each ZooKeeper server has a unique id. This id is used in two - places: the myid file and the configuration file. - The myid file identifies the server that - corresponds to the given data directory. The configuration file lists - the contact information for each server identified by its server id. - When a ZooKeeper server instance starts, it reads its id from the - myid file and then, using that id, reads from the - configuration file, looking up the port on which it should - listen.

-

The snapshot files stored in the data - directory are fuzzy snapshots in the sense that during the time the - ZooKeeper server is taking the snapshot, updates are occurring to the - data tree. The suffix of the snapshot file names - is the zxid, the ZooKeeper transaction id, of the - last committed transaction at the start of the snapshot. Thus, the - snapshot includes a subset of the updates to the data tree that - occurred while the snapshot was in process. The snapshot, then, may - not correspond to any data tree that actually existed, and for this - reason we refer to it as a fuzzy snapshot. Still, ZooKeeper can - recover using this snapshot because it takes advantage of the - idempotent nature of its updates. By replaying the transaction log - against fuzzy snapshots ZooKeeper gets the state of the system at the - end of the log.

- -

The Log Directory

-

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

- -

File Management

-

The format of snapshot and log files does not change between - standalone ZooKeeper servers and different configurations of - replicated ZooKeeper servers. Therefore, you can pull these files from - a running replicated ZooKeeper server to a development machine with a - stand-alone ZooKeeper server for trouble shooting.

-

Using older log and snapshot files, you can look at the previous - state of ZooKeeper servers and even restore that state. The - LogFormatter class allows an administrator to look at the transactions - in a log.

-

The ZooKeeper server creates snapshot and log files, but - never deletes them. The retention policy of the data and log - files is implemented outside of the ZooKeeper server. The - server itself only needs the latest complete fuzzy snapshot - and the log files from the start of that snapshot. See the - maintenance section in - this document for more details on setting a retention policy - and maintenance of ZooKeeper storage. -

- -

Things to Avoid

-

Here are some common problems you can avoid by configuring - ZooKeeper correctly:

-
- -
-inconsistent lists of servers -
-
-

The list of ZooKeeper servers used by the clients must match - the list of ZooKeeper servers that each ZooKeeper server has. - Things work okay if the client list is a subset of the real list, - but things will really act strange if clients have a list of - ZooKeeper servers that are in different ZooKeeper clusters. Also, - the server lists in each Zookeeper server configuration file - should be consistent with one another.

-
- - -
-incorrect placement of transasction log -
-
-

The most performance critical part of ZooKeeper is the - transaction log. ZooKeeper syncs transactions to media before it - returns a response. A dedicated transaction log device is key to - consistent good performance. Putting the log on a busy device will - adversely effect performance. If you only have one storage device, - put trace files on NFS and increase the snapshotCount; it doesn't - eliminate the problem, but it should mitigate it.

-
- - -
-incorrect Java heap size -
-
-

You should take special care to set your Java max heap size - correctly. In particular, you should not create a situation in - which ZooKeeper swaps to disk. The disk is death to ZooKeeper. - Everything is ordered, so if processing one request swaps the - disk, all other queued requests will probably do the same. the - disk. DON'T SWAP.

-

Be conservative in your estimates: if you have 4G of RAM, do - not set the Java max heap size to 6G or even 4G. For example, it - is more likely you would use a 3G heap for a 4G machine, as the - operating system and the cache also need memory. The best and only - recommend practice for estimating the heap size your system needs - is to run load tests, and then make sure you are well below the - usage limit that would cause the system to swap.

-
- -
- -

Best Practices

-

For best results, take note of the following list of good - Zookeeper practices:

-

For multi-tennant installations see the section - detailing ZooKeeper "chroot" support, this can be very useful - when deploying many applications/services interfacing to a - single ZooKeeper cluster.

-
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperAdmin.pdf b/docs/zookeeperAdmin.pdf deleted file mode 100644 index 95d47886ec826148a52910a9203988fdc15f8f88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67692 zcmc$`d6%kK`Y!l?ev0FWsLZo7inE9~f*=ZrqB5(uZ}%Vlf$r`8j!i^lR@SfTWZe_h z)vNQYT(P!+#KY#~me}vZkw?^X!vi{#A=La$l zF5z#;A_>oL+evT&FUB(T( zJXx;5Ug=68vf67lWcu_-LU`yR%qawn&MDK@%}kL-S5aw~%Nxl|h+6ShyID~_3aReW zZ!Y~c3pB6mu{~CW&bwM+)-MaKaB}$=Gn;jgdkb5-({b)6F2ku4X&(d!wLxDP4!)uM zV+PlHJiTvhw#6Tnj}--p#<#nvm$?NUCT;r&B>X3ZSVWt-S^EcZL|A{Sl^YxwMC zuz_VKeDYLJlg)k9q2?X5?d69dpU=D_zbept{ZNi=2`RhoZgdkySdYYt7ar|e^~9V&zqVypyxSY8W!_q;WB); zR`qD8bJKY<8Z?KCS6|OYvs~j0n)3~kQ*JLa(*KH)(YnR85MOy{%)+_nQ04ey-0sY^mTqHW({vKRd2yssp|zx z?oW!6=Yy-^Ox0Co{l@XZu6G$C%|3t2Rg-#>9MNWx?&r1z%TMZUgg9(qWq9z$?K?OJ z%!)N&9=Zzf_fBFg2O&BcbBpSx*ec--7Z^^(a4o9)WAoZ0Qh%61}D)oE`tn{3Vg_Rt-75^uMm$(L^9g}!jTMptjE z)>yBj(AW#Tk&RmgI*b&;D9yHKf6>D)t^O@wB8PzWZdNhVc!0?%c=HA>pBN9~b>bvRx`Dr14 zw`{-LO8W0?<(G|Jm{%vYv1Z}~uOC?Y>*a5V=$vn6H~jCZ=|t0iG=~3RIQ+-qH)QEN z!*^mk&mY9srM)nCrQdx1_^`t1SNR|a(-HGI`qM^#j6ZC(_8a^88|>r0%iq!)fByI| z{psifUHaqe68!5D{Oc0@>k|Cy68!5D;_H&s)_;7D__~Dnx`g<;g#5aM{JMnvy5#W3 z*70%8f8BCE@&AUb-q!1L3!2@6$H!yauXHjn6o=6t6o$bRiT%K64kl70B*%S@)9LB` zK4a*|Ty?|b{JH&nK=}NG|G|ACMMmk~_S>Jc^En&e!oj9ENREJs6bFv8F!h6E)15RO z{R#@~_xJpdFg_;f`!E;^#y(&$ILxN6kfe#v@h<`Sokjl$f@E;^je~y0%$k)pGCx(2Rshmw^17s-LU!`>!TlI3MK1SQ2K_MM!YyFOwDjIaxpL!nc7CDS!{M zk|`iIwJAhO+K=(C%n1H-vVK~o?*pN!PjX@@S=p4V1V#KZS@EBf_0u+f9|+0(PF4yg z*&hT!{W4eapL6xoIDa1p{$)im7X6wKjQM4*;y>r==l1Y@AgOu(<>J(@W0Ac;^$ob+#buh5t0H6q6d(bO)w?nXAOl zx%%m+d>=<@;6J#Dr+7Z*Ben0pO4amRA5POhZZw~c()V$s%=n-tjxwKX221VxuTqu# zIaNPhwQmE#*e^4RaWsrk^bd@|ewnJ| We-#?v^-}4LU3#b3B{DPIKJFH9!G@u@X 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

fb(k*QF=ZmK1x0-}_wV#j$ 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 bP@?;OU2l`iO?fY7NDjw;SuDv?>ev4R6~5!O diff --git a/docs/zookeeperHierarchicalQuorums.html b/docs/zookeeperHierarchicalQuorums.html deleted file mode 100644 index e61b4a0f8fe..00000000000 --- a/docs/zookeeperHierarchicalQuorums.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - - - - -Introduction to hierarchical quorums - - - - - - - - - -

- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

Introduction to hierarchical quorums

- - - - - -

- This document gives an example of how to use hierarchical quorums. The basic idea is - very simple. First, we split servers into groups, and add a line for each group listing - the servers that form this group. Next we have to assign a weight to each server. -

- - -

- The following example shows how to configure a system with three groups of three servers - each, and we assign a weight of 1 to each server: -

- - -
-    group.1=1:2:3
-    group.2=4:5:6
-    group.3=7:8:9
-   
-    weight.1=1
-    weight.2=1
-    weight.3=1
-    weight.4=1
-    weight.5=1
-    weight.6=1
-    weight.7=1
-    weight.8=1
-    weight.9=1
- 	
- - -

- When running the system, we are able to form a quorum once we have a majority of votes from - a majority of non-zero-weight groups. Groups that have zero weight are discarded and not - considered when forming quorums. Looking at the example, we are able to form a quorum once - we have votes from at least two servers from each of two different groups. -

- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperHierarchicalQuorums.pdf b/docs/zookeeperHierarchicalQuorums.pdf deleted file mode 100644 index c60d10022a7f4e54dcd3d4dc737f8dcc4a651b89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2521 zcmb7G*_NW%5`E_@a6p`yRYY-Ma26-bR1{@O#6(cUUe*0@Kj?Pff4q>LU6ozgt5+4g z9D<0mBTj5K0nK=^gp~;Z`tR?5{0|^O6!i5o&}e|j>es6UA_MQm2as9dn-9YZz^o|E zK(vgbWhw``#4&8(W?oovZ~jNu{=Hn?Ql zh1!s$`tS)Q)R8zV57VYe7aNA$hR~jPgb7Z}?JkqPthKg#xcyN2@@SV|`{#|yq-W02 znU4DeGFe)5@T{Ksr_;5XC9m>kw{O>KD`(z%&hp7RJumdp)pne}w`3_p7Nqf1U;_;1 zb5p@kP^Cg-nJs|}Nl)39tZ7G0cSLSX1(MQTX#M`Q#m*VQfl{?>F1KCo6L~eW)D7-% zw#{Mx8MdAfxs*b=J=swVHNO+4++&;?JvoC~ue50lp`^|BE6(YZ)wyGdE4`eF-0MM9 zE%xSI`r6iQWwtG>yf63ML#n4Jm|z(FUB9e9$cg|rQ1X0Sn73-_QfXYvrp7LI=+qr- znc0rybbim#@&uBI(^O{8li^x#Ch471oT)vv4a41TD7YEkdSoi%6WP9&{3`d-6Y7=K z9^npnT`u-_1Ao&PjraO)Q12O+g#jHFq>&!?MwE%IkHVEz%x29^+^h!Xx6@@dEpQs?p9`&k5$bS z=1{tLo7Cihn|#SIZ^hT_w&;~lQ>nOxA%;%b2P$tDZp_K5G&{K$q}%e>9gJ@B+0v+;$4;}g=`)x1>4i@vlh4P5+^w;_O}=xw zNO*UURMPn?2X9uL9IPnI309aJYHnF33#>aTGPQ)x6hfAMc4kUtWF_2Fj@8NY%sXHk zmEPy}8)p5oI^Y{BDH)!|rH9p%m_`*cZIgM%c}iqGCfwcp+hC-wael4CUY{E=))RE# zkJnB2C>Cs2dvZi4t+f5jEI(gM>_$3Qn zgkMfQ%b96?!nwvW;~oxoZ`vh`N0vryjj1kXsY~FA`Y4yI?jYH%$`UUny6T<3=4Wv3 z)H}7I;jUm9snjvth;^fMTQBEOsx1EJP1WAj=w06`AN1`{U7TsQ73rcDSY8NX(dz|( zOf1vUy8f3)?$GG2U^rN%ac~bH3+wJb0>iq4#GkI5?}bt19gLN80en-^YV`bf1r{S$ z09iit@J*0ocnmDp(=n2&b=i87D*2p}WJyn{;2+XcA)@TbNeco(@C;jH$YaRumYMC|+S;+~IgdgKrIpmv}S^yjfWme48YU zr4_~#K{R?q^Tj1X<)UfB`Vzh+3jQ@yd4L@9r7t9`PeDE289MXOP+e~-loEUM}|8;O64#V8^=k?;H}WYmK1Y*p%0E?ABF zfDs0o<3{vrG@|d``OS*Y09p8c2*%xugeD%!z<5@BQ;vJ61>xIi;Zf}X1PPJ|{2!Vt B#^L|~ diff --git a/docs/zookeeperInternals.html b/docs/zookeeperInternals.html deleted file mode 100644 index 4fe2f233e5c..00000000000 --- a/docs/zookeeperInternals.html +++ /dev/null @@ -1,803 +0,0 @@ - - - - - - - -ZooKeeper Internals - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper Internals

- - - - - - - -

Introduction

-
-

This document contains information on the inner workings of ZooKeeper. - So far, it discusses these topics: -

- -
- - - -

Atomic Broadcast

-
-

-At the heart of ZooKeeper is an atomic messaging system that keeps all of the servers in sync.

- -

Guarantees, Properties, and Definitions

-

-The specific guarantees provided by the messaging system used by ZooKeeper are the following:

-
- - -
- -Reliable delivery - -
-
-

If a message, m, is delivered -by one server, it will be eventually delivered by all servers.

-
- - -
- -Total order - -
-
-

If a message is -delivered before message b by one server, a will be delivered before b by all -servers. If a and b are delivered messages, either a will be delivered before b -or b will be delivered before a.

-
- - -
- -Causal order - -
-
-

-If a message b is sent after a message a has been delivered by the sender of b, -a must be ordered before b. If a sender sends c after sending b, c must be ordered after b. -

-
- - -
-

-The ZooKeeper messaging system also needs to be efficient, reliable, and easy to -implement and maintain. We make heavy use of messaging, so we need the system to -be able to handle thousands of requests per second. Although we can require at -least k+1 correct servers to send new messages, we must be able to recover from -correlated failures such as power outages. When we implemented the system we had -little time and few engineering resources, so we needed a protocol that is -accessible to engineers and is easy to implement. We found that our protocol -satisfied all of these goals. - -

-

-Our protocol assumes that we can construct point-to-point FIFO channels between -the servers. While similar services usually assume message delivery that can -lose or reorder messages, our assumption of FIFO channels is very practical -given that we use TCP for communication. Specifically we rely on the following property of TCP:

-
- - -
- -Ordered delivery - -
-
-

Data is delivered in the same order it is sent and a message m is -delivered only after all messages sent before m have been delivered. -(The corollary to this is that if message m is lost all messages after m will be lost.)

-
- - -
- -No message after close - -
-
-

Once a FIFO channel is closed, no messages will be received from it.

-
- - -
-

-FLP proved that consensus cannot be achieved in asynchronous distributed systems -if failures are possible. To ensure we achieve consensus in the presence of failures -we use timeouts. However, we rely on times for liveness not for correctness. So, -if timeouts stop working (clocks malfunction for example) the messaging system may -hang, but it will not violate its guarantees.

-

When describing the ZooKeeper messaging protocol we will talk of packets, -proposals, and messages:

-
- -
- -Packet - -
-
-

a sequence of bytes sent through a FIFO channel

-
-
- -Proposal - -
-
-

a unit of agreement. Proposals are agreed upon by exchanging packets -with a quorum of ZooKeeper servers. Most proposals contain messages, however the -NEW_LEADER proposal is an example of a proposal that does not correspond to a message.

-
-
- -Message - -
-
-

a sequence of bytes to be atomically broadcast to all ZooKeeper -servers. A message put into a proposal and agreed upon before it is delivered.

-
- - -
-

-As stated above, ZooKeeper guarantees a total order of messages, and it also -guarantees a total order of proposals. ZooKeeper exposes the total ordering using -a ZooKeeper transaction id (zxid). All proposals will be stamped with a zxid when -it is proposed and exactly reflects the total ordering. Proposals are sent to all -ZooKeeper servers and committed when a quorum of them acknowledge the proposal. -If a proposal contains a message, the message will be delivered when the proposal -is committed. Acknowledgement means the server has recorded the proposal to persistent storage. -Our quorums have the requirement that any pair of quorum must have at least one server -in common. We ensure this by requiring that all quorums have size (n/2+1) where -n is the number of servers that make up a ZooKeeper service. -

-

-The zxid has two parts: the epoch and a counter. In our implementation the zxid -is a 64-bit number. We use the high order 32-bits for the epoch and the low order -32-bits for the counter. Because it has two parts represent the zxid both as a -number and as a pair of integers, (epoch, count). The epoch number represents a -change in leadership. Each time a new leader comes into power it will have its -own epoch number. We have a simple algorithm to assign a unique zxid to a proposal: -the leader simply increments the zxid to obtain a unique zxid for each proposal. -Leadership activation will ensure that only one leader uses a given epoch, so our -simple algorithm guarantees that every proposal will have a unique id. - -

-

-ZooKeeper messaging consists of two phases:

-
- -
- -Leader activation - -
-
-

In this phase a leader establishes the correct state of the system -and gets ready to start making proposals.

-
- - -
- -Active messaging - -
-
-

In this phase a leader accepts messages to propose and coordinates message delivery.

-
- -
-

-ZooKeeper is a holistic protocol. We do not focus on individual proposals, rather -look at the stream of proposals as a whole. Our strict ordering allows us to do this -efficiently and greatly simplifies our protocol. Leadership activation embodies -this holistic concept. A leader becomes active only when a quorum of followers -(The leader counts as a follower as well. You can always vote for yourself ) has synced -up with the leader, they have the same state. This state consists of all of the -proposals that the leader believes have been committed and the proposal to follow -the leader, the NEW_LEADER proposal. (Hopefully you are thinking to -yourself, Does the set of proposals that the leader believes has been committed -included all the proposals that really have been committed? The answer is yes. -Below, we make clear why.) -

- -

Leader Activation

-

-Leader activation includes leader election. We currently have two leader election -algorithms in ZooKeeper: LeaderElection and FastLeaderElection (AuthFastLeaderElection -is a variant of FastLeaderElection that uses UDP and allows servers to perform a simple -form of authentication to avoid IP spoofing). ZooKeeper messaging doesn't care about the -exact method of electing a leader has long as the following holds: -

-
    - - -
  • -

    The leader has seen the highest zxid of all the followers.

    -
  • - -
  • -

    A quorum of servers have committed to following the leader.

    -
  • - - -
-

-Of these two requirements only the first, the highest zxid amoung the followers -needs to hold for correct operation. The second requirement, a quorum of followers, -just needs to hold with high probability. We are going to recheck the second requirement, -so if a failure happens during or after the leader election and quorum is lost, -we will recover by abandoning leader activation and running another election. -

-

-After leader election a single server will be designated as a leader and start -waiting for followers to connect. The rest of the servers will try to connect to -the leader. The leader will sync up with followers by sending any proposals they -are missing, or if a follower is missing too many proposals, it will send a full -snapshot of the state to the follower. -

-

-There is a corner case in which a follower that has proposals, U, not seen -by a leader arrives. Proposals are seen in order, so the proposals of U will have a zxids -higher than zxids seen by the leader. The follower must have arrived after the -leader election, otherwise the follower would have been elected leader given that -it has seen a higher zxid. Since committed proposals must be seen by a quorum of -servers, and a quorum of servers that elected the leader did not see U, the proposals -of you have not been committed, so they can be discarded. When the follower connects -to the leader, the leader will tell the follower to discard U. -

-

-A new leader establishes a zxid to start using for new proposals by getting the -epoch, e, of the highest zxid it has seen and setting the next zxid to use to be -(e+1, 0), fter the leader syncs with a follower, it will propose a NEW_LEADER -proposal. Once the NEW_LEADER proposal has been committed, the leader will activate -and start receiving and issuing proposals. -

-

-It all sounds complicated but here are the basic rules of operation during leader -activation: -

-
    - -
  • -

    A follower will ACK the NEW_LEADER proposal after it has synced with the leader.

    -
  • - -
  • -

    A follower will only ACK a NEW_LEADER proposal with a given zxid from a single server.

    -
  • - -
  • -

    A new leader will COMMIT the NEW_LEADER proposal when a quorum of followers have ACKed it.

    -
  • - -
  • -

    A follower will commit any state it received from the leader when the NEW_LEADER proposal is COMMIT.

    -
  • - -
  • -

    A new leader will not accept new proposals until the NEW_LEADER proposal has been COMMITED.

    -
  • - -
-

-If leader election terminates erroneously, we don't have a problem since the -NEW_LEADER proposal will not be committed since the leader will not have quorum. -When this happens, the leader and any remaining followers will timeout and go back -to leader election. -

- -

Active Messaging

-

-Leader Activation does all the heavy lifting. Once the leader is coronated he can -start blasting out proposals. As long as he remains the leader no other leader can -emerge since no other leader will be able to get a quorum of followers. If a new -leader does emerge, -it means that the leader has lost quorum, and the new leader will clean up any -mess left over during her leadership activation. -

-

ZooKeeper messaging operates similar to a classic two-phase commit.

-

-All communication channels are FIFO, so everything is done in order. Specifically -the following operating constraints are observed:

-
    - - -
  • -

    The leader sends proposals to all followers using -the same order. Moreover, this order follows the order in which requests have been -received. Because we use FIFO channels this means that followers also receive proposals in order. -

    -
  • - - -
  • -

    Followers process messages in the order they are received. This -means that messages will be ACKed in order and the leader will receive ACKs from -followers in order, due to the FIFO channels. It also means that if message $m$ -has been written to non-volatile storage, all messages that were proposed before -$m$ have been written to non-volatile storage.

    -
  • - - -
  • -

    The leader will issue a COMMIT to all followers as soon as a -quorum of followers have ACKed a message. Since messages are ACKed in order, -COMMITs will be sent by the leader as received by the followers in order.

    -
  • - - -
  • -

    COMMITs are processed in order. Followers deliver a proposals -message when that proposal is committed.

    -
  • - - -
- -

Summary

-

So there you go. Why does it work? Specifically, why does is set of proposals -believed by a new leader always contain any proposal that has actually been committed? -First, all proposals have a unique zxid, so unlike other protocols, we never have -to worry about two different values being proposed for the same zxid; followers -(a leader is also a follower) see and record proposals in order; proposals are -committed in order; there is only one active leader at a time since followers only -follow a single leader at a time; a new leader has seen all committed proposals -from the previous epoch since it has seen the highest zxid from a quorum of servers; -any uncommited proposals from a previous epoch seen by a new leader will be committed -by that leader before it becomes active.

- -

Comparisons

-

-Isn't this just Multi-Paxos? No, Multi-Paxos requires some way of assuring that -there is only a single coordinator. We do not count on such assurances. Instead -we use the leader activation to recover from leadership change or old leaders -believing they are still active. -

-

-Isn't this just Paxos? Your active messaging phase looks just like phase 2 of Paxos? -Actually, to us active messaging looks just like 2 phase commit without the need to -handle aborts. Active messaging is different from both in the sense that it has -cross proposal ordering requirements. If we do not maintain strict FIFO ordering of -all packets, it all falls apart. Also, our leader activation phase is different from -both of them. In particular, our use of epochs allows us to skip blocks of uncommitted -proposals and to not worry about duplicate proposals for a given zxid. -

-
- - - -

Quorums

-
-

-Atomic broadcast and leader election use the notion of quorum to guarantee a consistent -view of the system. By default, ZooKeeper uses majority quorums, which means that every -voting that happens in one of these protocols requires a majority to vote on. One example is -acknowledging a leader proposal: the leader can only commit once it receives an -acknowledgement from a quorum of servers. -

-

-If we extract the properties that we really need from our use of majorities, we have that we only -need to guarantee that groups of processes used to validate an operation by voting (e.g., acknowledging -a leader proposal) pairwise intersect in at least one server. Using majorities guarantees such a property. -However, there are other ways of constructing quorums different from majorities. For example, we can assign -weights to the votes of servers, and say that the votes of some servers are more important. To obtain a quorum, -we get enough votes so that the sum of weights of all votes is larger than half of the total sum of all weights. -

-

-A different construction that uses weights and is useful in wide-area deployments (co-locations) is a hierarchical -one. With this construction, we split the servers into disjoint groups and assign weights to processes. To form -a quorum, we have to get a hold of enough servers from a majority of groups G, such that for each group g in G, -the sum of votes from g is larger than half of the sum of weights in g. Interestingly, this construction enables -smaller quorums. If we have, for example, 9 servers, we split them into 3 groups, and assign a weight of 1 to each -server, then we are able to form quorums of size 4. Note that two subsets of processes composed each of a majority -of servers from each of a majority of groups necessarily have a non-empty intersection. It is reasonable to expect -that a majority of co-locations will have a majority of servers available with high probability. -

-

-With ZooKeeper, we provide a user with the ability of configuring servers to use majority quorums, weights, or a -hierarchy of groups. -

-
- - - -

Logging

-
-

-ZooKeeper uses -log4j -version 1.2 as its logging infrastructure. For information on configuring log4j for -ZooKeeper, see the Logging section -of the ZooKeeper Administrator's Guide. - -

- -

Developer Guidelines

-

Please follow these guidelines when submitting code. Patch reviewers will look for the following:

- -

Logging at the Right Level

-

-There are 6 levels of logging in log4j. -It's important to pick the right one. In order of higher to lower severity:

-
    - -
  1. -

    FATAL level designates very severe error events that will presumably lead the application to abort

    -
  2. - -
  3. -

    ERROR level designates error events that might still allow the application to continue running.

    -
  4. - -
  5. -

    WARN level designates potentially harmful situations.

    -
  6. - -
  7. -

    INFO level designates informational messages that highlight the progress of the application at coarse-grained level.

    -
  8. - -
  9. -

    EBUG Level designates fine-grained informational events that are most useful to debug an application.

    -
  10. - -
  11. -

    TRACE Level designates finer-grained informational events than the DEBUG.

    -
  12. - -
-

-ZooKeeper is typically run in production such that log messages of INFO level -severity and higher (more severe) are output to the log.

- -

Use of Standard log4j Idioms

-

-Static Message Logging -

-
-LOG.debug("process completed successfully!");
-
-

However when creating a message from a number of components (string -concatenation), the log call should be wrapped with a "isXEnabled()" call. this -eliminates the string concatenation overhead when debug level logging is not enabled. -

-
-if (LOG.isDebugEnabled()) {
-    LOG.debug("got " + count + " messages in " + time + " minutes");
-}
-
-

-Naming -

-

-Loggers should be named after the class in which they are used. (See the -log4j faq -for reasons why this is a good idea.) -

-
-public class Foo {
-    private static final Logger LOG = Logger.getLogger(Foo.class);
-    ....
-    public Foo() {
-       LOG.info("constructing Foo");
-
-

-Exception handling -

-
-try {
-  // code
-} catch (XYZException e) {
-  // do this
-  LOG.error("Something bad happened", e);
-  // don't do this (generally)
-  // LOG.error(e);
-  // why? because "don't do" case hides the stack trace
- 
-  // continue process here as you need... recover or (re)throw
-}
-
-
- - -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperInternals.pdf b/docs/zookeeperInternals.pdf deleted file mode 100644 index 9450e2bcbdf62cc72449e904866d3ad6e50e25a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44380 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

VrCN8%4ZHFRVG(01 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�S?+g{zjp7wVN3VID+kYM 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 diff --git a/docs/zookeeperJMX.html b/docs/zookeeperJMX.html deleted file mode 100644 index b4ccaa5e22f..00000000000 --- a/docs/zookeeperJMX.html +++ /dev/null @@ -1,477 +0,0 @@ - - - - - - - -ZooKeeper JMX - - - - - - - - - -

- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper JMX

- - - - - - - -

JMX

-
-

Apache ZooKeeper has extensive support for JMX, allowing you - to view and manage a ZooKeeper serving ensemble.

-

This document assumes that you have basic knowledge of - JMX. See - Sun JMX Technology page to get started with JMX. -

-

See the - JMX Management Guide for details on setting up local and - remote management of VM instances. By default the included - zkServer.sh supports only local management - - review the linked document to enable support for remote management - (beyond the scope of this document). -

-
- - - -

Starting ZooKeeper with JMX enabled

-
-

The class - org.apache.zookeeper.server.quorum.QuorumPeerMain - will start a JMX manageable ZooKeeper server. This class - registers the proper MBeans during initalization to support JMX - monitoring and management of the - instance. See bin/zkServer.sh for one - example of starting ZooKeeper using QuorumPeerMain.

-
- - - -

Run a JMX console

-
-

There are a number of JMX consoles available which can connect - to the running server. For this example we will use Sun's - jconsole.

-

The Java JDK ships with a simple JMX console - named jconsole - which can be used to connect to ZooKeeper and inspect a running - server. Once you've started ZooKeeper using QuorumPeerMain - start jconsole, which typically resides in - JDK_HOME/bin/jconsole -

-

When the "new connection" window is displayed either connect - to local process (if jconsole started on same host as Server) or - use the remote process connection.

-

By default the "overview" tab for the VM is displayed (this - is a great way to get insight into the VM btw). Select - the "MBeans" tab.

-

You should now see org.apache.ZooKeeperService - on the left hand side. Expand this item and depending on how you've - started the server you will be able to monitor and manage various - service related features.

-

Also note that ZooKeeper will register log4j MBeans as - well. In the same section along the left hand side you will see - "log4j". Expand that to manage log4j through JMX. Of particular - interest is the ability to dynamically change the logging levels - used by editing the appender and root thresholds. Log4j MBean - registration can be disabled by passing - -Dzookeeper.jmx.log4j.disable=true to the JVM - when starting ZooKeeper. -

-
- - - -

ZooKeeper MBean Reference

-
-

This table details JMX for a server participating in a - replicated ZooKeeper ensemble (ie not standalone). This is the - typical case for a production environment.

- - - -MBeans, their names and description - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MBeans, their names and description
MBeanMBean Object NameDescription
QuorumReplicatedServer_id<#>Represents the Quorum, or Ensemble - parent of all - cluster members. Note that the object name includes the - "myid" of the server (name suffix) that your JMX agent has - connected to.
LocalPeer|RemotePeerreplica.<#>Represents a local or remote peer (ie server - participating in the ensemble). Note that the object name - includes the "myid" of the server (name suffix).
LeaderElectionLeaderElectionRepresents a ZooKeeper cluster leader election which is - in progress. Provides information about the election, such as - when it started.
LeaderLeaderIndicates that the parent replica is the leader and - provides attributes/operations for that server. Note that - Leader is a subclass of ZooKeeperServer, so it provides - all of the information normally associated with a - ZooKeeperServer node.
FollowerFollowerIndicates that the parent replica is a follower and - provides attributes/operations for that server. Note that - Follower is a subclass of ZooKeeperServer, so it provides - all of the information normally associated with a - ZooKeeperServer node.
DataTreeInMemoryDataTreeStatistics on the in memory znode database, also - operations to access finer (and more computationally - intensive) statistics on the data (such as ephemeral - count). InMemoryDataTrees are children of ZooKeeperServer - nodes.
ServerCnxn<session_id>Statistics on each client connection, also - operations on those connections (such as - termination). Note the object name is the session id of - the connection in hex form.
-

This table details JMX for a standalone server. Typically - standalone is only used in development situations.

- - - -MBeans, their names and description - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MBeans, their names and description
MBeanMBean Object NameDescription
ZooKeeperServerStandaloneServer_port<#>Statistics on the running server, also operations - to reset these attributes. Note that the object name - includes the client port of the server (name - suffix).
DataTreeInMemoryDataTreeStatistics on the in memory znode database, also - operations to access finer (and more computationally - intensive) statistics on the data (such as ephemeral - count).
ServerCnxn<session_id>Statistics on each client connection, also - operations on those connections (such as - termination). Note the object name is the session id of - the connection in hex form.
-
- - -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperJMX.pdf b/docs/zookeeperJMX.pdf deleted file mode 100644 index 791cd840a341cda7c0617aef41131fcada18b2fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12240 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 UTKIVH5Q2!p?;jx88j2tPA8$=XmjD0& diff --git a/docs/zookeeperObservers.html b/docs/zookeeperObservers.html deleted file mode 100644 index ffaf326b72b..00000000000 --- a/docs/zookeeperObservers.html +++ /dev/null @@ -1,364 +0,0 @@ - - - - - - - -ZooKeeper Observers - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper Observers

- - - - - - - -

Observers: Scaling ZooKeeper Without Hurting Write Performance -

-
-

- Although ZooKeeper performs very well by having clients connect directly - to voting members of the ensemble, this architecture makes it hard to - scale out to huge numbers of clients. The problem is that as we add more - voting members, the write performance drops. This is due to the fact that - a write operation requires the agreement of (in general) at least half the - nodes in an ensemble and therefore the cost of a vote can increase - significantly as more voters are added. -

-

- We have introduced a new type of ZooKeeper node called - an Observer which helps address this problem and - further improves ZooKeeper's scalability. Observers are non-voting members - of an ensemble which only hear the results of votes, not the agreement - protocol that leads up to them. Other than this simple distinction, - Observers function exactly the same as Followers - clients may connect to - them and send read and write requests to them. Observers forward these - requests to the Leader like Followers do, but they then simply wait to - hear the result of the vote. Because of this, we can increase the number - of Observers as much as we like without harming the performance of votes. -

-

- Observers have other advantages. Because they do not vote, they are not a - critical part of the ZooKeeper ensemble. Therefore they can fail, or be - disconnected from the cluster, without harming the availability of the - ZooKeeper service. The benefit to the user is that Observers may connect - over less reliable network links than Followers. In fact, Observers may be - used to talk to a ZooKeeper server from another data center. Clients of - the Observer will see fast reads, as all reads are served locally, and - writes result in minimal network traffic as the number of messages - required in the absence of the vote protocol is smaller. -

-
- - -

How to use Observers

-
-

Setting up a ZooKeeper ensemble that uses Observers is very simple, - and requires just two changes to your config files. Firstly, in the config - file of every node that is to be an Observer, you must place this line: -

-
-      peerType=observer
-    
-

- This line tells ZooKeeper that the server is to be an Observer. Secondly, - in every server config file, you must add :observer to the server - definition line of each Observer. For example: -

-
-      server.1:localhost:2181:3181:observer
-    
-

- This tells every other server that server.1 is an Observer, and that they - should not expect it to vote. This is all the configuration you need to do - to add an Observer to your ZooKeeper cluster. Now you can connect to it as - though it were an ordinary Follower. Try it out, by running:

-
-      bin/zkCli.sh -server localhost:2181
-    
-

- where localhost:2181 is the hostname and port number of the Observer as - specified in every config file. You should see a command line prompt - through which you can issue commands like ls to query - the ZooKeeper service. -

-
- - - -

Example use cases

-
-

- Two example use cases for Observers are listed below. In fact, wherever - you wish to scale the numbe of clients of your ZooKeeper ensemble, or - where you wish to insulate the critical part of an ensemble from the load - of dealing with client requests, Observers are a good architectural - choice. -

-
    - -
  • - -

    As a datacenter bridge: Forming a ZK ensemble between two - datacenters is a problematic endeavour as the high variance in latency - between the datacenters could lead to false positive failure detection - and partitioning. However if the ensemble runs entirely in one - datacenter, and the second datacenter runs only Observers, partitions - aren't problematic as the ensemble remains connected. Clients of the - Observers may still see and issue proposals.

    - -
  • - -
  • - -

    As a link to a message bus: Some companies have expressed an - interest in using ZK as a component of a persistent reliable message - bus. Observers would give a natural integration point for this work: a - plug-in mechanism could be used to attach the stream of proposals an - Observer sees to a publish-subscribe system, again without loading the - core ensemble. -

    - -
  • - -
-
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperObservers.pdf b/docs/zookeeperObservers.pdf deleted file mode 100644 index 218a3ba0e28845104b6a54feafe863e8f65b5360..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8660 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 cd;L8| - - - - - - -ZooKeeper - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper

-
- -
- - - - - - -

Other Info

-
-

currently empty

-
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperOtherInfo.pdf b/docs/zookeeperOtherInfo.pdf deleted file mode 100644 index bbefa1339805374eec7313fb2e387d9c10e82bca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2947 zcmb_e=~C)Q6#k#5Xar<6k3awV8)E?poY^y$%>ttT>x&MEy1jHTqU$<~-CTD;yxQpk z5~oR=OMqMs)9nSi&mDc&(rv?E0Y=~fQ8g_eex(9JR!a4{$d&cEvw&=?mgejG%@jkN zJm1x|4OY{3PwiRm(k0Gi!FqnVYUOo(x24_KO_*>mYS|k-I-Jq7u3yWNq4fE6o+WCk zR_l;%^(C7jw`}-UuBOtlXp(IB2{TK~xHuyu5(-YJTq8UwUf*3uhxO!mT(hilWA-#4 z&O+N^`e8#2JCh!JW*hY>o>t}d*|t4waj{rYzU@B6+gmf%zPO~x(A>EqOWTy87B-7S z3Sv`<E}w#EdUjo6B){+9V^(hpf^r zzVub5zO;Ge){p!i1acpNeMtGEkiJ5ps~I|kg68VB4=8xt!-%%NFtwud1BRG{Kc1q2 zAkkoo5k1{=cJ5sF0P}ja&M-8+=f9zjaQ#NcFlz!=(o#Ad(& zBb43D53t%Y?KKiS_n|rzN#i8oXbI<73Q#+(iIXWJN%c zffUFmu3-{j@A^^b{Gi7H>mv8xui3?J=&sGV#5@kwQg7IWI1`|cl~ih?0Q*llozA1^+k3Q9*Xh8qrGtp3F!=fFzCwE;^iu7nTfd~v7uSN9>Kcb8L>T*Q~`wPZ}m zWU5f{Xy2yzw$u}so4m`m8Zmz<$1k3Yb?jbBXC7x$E5+8jcyp`VTs(x>2P!O2mo8iK z+D$gZr|Vs(!yB_GE418py~CYlLl1Lhu(}ZR%h6*=wNlf5TVBl~Jv`~$B`* zaWq*?la*2K<$Tn6d3u}{#mljo3}(xf4>` z<l_7u;el?x~=7f@)p?ysQR6vv~L4OUJ_K)#1` zr2{a-I=qK4HcXWFvunQw%n3_9)>uTK6}Aa^XVZ*Fui)pItQ{gcv83)#2QprK!w_mi>? z#p}I)ek+UqQdX)nv&`)d*4j7cedo>7y7;Y3;aKLYH~&TG-XMFJ4^UI^U@>)e5Dw6u zrn&HdihSM!K{oZk^k`5(F#MQbCC%3?$3Tq*a@zM5Ky-G#W!i{vKrT9O2gAd_(+|hN z=&%R!dp-e-Kt2E{3{?gM2=BpH4p2e!fv>0~1WFQB8c;zKf>Z%eLDzvALmqJInJYSK zCIlRlU>n##6-v-@{f8e|1rEIlwTgWz6AB<|P9F|UWuWdoK#cCkK;&RQ;yDSf_Rg}V z$ED!iEih%Prt855``R`E(bC>`?^*l9)t49vC|?zIIskJ8cj;jT=W3@jRrg6 z3618U0w3>@By!bfngHwLCmP2 - - - - - - -ZooKeeper - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper

- - - - - - - -

ZooKeeper: A Distributed Coordination Service for Distributed - Applications

-
-

ZooKeeper is a distributed, open-source coordination service for - distributed applications. It exposes a simple set of primitives that - distributed applications can build upon to implement higher level services - for synchronization, configuration maintenance, and groups and naming. It - is designed to be easy to program to, and uses a data model styled after - the familiar directory tree structure of file systems. It runs in Java and - has bindings for both Java and C.

-

Coordination services are notoriously hard to get right. They are - especially prone to errors such as race conditions and deadlock. The - motivation behind ZooKeeper is to relieve distributed applications the - responsibility of implementing coordination services from scratch.

- -

Design Goals

-

-ZooKeeper is simple. ZooKeeper - allows distributed processes to coordinate with each other through a - shared hierarchal namespace which is organized similarly to a standard - file system. The name space consists of data registers - called znodes, - in ZooKeeper parlance - and these are similar to files and directories. - Unlike a typical file system, which is designed for storage, ZooKeeper - data is kept in-memory, which means ZooKeeper can acheive high - throughput and low latency numbers.

-

The ZooKeeper implementation puts a premium on high performance, - highly available, strictly ordered access. The performance aspects of - ZooKeeper means it can be used in large, distributed systems. The - reliability aspects keep it from being a single point of failure. The - strict ordering means that sophisticated synchronization primitives can - be implemented at the client.

-

-ZooKeeper is replicated. Like the - distributed processes it coordinates, ZooKeeper itself is intended to be - replicated over a sets of hosts called an ensemble.

- - - - - - - -
ZooKeeper Service
- - - -
-

The servers that make up the ZooKeeper service must all know about - each other. They maintain an in-memory image of state, along with a - transaction logs and snapshots in a persistent store. As long as a - majority of the servers are available, the ZooKeeper service will be - available.

-

Clients connect to a single ZooKeeper server. The client maintains - a TCP connection through which it sends requests, gets responses, gets - watch events, and sends heart beats. If the TCP connection to the server - breaks, the client will connect to a different server.

-

-ZooKeeper is ordered. ZooKeeper - stamps each update with a number that reflects the order of all - ZooKeeper transactions. Subsequent operations can use the order to - implement higher-level abstractions, such as synchronization - primitives.

-

-ZooKeeper is fast. It is - especially fast in "read-dominant" workloads. ZooKeeper applications run - on thousands of machines, and it performs best where reads are more - common than writes, at ratios of around 10:1.

- -

Data model and the hierarchical namespace

-

The name space provided by ZooKeeper is much like that of a - standard file system. A name is a sequence of path elements separated by - a slash (/). Every node in ZooKeeper's name space is identified by a - path.

- - - - - - - -
ZooKeeper's Hierarchical Namespace
- - - -
- -

Nodes and ephemeral nodes

-

Unlike is standard file systems, each node in a ZooKeeper - namespace can have data associated with it as well as children. It is - like having a file-system that allows a file to also be a directory. - (ZooKeeper was designed to store coordination data: status information, - configuration, location information, etc., so the data stored at each - node is usually small, in the byte to kilobyte range.) We use the term - znode to make it clear that we are talking about - ZooKeeper data nodes.

-

Znodes maintain a stat structure that includes version numbers for - data changes, ACL changes, and timestamps, to allow cache validations - and coordinated updates. Each time a znode's data changes, the version - number increases. For instance, whenever a client retrieves data it also - receives the version of the data.

-

The data stored at each znode in a namespace is read and written - atomically. Reads get all the data bytes associated with a znode and a - write replaces all the data. Each node has an Access Control List (ACL) - that restricts who can do what.

-

ZooKeeper also has the notion of ephemeral nodes. These znodes - exists as long as the session that created the znode is active. When the - session ends the znode is deleted. Ephemeral nodes are useful when you - want to implement [tbd].

- -

Conditional updates and watches

-

ZooKeeper supports the concept of watches. - Clients can set a watch on a znodes. A watch will be triggered and - removed when the znode changes. When a watch is triggered the client - receives a packet saying that the znode has changed. And if the - connection between the client and one of the Zoo Keeper servers is - broken, the client will receive a local notification. These can be used - to [tbd].

- -

Guarantees

-

ZooKeeper is very fast and very simple. Since its goal, though, is - to be a basis for the construction of more complicated services, such as - synchronization, it provides a set of guarantees. These are:

-
    - -
  • - -

    Sequential Consistency - Updates from a client will be applied - in the order that they were sent.

    - -
  • - - -
  • - -

    Atomicity - Updates either succeed or fail. No partial - results.

    - -
  • - - -
  • - -

    Single System Image - A client will see the same view of the - service regardless of the server that it connects to.

    - -
  • - -
-
    - -
  • - -

    Reliability - Once an update has been applied, it will persist - from that time forward until a client overwrites the update.

    - -
  • - -
-
    - -
  • - -

    Timeliness - The clients view of the system is guaranteed to - be up-to-date within a certain time bound.

    - -
  • - -
-

For more information on these, and how they can be used, see - [tbd] -

- -

Simple API

-

One of the design goals of ZooKeeper is provide a very simple - programming interface. As a result, it supports only these - operations:

-
- -
-create -
-
-

creates a node at a location in the tree

-
- - -
-delete -
-
-

deletes a node

-
- - -
-exists -
-
-

tests if a node exists at a location

-
- - -
-get data -
-
-

reads the data from a node

-
- - -
-set data -
-
-

writes data to a node

-
- - -
-get children -
-
-

retrieves a list of children of a node

-
- - -
-sync -
-
-

waits for data to be propagated

-
- -
-

For a more in-depth discussion on these, and how they can be used - to implement higher level operations, please refer to - [tbd] -

- -

Implementation

-

-ZooKeeper Components shows the high-level components - of the ZooKeeper service. With the exception of the request processor, - each of - the servers that make up the ZooKeeper service replicates its own copy - of each of components.

- - - - - - - -
ZooKeeper Components
- - - -
-

The replicated database is an in-memory database containing the - entire data tree. Updates are logged to disk for recoverability, and - writes are serialized to disk before they are applied to the in-memory - database.

-

Every ZooKeeper server services clients. Clients connect to - exactly one server to submit irequests. Read requests are serviced from - the local replica of each server database. Requests that change the - state of the service, write requests, are processed by an agreement - protocol.

-

As part of the agreement protocol all write requests from clients - are forwarded to a single server, called the - leader. The rest of the ZooKeeper servers, called - followers, receive message proposals from the - leader and agree upon message delivery. The messaging layer takes care - of replacing leaders on failures and syncing followers with - leaders.

-

ZooKeeper uses a custom atomic messaging protocol. Since the - messaging layer is atomic, ZooKeeper can guarantee that the local - replicas never diverge. When the leader receives a write request, it - calculates what the state of the system is when the write is to be - applied and transforms this into a transaction that captures this new - state.

- -

Uses

-

The programming interface to ZooKeeper is deliberately simple. - With it, however, you can implement higher order operations, such as - synchronizations primitives, group membership, ownership, etc. Some - distributed applications have used it to: [tbd: add uses from - white paper and video presentation.] For more information, see - [tbd] -

- -

Performance

-

ZooKeeper is designed to be highly performant. But is it? The - results of the ZooKeeper's development team at Yahoo! Research indicate - that it is. (See ZooKeeper Throughput as the Read-Write Ratio Varies.) It is especially high - performance in applications where reads outnumber writes, since writes - involve synchronizing the state of all servers. (Reads outnumbering - writes is typically the case for a coordination service.)

- - - - - - - -
ZooKeeper Throughput as the Read-Write Ratio Varies
- - - -
-

The figure ZooKeeper Throughput as the Read-Write Ratio Varies is a throughput - graph of ZooKeeper release 3.2 running on servers with dual 2Ghz - Xeon and two SATA 15K RPM drives. One drive was used as a - dedicated ZooKeeper log device. The snapshots were written to - the OS drive. Write requests were 1K writes and the reads were - 1K reads. "Servers" indicate the size of the ZooKeeper - ensemble, the number of servers that make up the - service. Approximately 30 other servers were used to simulate - the clients. The ZooKeeper ensemble was configured such that - leaders do not allow connections from clients.

-
-
Note
-
-

In version 3.2 r/w performance improved by ~2x - compared to the previous - 3.1 release.

-
-
-

Benchmarks also indicate that it is reliable, too. Reliability in the Presence of Errors shows how a deployment responds to - various failures. The events marked in the figure are the - following:

-
    - -
  1. - -

    Failure and recovery of a follower

    - -
  2. - - -
  3. - -

    Failure and recovery of a different follower

    - -
  4. - - -
  5. - -

    Failure of the leader

    - -
  6. - - -
  7. - -

    Failure and recovery of two followers

    - -
  8. - - -
  9. - -

    Failure of another leader

    - -
  10. - -
- -

Reliability

-

To show the behavior of the system over time as - failures are injected we ran a ZooKeeper service made up of - 7 machines. We ran the same saturation benchmark as before, - but this time we kept the write percentage at a constant - 30%, which is a conservative ratio of our expected - workloads. -

- - - - - - - -
Reliability in the Presence of Errors
- - - -
-

The are a few important observations from this graph. First, if - followers fail and recover quickly, then ZooKeeper is able to sustain a - high throughput despite the failure. But maybe more importantly, the - leader election algorithm allows for the system to recover fast enough - to prevent throughput from dropping substantially. In our observations, - ZooKeeper takes less than 200ms to elect a new leader. Third, as - followers recover, ZooKeeper is able to raise throughput again once they - start processing requests.

- -

The ZooKeeper Project

-

ZooKeeper has been - - successfully used - - in many industrial applications. It is used at Yahoo! as the - coordination and failure recovery service for Yahoo! Message - Broker, which is a highly scalable publish-subscribe system - managing thousands of topics for replication and data - delivery. It is used by the Fetching Service for Yahoo! - crawler, where it also manages failure recovery. A number of - Yahoo! advertising systems also use ZooKeeper to implement - reliable services. -

-

All users and developers are encouraged to join the - community and contribute their expertise. See the - - Zookeeper Project on Apache - - for more information. -

-
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperOver.pdf b/docs/zookeeperOver.pdf deleted file mode 100644 index 9f47763a8e7a2d3719c241cf5ab8d85e2347b97b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 307962 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 qCJP1-hL$&N7@ - - - - - - -ZooKeeper Programmer's Guide - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper Programmer's Guide

-

Developing Distributed Applications that use ZooKeeper

- - - - - - - - - -

Introduction

-
-

This document is a guide for developers wishing to create - distributed applications that take advantage of ZooKeeper's coordination - services. It contains conceptual and practical information.

-

The first four sections of this guide present higher level - discussions of various ZooKeeper concepts. These are necessary both for an - understanding of how ZooKeeper works as well how to work with it. It does - not contain source code, but it does assume a familiarity with the - problems associated with distributed computing. The sections in this first - group are:

- -

The next four sections provide practical programming - information. These are:

- -

The book concludes with an appendix containing links to other - useful, ZooKeeper-related information.

-

Most of information in this document is written to be accessible as - stand-alone reference material. However, before starting your first - ZooKeeper application, you should probably at least read the chaptes on - the ZooKeeper Data Model and ZooKeeper Basic Operations. Also, - the Simple Programmming - Example [tbd] is helpful for understanding the basic - structure of a ZooKeeper client application.

-
- - - -

The ZooKeeper Data Model

-
-

ZooKeeper has a hierarchal name space, much like a distributed file - system. The only difference is that each node in the namespace can have - data associated with it as well as children. It is like having a file - system that allows a file to also be a directory. Paths to nodes are - always expressed as canonical, absolute, slash-separated paths; there are - no relative reference. Any unicode character can be used in a path subject - to the following constraints:

-
    - -
  • - -

    The null character (\u0000) cannot be part of a path name. (This - causes problems with the C binding.)

    - -
  • - - -
  • - -

    The following characters can't be used because they don't - display well, or render in confusing ways: \u0001 - \u0019 and \u007F - - \u009F.

    - -
  • - - -
  • - -

    The following characters are not allowed: \ud800 -uF8FFF, - \uFFF0-uFFFF, \uXFFFE - \uXFFFF (where X is a digit 1 - E), \uF0000 - - \uFFFFF.

    - -
  • - - -
  • - -

    The "." character can be used as part of another name, but "." - and ".." cannot alone be used to indicate a node along a path, - because ZooKeeper doesn't use relative paths. The following would be - invalid: "/a/b/./c" or "/a/b/../c".

    - -
  • - - -
  • - -

    The token "zookeeper" is reserved.

    - -
  • - -
- -

ZNodes

-

Every node in a ZooKeeper tree is referred to as a - znode. Znodes maintain a stat structure that - includes version numbers for data changes, acl changes. The stat - structure also has timestamps. The version number, together with the - timestamp allow ZooKeeper to validate the cache and to coordinate - updates. Each time a znode's data changes, the version number increases. - For instance, whenever a client retrieves data, it also receives the - version of the data. And when a client performs an update or a delete, - it must supply the version of the data of the znode it is changing. If - the version it supplies doesn't match the actual version of the data, - the update will fail. (This behavior can be overridden. For more - information see... )[tbd...] -

-
-
Note
-
- -

In distributed application engineering, the word - node can refer to a generic host machine, a - server, a member of an ensemble, a client process, etc. In the ZooKeeper - documentation, znodes refer to the data nodes. - Servers refer to machines that make up the - ZooKeeper service; quorum peers refer to the - servers that make up an ensemble; client refers to any host or process - which uses a ZooKeeper service.

- -
-
-

Znodes are the main enitity that a programmer access. They have - several characteristics that are worth mentioning here.

- -

Watches

-

Clients can set watches on znodes. Changes to that znode trigger - the watch and then clear the watch. When a watch triggers, ZooKeeper - sends the client a notification. More information about watches can be - found in the section - ZooKeeper Watches.

- -

Data Access

-

The data stored at each znode in a namespace is read and written - atomically. Reads get all the data bytes associated with a znode and a - write replaces all the data. Each node has an Access Control List - (ACL) that restricts who can do what.

-

ZooKeeper was not designed to be a general database or large - object store. Instead, it manages coordination data. This data can - come in the form of configuration, status information, rendezvous, etc. - A common property of the various forms of coordination data is that - they are relatively small: measured in kilobytes. - The ZooKeeper client and the server implementations have sanity checks - to ensure that znodes have less than 1M of data, but the data should - be much less than that on average. Operating on relatively large data - sizes will cause some operations to take much more time than others and - will affect the latencies of some operations because of the extra time - needed to move more data over the network and onto storage media. If - large data storage is needed, the usually pattern of dealing with such - data is to store it on a bulk storage system, such as NFS or HDFS, and - store pointers to the storage locations in ZooKeeper.

- -

Ephemeral Nodes

-

ZooKeeper also has the notion of ephemeral nodes. These znodes - exists as long as the session that created the znode is active. When - the session ends the znode is deleted. Because of this behavior - ephemeral znodes are not allowed to have children.

- -

Sequence Nodes -- Unique Naming

-

When creating a znode you can also request that - ZooKeeper append a monotonically increasing counter to the end - of path. This counter is unique to the parent znode. The - counter has a format of %010d -- that is 10 digits with 0 - (zero) padding (the counter is formatted in this way to - simplify sorting), i.e. "<path>0000000001". See - Queue - Recipe for an example use of this feature. Note: the - counter used to store the next sequence number is a signed int - (4bytes) maintained by the parent node, the counter will - overflow when incremented beyond 2147483647 (resulting in a - name "<path>-2147483647").

- -

Time in ZooKeeper

-

ZooKeeper tracks time multiple ways:

-
    - -
  • - -

    -Zxid -

    - - -

    Every change to the ZooKeeper state receives a stamp in the - form of a zxid (ZooKeeper Transaction Id). - This exposes the total ordering of all changes to ZooKeeper. Each - change will have a unique zxid and if zxid1 is smaller than zxid2 - then zxid1 happened before zxid2.

    - -
  • - - -
  • - -

    -Version numbers -

    - - -

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

    - -
  • - - -
  • - -

    -Ticks -

    - - -

    When using multi-server ZooKeeper, servers use ticks to define - timing of events such as status uploads, session timeouts, - connection timeouts between peers, etc. The tick time is only - indirectly exposed through the minimum session timeout (2 times the - tick time); if a client requests a session timeout less than the - minimum session timeout, the server will tell the client that the - session timeout is actually the minimum session timeout.

    - -
  • - - -
  • - -

    -Real time -

    - - -

    ZooKeeper doesn't use real time, or clock time, at all except - to put timestamps into the stat structure on znode creation and - znode modification.

    - -
  • - -
- -

ZooKeeper Stat Structure

-

The Stat structure for each znode in ZooKeeper is made up of the - following fields:

-
    - -
  • - -

    -czxid -

    - - -

    The zxid of the change that caused this znode to be - created.

    - -
  • - - -
  • - -

    -mzxid -

    - - -

    The zxid of the change that last modified this znode.

    - -
  • - - -
  • - -

    -ctime -

    - - -

    The time in milliseconds from epoch when this znode was - created.

    - -
  • - - -
  • - -

    -mtime -

    - - -

    The time in milliseconds from epoch when this znode was last - modified.

    - -
  • - - -
  • - -

    -version -

    - - -

    The number of changes to the data of this znode.

    - -
  • - - -
  • - -

    -cversion -

    - - -

    The number of changes to the children of this znode.

    - -
  • - - -
  • - -

    -aversion -

    - - -

    The number of changes to the ACL of this znode.

    - -
  • - - -
  • - -

    -ephemeralOwner -

    - - -

    The session id of the owner of this znode if the znode is an - ephemeral node. If it is not an ephemeral node, it will be - zero.

    - -
  • - - -
  • - -

    -dataLength -

    - - -

    The length of the data field of this znode.

    - -
  • - - -
  • - -

    -numChildren -

    - - -

    The number of children of this znode.

    - -
  • - - -
-
- - - -

ZooKeeper Sessions

-
-

A ZooKeeper client establishes a session with the ZooKeeper - service by creating a handle to the service using a language - binding. Once created, the handle starts of in the CONNECTING state - and the client library tries to connect to one of the servers that - make up the ZooKeeper service at which point it switches to the - CONNECTED state. During normal operation will be in one of these - two states. If an unrecoverable error occurs, such as session - expiration or authentication failure, or if the application explicitly - closes the handle, the handle will move to the CLOSED state. - The following figure shows the possible state transitions of a - ZooKeeper client:

-

To create a client session the application code must provide - a connection string containing a comma separated list of host:port pairs, - each corresponding to a ZooKeeper server (e.g. "127.0.0.1:4545" or - "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"). The ZooKeeper - client library will pick an arbitrary server and try to connect to - it. If this connection fails, or if the client becomes - disconnected from the server for any reason, the client will - automatically try the next server in the list, until a connection - is (re-)established.

-

-Added in 3.2.0: An - optional "chroot" suffix may also be appended to the connection - string. This will run the client commands while interpreting all - paths relative to this root (similar to the unix chroot - command). If used the example would look like: - "127.0.0.1:4545/app/a" or - "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" where the - client would be rooted at "/app/a" and all paths would be relative - to this root - ie getting/setting/etc... "/foo/bar" would result - in operations being run on "/app/a/foo/bar" (from the server - perspective). This feature is particularly useful in multi-tenant - environments where each user of a particular ZooKeeper service - could be rooted differently. This makes re-use much simpler as - each user can code his/her application as if it were rooted at - "/", while actual location (say /app/a) could be determined at - deployment time.

-

When a client gets a handle to the ZooKeeper service, - ZooKeeper creates a ZooKeeper session, represented as a 64-bit - number, that it assigns to the client. If the client connects to a - different ZooKeeper server, it will send the session id as a part - of the connection handshake. As a security measure, the server - creates a password for the session id that any ZooKeeper server - can validate.The password is sent to the client with the session - id when the client establishes the session. The client sends this - password with the session id whenever it reestablishes the session - with a new server.

-

One of the parameters to the ZooKeeper client library call - to create a ZooKeeper session is the session timeout in - milliseconds. The client sends a requested timeout, the server - responds with the timeout that it can give the client. The current - implementation requires that the timeout be a minimum of 2 times - the tickTime (as set in the server configuration) and a maximum of - 20 times the tickTime. The ZooKeeper client API allows access to - the negotiated timeout.

-

When a client (session) becomes partitioned from the ZK - serving cluster it will begin searching the list of servers that - were specified during session creation. Eventually, when - connectivity between the client and at least one of the servers is - re-established, the session will either again transition to the - "connected" state (if reconnected within the session timeout - value) or it will transition to the "expired" state (if - reconnected after the session timeout). It is not advisable to - create a new session object (a new ZooKeeper.class or zookeeper - handle in the c binding) for disconnection. The ZK client library - will handle reconnect for you. In particular we have heuristics - built into the client library to handle things like "herd effect", - etc... Only create a new session when you are notified of session - expiration (mandatory).

-

Session expiration is managed by the ZooKeeper cluster - itself, not by the client. When the ZK client establishes a - session with the cluster it provides a "timeout" value detailed - above. This value is used by the cluster to determine when the - client's session expires. Expirations happens when the cluster - does not hear from the client within the specified session timeout - period (i.e. no heartbeat). At session expiration the cluster will - delete any/all ephemeral nodes owned by that session and - immediately notify any/all connected clients of the change (anyone - watching those znodes). At this point the client of the expired - session is still disconnected from the cluster, it will not be - notified of the session expiration until/unless it is able to - re-establish a connection to the cluster. The client will stay in - disconnected state until the TCP connection is re-established with - the cluster, at which point the watcher of the expired session - will receive the "session expired" notification.

-

Example state transitions for an expired session as seen by - the expired session's watcher:

-
    - -
  1. -

    'connected' : session is established and client - is communicating with cluster (client/server communication is - operating properly)

    -
  2. - -
  3. -

    .... client is partitioned from the - cluster

    -
  4. - -
  5. -

    'disconnected' : client has lost connectivity - with the cluster

    -
  6. - -
  7. -

    .... time elapses, after 'timeout' period the - cluster expires the session, nothing is seen by client as it is - disconnected from cluster

    -
  8. - -
  9. -

    .... time elapses, the client regains network - level connectivity with the cluster

    -
  10. - -
  11. -

    'expired' : eventually the client reconnects to - the cluster, it is then notified of the - expiration

    -
  12. - -
-

Another parameter to the ZooKeeper session establishment - call is the default watcher. Watchers are notified when any state - change occurs in the client. For example if the client loses - connectivity to the server the client will be notified, or if the - client's session expires, etc... This watcher should consider the - initial state to be disconnected (i.e. before any state changes - events are sent to the watcher by the client lib). In the case of - a new connection, the first event sent to the watcher is typically - the session connection event.

-

The session is kept alive by requests sent by the client. If - the session is idle for a period of time that would timeout the - session, the client will send a PING request to keep the session - alive. This PING request not only allows the ZooKeeper server to - know that the client is still active, but it also allows the - client to verify that its connection to the ZooKeeper server is - still active. The timing of the PING is conservative enough to - ensure reasonable time to detect a dead connection and reconnect - to a new server.

-

- Once a connection to the server is successfully established - (connected) there are basically two cases where the client lib generates - connectionloss (the result code in c binding, exception in Java -- see - the API documentation for binding specific details) when either a synchronous or - asynchronous operation is performed and one of the following holds: -

-
    - -
  1. -

    The application calls an operation on a session that is no - longer alive/valid

    -
  2. - -
  3. -

    The ZooKeeper client disconnects from a server when there - are pending operations to that server, i.e., there is a pending asynchronous call. -

    -
  4. - -
-

-Added in 3.2.0 -- SessionMovedException. There is an internal - exception that is generally not seen by clients called the SessionMovedException. - This exception occurs because a request was received on a connection for a session - which has be reestablished on a different server. The normal cause of this error is - a client that sends a request to a server, but the network packet gets delayed, so - the client times out and connects to a new server. When the delayed packet arrives at - the first server, the old server detects that the session has moved, and closes the - client connection. Clients normally do not see this error since they do not read - from those old connections. (Old connections are usually closed.) One situation in which this - condition can be seen is when two clients try to reestablish the same connection using - a saved session id and password. One of the clients will reestablish the connection - and the second client will be disconnected (causing the pair to attempt to re-establish - it's connection/session indefinitely).

-
- - - -

ZooKeeper Watches

-
-

All of the read operations in ZooKeeper - getData(), getChildren(), and exists() - have the option of setting a watch as a - side effect. Here is ZooKeeper's definition of a watch: a watch event is - one-time trigger, sent to the client that set the watch, which occurs when - the data for which the watch was set changes. There are three key points - to consider in this definition of a watch:

-
    - -
  • - -

    -One-time trigger -

    - - -

    One watch event will be sent to the client when the data has changed. - For example, if a client does a getData("/znode1", true) and later the - data for /znode1 is changed or deleted, the client will get a watch - event for /znode1. If /znode1 changes again, no watch event will be - sent unless the client has done another read that sets a new - watch.

    - -
  • - - -
  • - -

    -Sent to the client -

    - - -

    This implies that an event is on the way to the client, but may - not reach the client before the successful return code to the change - operation reaches the client that initiated the change. Watches are - sent asynchronously to watchers. ZooKeeper provides an ordering - guarantee: a client will never see a change for which it has set a - watch until it first sees the watch event. Network delays or other - factors may cause different clients to see watches and return codes - from updates at different times. The key point is that everything seen - by the different clients will have a consistent order.

    - -
  • - - -
  • - -

    -The data for which the watch was - set -

    - - -

    This refers to the different ways a node can change. It - helps to think of ZooKeeper as maintaining two lists of - watches: data watches and child watches. getData() and - exists() set data watches. getChildren() sets child - watches. Alternatively, it may help to think of watches being - set according to the kind of data returned. getData() and - exists() return information about the data of the node, - whereas getChildren() returns a list of children. Thus, - setData() will trigger data watches for the znode being set - (assuming the set is successful). A successful create() will - trigger a data watch for the znode being created and a child - watch for the parent znode. A successful delete() will trigger - both a data watch and a child watch (since there can be no - more children) for a znode being deleted as well as a child - watch for the parent znode.

    - -
  • - -
-

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

- -

What ZooKeeper Guarantees about Watches

-

With regard to watches, ZooKeeper maintains these - guarantees:

-
    - -
  • - -

    Watches are ordered with respect to other events, other - watches, and asynchronous replies. The ZooKeeper client libraries - ensures that everything is dispatched in order.

    - -
  • - -
-
    - -
  • - -

    A client will see a watch event for a znode it is watching - before seeing the new data that corresponds to that znode.

    - -
  • - -
-
    - -
  • - -

    The order of watch events from ZooKeeper corresponds to the - order of the updates as seen by the ZooKeeper service.

    - -
  • - -
- -

Things to Remember about Watches

-
    - -
  • - -

    Watches are one time triggers; if you get a watch event and - you want to get notified of future changes, you must set another - watch.

    - -
  • - -
-
    - -
  • - -

    Because watches are one time triggers and there is latency - between getting the event and sending a new request to get a watch - you cannot reliably see every change that happens to a node in - ZooKeeper. Be prepared to handle the case where the znode changes - multiple times between getting the event and setting the watch - again. (You may not care, but at least realize it may - happen.)

    - -
  • - -
-
    - -
  • - -

    A watch object, or function/context pair, will only be - triggered once for a given notification. For example, if the same - watch object is registered for an exists and a getData call for the - same file and that file is then deleted, the watch object would - only be invoked once with the deletion notification for the file. -

    - -
  • - -
-
    - -
  • - -

    When you disconnect from a server (for example, when the - server fails), you will not get any watches until the connection - is reestablished. For this reason session events are sent to all - outstanding watch handlers. Use session events to go into a safe - mode: you will not be receiving events while disconnected, so your - process should act conservatively in that mode.

    - -
  • - -
-
- - - -

ZooKeeper access control using ACLs

-
-

ZooKeeper uses ACLs to control access to its znodes (the - data nodes of a ZooKeeper data tree). The ACL implementation is - quite similar to UNIX file access permissions: it employs - permission bits to allow/disallow various operations against a - node and the scope to which the bits apply. Unlike standard UNIX - permissions, a ZooKeeper node is not limited by the three standard - scopes for user (owner of the file), group, and world - (other). ZooKeeper does not have a notion of an owner of a - znode. Instead, an ACL specifies sets of ids and permissions that - are associated with those ids.

-

Note also that an ACL pertains only to a specific znode. In - particular it does not apply to children. For example, if - /app is only readable by ip:172.16.16.1 and - /app/status is world readable, anyone will - be able to read /app/status; ACLs are not - recursive.

-

ZooKeeper supports pluggable authentication schemes. Ids are - specified using the form scheme:id, - where scheme is a the authentication scheme - that the id corresponds to. For - example, ip:172.16.16.1 is an id for a - host with the address 172.16.16.1.

-

When a client connects to ZooKeeper and authenticates - itself, ZooKeeper associates all the ids that correspond to a - client with the clients connection. These ids are checked against - the ACLs of znodes when a clients tries to access a node. ACLs are - made up of pairs of (scheme:expression, - perms). The format of - the expression is specific to the scheme. For - example, the pair (ip:19.22.0.0/16, READ) - gives the READ permission to any clients with - an IP address that starts with 19.22.

- -

ACL Permissions

-

ZooKeeper supports the following permissions:

-
    - -
  • -

    -CREATE: you can create a child node

    -
  • - -
  • -

    -READ: you can get data from a node and list its children.

    -
  • - -
  • -

    -WRITE: you can set data for a node

    -
  • - -
  • -

    -DELETE: you can delete a child node

    -
  • - -
  • -

    -ADMIN: you can set permissions

    -
  • - -
-

The CREATE - and DELETE permissions have been broken out - of the WRITE permission for finer grained - access controls. The cases for CREATE - and DELETE are the following:

-

You want A to be able to do a set on a ZooKeeper node, but - not be able to CREATE - or DELETE children.

-

-CREATE - without DELETE: clients create requests by - creating ZooKeeper nodes in a parent directory. You want all - clients to be able to add, but only request processor can - delete. (This is kind of like the APPEND permission for - files.)

-

Also, the ADMIN permission is there - since ZooKeeper doesn’t have a notion of file owner. In some - sense the ADMIN permission designates the - entity as the owner. ZooKeeper doesn’t support the LOOKUP - permission (execute permission bit on directories to allow you - to LOOKUP even though you can't list the directory). Everyone - implicitly has LOOKUP permission. This allows you to stat a - node, but nothing more. (The problem is, if you want to call - zoo_exists() on a node that doesn't exist, there is no - permission to check.)

- -

Builtin ACL Schemes

-

ZooKeeeper has the following built in schemes:

-
    - -
  • -

    -world has a - single id, anyone, that represents - anyone.

    -
  • - - -
  • -

    -auth doesn't - use any id, represents any authenticated - user.

    -
  • - - -
  • -

    -digest uses - a username:password string to generate - MD5 hash which is then used as an ACL ID - identity. Authentication is done by sending - the username:password in clear text. When - used in the ACL the expression will be - the username:base64 - encoded SHA1 - password digest.

    - -
  • - - -
  • -

    -ip uses the - client host IP as an ACL ID identity. The ACL expression is of - the form addr/bits where the most - significant bits - of addr are matched against the most - significant bits of the client host - IP.

    -
  • - - -
- -

ZooKeeper C client API

-

The following constants are provided by the ZooKeeper C - library:

-
    - -
  • -

    -const int ZOO_PERM_READ; //can read node’s value and list its children

    -
  • - -
  • -

    -const int ZOO_PERM_WRITE;// can set the node’s value

    -
  • - -
  • -

    -const int ZOO_PERM_CREATE; //can create children

    -
  • - -
  • -

    -const int ZOO_PERM_DELETE;// can delete children

    -
  • - -
  • -

    -const int ZOO_PERM_ADMIN; //can execute set_acl()

    -
  • - -
  • -

    -const int ZOO_PERM_ALL;// all of the above flags OR’d together

    -
  • - -
-

The following are the standard ACL IDs:

-
    - -
  • -

    -struct Id ZOO_ANYONE_ID_UNSAFE; //(‘world’,’anyone’)

    -
  • - -
  • -

    -struct Id ZOO_AUTH_IDS;// (‘auth’,’’)

    -
  • - -
-

ZOO_AUTH_IDS empty identity string should be interpreted as “the identity of the creator”.

-

ZooKeeper client comes with three standard ACLs:

-
    - -
  • -

    -struct ACL_vector ZOO_OPEN_ACL_UNSAFE; //(ZOO_PERM_ALL,ZOO_ANYONE_ID_UNSAFE)

    -
  • - -
  • -

    -struct ACL_vector ZOO_READ_ACL_UNSAFE;// (ZOO_PERM_READ, ZOO_ANYONE_ID_UNSAFE)

    -
  • - -
  • -

    -struct ACL_vector ZOO_CREATOR_ALL_ACL; //(ZOO_PERM_ALL,ZOO_AUTH_IDS)

    -
  • - -
-

The ZOO_OPEN_ACL_UNSAFE is completely open free for all - ACL: any application can execute any operation on the node and - can create, list and delete its children. The - ZOO_READ_ACL_UNSAFE is read-only access for any - application. CREATE_ALL_ACL grants all permissions to the - creator of the node. The creator must have been authenticated by - the server (for example, using “digest” - scheme) before it can create nodes with this ACL.

-

The following ZooKeeper operations deal with ACLs:

-
    -
  • - -

    -int zoo_add_auth - (zhandle_t *zh,const char* - scheme,const char* - cert, int certLen, void_completion_t - completion, const void - *data);

    - -
  • -
-

The application uses the zoo_add_auth function to - authenticate itself to the server. The function can be called - multiple times if the application wants to authenticate using - different schemes and/or identities.

-
    -
  • - -

    -int zoo_create - (zhandle_t *zh, const char - *path, const char - *value,int - valuelen, const struct - ACL_vector *acl, int - flags,char - *realpath, int - max_realpath_len);

    - -
  • -
-

zoo_create(...) operation creates a new node. The acl - parameter is a list of ACLs associated with the node. The parent - node must have the CREATE permission bit set.

-
    -
  • - -

    -int zoo_get_acl - (zhandle_t *zh, const char - *path,struct ACL_vector - *acl, struct Stat *stat);

    - -
  • -
-

This operation returns a node’s ACL info.

-
    -
  • - -

    -int zoo_set_acl - (zhandle_t *zh, const char - *path, int - version,const struct - ACL_vector *acl);

    - -
  • -
-

This function replaces node’s ACL list with a new one. The - node must have the ADMIN permission set.

-

Here is a sample code that makes use of the above APIs to - authenticate itself using the “foo” scheme - and create an ephemeral node “/xyz” with create-only - permissions.

-
-
Note
-
-

This is a very simple example which is intended to show - how to interact with ZooKeeper ACLs - specifically. See .../trunk/src/c/src/cli.c - for an example of a proper C client implementation

- -
-
-
-#include <string.h>
-#include <errno.h>
-
-#include "zookeeper.h"
-
-static zhandle_t *zh;
-
-/**
- * In this example this method gets the cert for your
- *   environment -- you must provide
- */
-char *foo_get_cert_once(char* id) { return 0; }
-
-/** Watcher function -- empty for this example, not something you should
- * do in real code */
-void watcher(zhandle_t *zzh, int type, int state, const char *path,
-             void *watcherCtx) {}
-
-int main(int argc, char argv) {
-  char buffer[512];
-  char p[2048];
-  char *cert=0;
-  char appId[64];
-
-  strcpy(appId, "example.foo_test");
-  cert = foo_get_cert_once(appId);
-  if(cert!=0) {
-    fprintf(stderr,
-            "Certificate for appid [%s] is [%s]\n",appId,cert);
-    strncpy(p,cert, sizeof(p)-1);
-    free(cert);
-  } else {
-    fprintf(stderr, "Certificate for appid [%s] not found\n",appId);
-    strcpy(p, "dummy");
-  }
-
-  zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);
-
-  zh = zookeeper_init("localhost:3181", watcher, 10000, 0, 0, 0);
-  if (!zh) {
-    return errno;
-  }
-  if(zoo_add_auth(zh,"foo",p,strlen(p),0,0)!=ZOK)
-    return 2;
-
-  struct ACL CREATE_ONLY_ACL[] = {{ZOO_PERM_CREATE, ZOO_AUTH_IDS}};
-  struct ACL_vector CREATE_ONLY = {1, CREATE_ONLY_ACL};
-  int rc = zoo_create(zh,"/xyz","value", 5, &CREATE_ONLY, ZOO_EPHEMERAL,
-                      buffer, sizeof(buffer)-1);
-
-  /** this operation will fail with a ZNOAUTH error */
-  int buflen= sizeof(buffer);
-  struct Stat stat;
-  rc = zoo_get(zh, "/xyz", 0, buffer, &buflen, &stat);
-  if (rc) {
-    fprintf(stderr, "Error %d for %s\n", rc, __LINE__);
-  }
-
-  zookeeper_close(zh);
-  return 0;
-}
-      
-
- - - -

Pluggable ZooKeeper authentication

-
-

ZooKeeper runs in a variety of different environments with - various different authentication schemes, so it has a completely - pluggable authentication framework. Even the builtin authentication - schemes use the pluggable authentication framework.

-

To understand how the authentication framework works, first you must - understand the two main authentication operations. The framework - first must authenticate the client. This is usually done as soon as - the client connects to a server and consists of validating information - sent from or gathered about a client and associating it with the connection. - The second operation handled by the framework is finding the entries in an - ACL that correspond to client. ACL entries are <idspec, - permissions> pairs. The idspec may be - a simple string match against the authentication information associated - with the connection or it may be a expression that is evaluated against that - information. It is up to the implementation of the authentication plugin - to do the match. Here is the interface that an authentication plugin must - implement:

-
-public interface AuthenticationProvider {
-    String getScheme();
-    KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte authData[]);
-    boolean isValid(String id);
-    boolean matches(String id, String aclExpr);
-    boolean isAuthenticated();
-}
-    
-

The first method getScheme returns the string - that identifies the plugin. Because we support multiple methods of authentication, - an authentication credential or an idspec will always be - prefixed with scheme:. The ZooKeeper server uses the scheme - returned by the authentication plugin to determine which ids the scheme - applies to.

-

-handleAuthentication is called when a client - sends authentication information to be associated with a connection. The - client specifies the scheme to which the information corresponds. The - ZooKeeper server passes the information to the authentication plugin whose - getScheme matches the scheme passed by the client. The - implementor of handleAuthentication will usually return - an error if it determines that the information is bad, or it will associate information - with the connection using cnxn.getAuthInfo().add(new Id(getScheme(), data)). -

-

The authentication plugin is involved in both setting and using ACLs. When an - ACL is set for a znode, the ZooKeeper server will pass the id part of the entry to - the isValid(String id) method. It is up to the plugin to verify - that the id has a correct form. For example, ip:172.16.0.0/16 - is a valid id, but ip:host.com is not. If the new ACL includes - an "auth" entry, isAuthenticated is used to see if the - authentication information for this scheme that is assocatied with the connection - should be added to the ACL. Some schemes - should not be included in auth. For example, the IP address of the client is not - considered as an id that should be added to the ACL if auth is specified.

-

ZooKeeper invokes - matches(String id, String aclExpr) when checking an ACL. It - needs to match authentication information of the client against the relevant ACL - entries. To find the entries which apply to the client, the ZooKeeper server will - find the scheme of each entry and if there is authentication information - from that client for that scheme, matches(String id, String aclExpr) - will be called with id set to the authentication information - that was previously added to the connection by handleAuthentication and - aclExpr set to the id of the ACL entry. The authentication plugin - uses its own logic and matching scheme to determine if id is included - in aclExpr. -

-

There are two built in authentication plugins: ip and - digest. Additional plugins can adding using system properties. At - startup the ZooKeeper server will look for system properties that start with - "zookeeper.authProvider." and interpret the value of those properties as the class name - of an authentication plugin. These properties can be set using the - -Dzookeeeper.authProvider.X=com.f.MyAuth or adding entries such as - the following in the server configuration file:

-
-authProvider.1=com.f.MyAuth
-authProvider.2=com.f.MyAuth2
-    
-

Care should be taking to ensure that the suffix on the property is unique. If there are - duplicates such as -Dzookeeeper.authProvider.X=com.f.MyAuth -Dzookeeper.authProvider.X=com.f.MyAuth2, - only one will be used. Also all servers must have the same plugins defined, otherwise clients using - the authentication schemes provided by the plugins will have problems connecting to some servers. -

-
- - - -

Consistency Guarantees

-
-

ZooKeeper is a high performance, scalable service. Both reads and - write operations are designed to be fast, though reads are faster than - writes. The reason for this is that in the case of reads, ZooKeeper can - serve older data, which in turn is due to ZooKeeper's consistency - guarantees:

-
- -
-Sequential Consistency -
-
-

Updates from a client will be applied in the order that they - were sent.

-
- - -
-Atomicity -
-
-

Updates either succeed or fail -- there are no partial - results.

-
- - -
-Single System Image -
-
-

A client will see the same view of the service regardless of - the server that it connects to.

-
- - -
-Reliability -
-
-

Once an update has been applied, it will persist from that - time forward until a client overwrites the update. This guarantee - has two corollaries:

-
    - -
  1. - -

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

    - -
  2. - - -
  3. - -

    Any updates that are seen by the client, through a read - request or successful update, will never be rolled back when - recovering from server failures.

    - -
  4. - -
-
- - -
-Timeliness -
-
-

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

-
- -
-

Using these consistency guarantees it is easy to build higher level - functions such as leader election, barriers, queues, and read/write - revocable locks solely at the ZooKeeper client (no additions needed to - ZooKeeper). See Recipes and Solutions - for more details.

-
-
Note
-
- -

Sometimes developers mistakenly assume one other guarantee that - ZooKeeper does not in fact make. This is:

- - -
- -
-Simultaneously Consistent Cross-Client Views -
-
-

ZooKeeper does not guarantee that at every instance in - time, two different clients will have identical views of - ZooKeeper data. Due to factors like network delays, one client - may perform an update before another client gets notified of the - change. Consider the scenario of two clients, A and B. If client - A sets the value of a znode /a from 0 to 1, then tells client B - to read /a, client B may read the old value of 0, depending on - which server it is connected to. If it - is important that Client A and Client B read the same value, - Client B should should call the sync() method from the ZooKeeper API - method before it performs its read.

-

So, ZooKeeper by itself doesn't guarantee that changes occur - synchronously across all servers, but ZooKeeper - primitives can be used to construct higher level functions that - provide useful client synchronization. (For more information, - see the ZooKeeper Recipes. - [tbd:..]).

-
- -
- -
-
-
- - - -

Bindings

-
-

The ZooKeeper client libraries come in two languages: Java and C. - The following sections describe these.

- -

Java Binding

-

There are two packages that make up the ZooKeeper Java binding: - org.apache.zookeeper and org.apache.zookeeper.data. The rest of the - packages that make up ZooKeeper are used internally or are part of the - server implementation. The org.apache.zookeeper.data package is made up of - generated classes that are used simply as containers.

-

The main class used by a ZooKeeper Java client is the ZooKeeper class. Its two constructors differ only - by an optional session id and password. ZooKeeper supports session - recovery accross instances of a process. A Java program may save its - session id and password to stable storage, restart, and recover the - session that was used by the earlier instance of the program.

-

When a ZooKeeper object is created, two threads are created as - well: an IO thread and an event thread. All IO happens on the IO thread - (using Java NIO). All event callbacks happen on the event thread. - Session maintenance such as reconnecting to ZooKeeper servers and - maintaining heartbeat is done on the IO thread. Responses for - synchronous methods are also processed in the IO thread. All responses - to asynchronous methods and watch events are processed on the event - thread. There are a few things to notice that result from this - design:

-
    - -
  • - -

    All completions for asynchronous calls and watcher callbacks - will be made in order, one at a time. The caller can do any - processing they wish, but no other callbacks will be processed - during that time.

    - -
  • - - -
  • - -

    Callbacks do not block the processing of the IO thread or the - processing of the synchronous calls.

    - -
  • - - -
  • - -

    Synchronous calls may not return in the correct order. For - example, assume a client does the following processing: issues an - asynchronous read of node /a with - watch set to true, and then in the completion - callback of the read it does a synchronous read of /a. (Maybe not good practice, but not illegal - either, and it makes for a simple example.)

    - - -

    Note that if there is a change to /a between the asynchronous read and the - synchronous read, the client library will receive the watch event - saying /a changed before the - response for the synchronous read, but because the completion - callback is blocking the event queue, the synchronous read will - return with the new value of /a - before the watch event is processed.

    - -
  • - -
-

Finally, the rules associated with shutdown are straightforward: - once a ZooKeeper object is closed or receives a fatal event - (SESSION_EXPIRED and AUTH_FAILED), the ZooKeeper object becomes invalid. - On a close, the two threads shut down and any further access on zookeeper - handle is undefined behavior and should be avoided.

- -

C Binding

-

The C binding has a single-threaded and multi-threaded library. - The multi-threaded library is easiest to use and is most similar to the - Java API. This library will create an IO thread and an event dispatch - thread for handling connection maintenance and callbacks. The - single-threaded library allows ZooKeeper to be used in event driven - applications by exposing the event loop used in the multi-threaded - library.

-

The package includes two shared libraries: zookeeper_st and - zookeeper_mt. The former only provides the asynchronous APIs and - callbacks for integrating into the application's event loop. The only - reason this library exists is to support the platforms were a - pthread library is not available or is unstable - (i.e. FreeBSD 4.x). In all other cases, application developers should - link with zookeeper_mt, as it includes support for both Sync and Async - API.

- -

Installation

-

If you're building the client from a check-out from the Apache - repository, follow the steps outlined below. If you're building from a - project source package downloaded from apache, skip to step 3.

-
    - -
  1. - -

    Run ant compile_jute from the ZooKeeper - top level directory (.../trunk). - This will create a directory named "generated" under - .../trunk/src/c.

    - -
  2. - - -
  3. - -

    Change directory to the.../trunk/src/c - and run autoreconf -if to bootstrap autoconf, automake and libtool. Make sure you have autoconf version 2.59 or greater installed. - Skip to step 4.

    - -
  4. - - -
  5. - -

    If you are building from a project source package, - unzip/untar the source tarball and cd to the - zookeeper-x.x.x/src/c directory.

    - -
  6. - - -
  7. - -

    Run ./configure <your-options> to - generate the makefile. Here are some of options the configure utility supports that can be - useful in this step:

    - - -
      - -
    • - -

      ---enable-debug -

      - - -

      Enables optimization and enables debug info compiler - options. (Disabled by default.)

      - -
    • - - -
    • - -

      ---without-syncapi -

      - - -

      Disables Sync API support; zookeeper_mt library won't be - built. (Enabled by default.)

      - -
    • - - -
    • - -

      ---disable-static -

      - - -

      Do not build static libraries. (Enabled by - default.)

      - -
    • - - -
    • - -

      ---disable-shared -

      - - -

      Do not build shared libraries. (Enabled by - default.)

      - -
    • - -
    - - -
    -
    Note
    -
    - -

    See INSTALL for general information about running - configure.

    - -
    -
    - -
  8. - - -
  9. - -

    Run make or make - install to build the libraries and install them.

    - -
  10. - - -
  11. - -

    To generate doxygen documentation for the ZooKeeper API, run - make doxygen-doc. All documentation will be - placed in a new subfolder named docs. By default, this command - only generates HTML. For information on other document formats, - run ./configure --help -

    - -
  12. - -
- -

Using the C Client

-

You can test your client by running a ZooKeeper server (see - instructions on the project wiki page on how to run it) and connecting - to it using one of the cli applications that were built as part of the - installation procedure. cli_mt (multithreaded, built against - zookeeper_mt library) is shown in this example, but you could also use - cli_st (singlethreaded, built against zookeeper_st library):

-

-$ cli_mt zookeeper_host:9876 -

-

This is a client application that gives you a shell for - executing simple ZooKeeper commands. Once successfully started - and connected to the server it displays a shell prompt. You - can now enter ZooKeeper commands. For example, to create a - node:

-

-> create /my_new_node -

-

To verify that the node's been created:

-

-> ls / -

-

You should see a list of node who are children of the root node - "/".

-

In order to be able to use the ZooKeeper API in your application - you have to remember to

-
    - -
  1. - -

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

    - -
  2. - - -
  3. - -

    If you are building a multithreaded client, compile with - -DTHREADED compiler flag to enable the multi-threaded version of - the library, and then link against against the - zookeeper_mt library. If you are building a - single-threaded client, do not compile with -DTHREADED, and be - sure to link against the zookeeper_st - library.

    - -
  4. - -
-

Refer to Program Structure, with Simple Example - for examples of usage in Java and C. - [tbd] - -

-
- - - -

Building Blocks: A Guide to ZooKeeper Operations

-
-

This section surveys all the operations a developer can perform - against a ZooKeeper server. It is lower level information than the earlier - concepts chapters in this manual, but higher level than the ZooKeeper API - Reference. It covers these topics:

- - -

Handling Errors

-

Both the Java and C client bindings may report errors. The Java client binding does so by throwing KeeperException, calling code() on the exception will return the specific error code. The C client binding returns an error code as defined in the enum ZOO_ERRORS. API callbacks indicate result code for both language bindings. See the API documentation (javadoc for Java, doxygen for C) for full details on the possible errors and their meaning.

- -

Connecting to ZooKeeper

-

- -

Read Operations

-

- -

Write Operations

-

- -

Handling Watches

-

- -

Miscelleaneous ZooKeeper Operations

-

-
- - - -

Program Structure, with Simple Example

-
-

-[tbd] -

-
- - - -

Gotchas: Common Problems and Troubleshooting

-
-

So now you know ZooKeeper. It's fast, simple, your application - works, but wait ... something's wrong. Here are some pitfalls that - ZooKeeper users fall into:

-
    - -
  1. - -

    If you are using watches, you must look for the connected watch - event. When a ZooKeeper client disconnects from a server, you will - not receive notification of changes until reconnected. If you are - watching for a znode to come into existance, you will miss the event - if the znode is created and deleted while you are disconnected.

    - -
  2. - - -
  3. - -

    You must test ZooKeeper server failures. The ZooKeeper service - can survive failures as long as a majority of servers are active. The - question to ask is: can your application handle it? In the real world - a client's connection to ZooKeeper can break. (ZooKeeper server - failures and network partitions are common reasons for connection - loss.) The ZooKeeper client library takes care of recovering your - connection and letting you know what happened, but you must make sure - that you recover your state and any outstanding requests that failed. - Find out if you got it right in the test lab, not in production - test - with a ZooKeeper service made up of a several of servers and subject - them to reboots.

    - -
  4. - - -
  5. - -

    The list of ZooKeeper servers used by the client must match the - list of ZooKeeper servers that each ZooKeeper server has. Things can - work, although not optimally, if the client list is a subset of the - real list of ZooKeeper servers, but not if the client lists ZooKeeper - servers not in the ZooKeeper cluster.

    - -
  6. - - -
  7. - -

    Be careful where you put that transaction log. The most - performance-critical part of ZooKeeper is the transaction log. - ZooKeeper must sync transactions to media before it returns a - response. A dedicated transaction log device is key to consistent good - performance. Putting the log on a busy device will adversely effect - performance. If you only have one storage device, put trace files on - NFS and increase the snapshotCount; it doesn't eliminate the problem, - but it can mitigate it.

    - -
  8. - - -
  9. - -

    Set your Java max heap size correctly. It is very important to - avoid swapping. Going to disk unnecessarily will - almost certainly degrade your performance unacceptably. Remember, in - ZooKeeper, everything is ordered, so if one request hits the disk, all - other queued requests hit the disk.

    - - -

    To avoid swapping, try to set the heapsize to the amount of - physical memory you have, minus the amount needed by the OS and cache. - The best way to determine an optimal heap size for your configurations - is to run load tests. If for some reason you - can't, be conservative in your estimates and choose a number well - below the limit that would cause your machine to swap. For example, on - a 4G machine, a 3G heap is a conservative estimate to start - with.

    - -
  10. - -
-
- - - - - -Links to Other Information - - -

Outside the formal documentation, there're several other sources of - information for ZooKeeper developers.

- - -
- -
-ZooKeeper Whitepaper [tbd: find url] - -
-
-

The definitive discussion of ZooKeeper design and performance, - by Yahoo! Research

-
- - -
-API Reference [tbd: find url] - -
-
-

The complete reference to the ZooKeeper API

-
- - -
- -ZooKeeper - Talk at the Hadoup Summit 2008 - -
-
-

A video introduction to ZooKeeper, by Benjamin Reed of Yahoo! - Research

-
- - -
- -Barrier and - Queue Tutorial - -
-
-

The excellent Java tutorial by Flavio Junqueira, implementing - simple barriers and producer-consumer queues using ZooKeeper.

-
- - -
- -ZooKeeper - - A Reliable, Scalable Distributed Coordination System - -
-
-

An article by Todd Hoff (07/15/2008)

-
- - -
- -ZooKeeper Recipes - -
-
-

Pseudo-level discussion of the implementation of various - synchronization solutions with ZooKeeper: Event Handles, Queues, - Locks, and Two-phase Commits.

-
- - -
- -[tbd] - -
-
-

Any other good sources anyone can think of...

-
- -
- -
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperProgrammers.pdf b/docs/zookeeperProgrammers.pdf deleted file mode 100644 index 3efcad60cb5f2c610472f94556875e2f3f865a20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134924 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 diff --git a/docs/zookeeperQuotas.html b/docs/zookeeperQuotas.html deleted file mode 100644 index 81947e961f9..00000000000 --- a/docs/zookeeperQuotas.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - -ZooKeeper Quota's Guide - - - - - - - - - - -
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper Quota's Guide

-

A Guide to Deployment and Administration

-
- -
- - - - - -

Quotas

-
-

ZooKeeper has both namespace and bytes quotas. You can use the ZooKeeperMain class to setup quotas. - ZooKeeper prints WARN messages if users exceed the quota assigned to them. The messages - are printed in the log of the ZooKeeper. -

-

-$java -cp zookeeper.jar:src/java/lib/log4j-1.2.15.jar/conf:src/java/lib/jline-0.9.94.jar \ - org.apache.zookeeper.ZooKeeperMain -server host:port -

-

The above command gives you a command line option of using quotas.

- -

Setting Quotas

-

You can use - setquota to set a quota on a ZooKeeper node. It has an option of setting quota with - -n (for namespace) - and -b (for bytes).

-

The ZooKeeper quota are stored in ZooKeeper itself in /zookeeper/quota. To disable other people from - changing the quota's set the ACL for /zookeeper/quota such that only admins are able to read and write to it. -

- -

Listing Quotas

-

You can use - listquota to list a quota on a ZooKeeper node. -

- -

Deleting Quotas

-

You can use - delquota to delete quota on a ZooKeeper node. -

-
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperQuotas.pdf b/docs/zookeeperQuotas.pdf deleted file mode 100644 index 646a913e91170f56bf5f075da1d04f6fc5d4086b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5701 zcmcIo>6WU>lK$^g;EW?Ph*POJ&kAZei-L+WDBxY^_K$v`JG^5jShaPRr}wVY>vq*D zicB&yzKD#B_#%V-ay5Y^2~Y4J|NPg#JtTxe#`fgNW+6oQ`$dM3hQ2pENZ&N1+fFti zxZ3MOXc9-0R0PWBJ+dx24fn444*5vOm5tfrWk)U{MCwr z1hcT9oez37juL~#VLm?12aV395Zq;gxhg;F`EvwjW{pN@%o|Z*zA3k&-b|G2k72s4 zUGiCROe~VMQB#@5F6n4+%MH#Y)ui}J0inH1eiRUPbgYtYoD!Q&u9&H&yjF`^@VD`Z zJryP`^B65_SQyDGg+S@G?BqNIlMt-)Zj zrQ%1qyDlMIX}goIBfP0oopQ9&9v_88zcNP`ePy#6P@~q0Z=RN^{y?g+>wtHWsWH-6 z4pI?+F%Bn2JyfZb&4DnS5pRC$yg5v}L3}leZo}QiqOq~924Y>tMKiLA27@70ytLn| zK4r;WeEGN8T2+kYbaJgz?Xf>857eucwbcR93WTg|zMD$uLQ2j|2a`!{!3^tavuRPh zL|jALQF3jq*IkB}&!$hNytQ6rd4L0D)u?5TBIixenlQvS-cZV}M#9v!k!+4+%DhJW zO>aG?1|x|kN(QPc`c`YzhXr%F^a_~QXqiP0J!yrgIgI5c-l+e#>tr9^@!@Xtle^vB zv@afH;HIJ}>o$ae&qEK=ktIbe8GiuxL%}bN;Sj^((9(kpWy`pkJK2JW`%uo%Z7|b< z97hIIi@I)r7UQ-aZtk=lcb|Jw?ib8icjy5NyZ@ZiGxRJ!4}~-4=fmVK>p}S2)^-n9 zThY%Bnw)iZu?t6 zKDPHOkk>Hxo1JkjLeWVU0Kri#nZkiZNYZV83&@9GeFgID$RBYNBhdd_;1q8t$I@(k z87ejd+S zP`I$_ti%Gs8G$r|3g(Tq=PS}|@C0t+>B73gGVM^(+u3KOW?6gh$Em?JO)|n-pBCm> z)SE24?Jz09;7Z*moC4jzg}skVHt-d1BYwr0W4nMgPNggZ#1@V*MiABR=y=VH^22h znsj}`;%#vxs5508+UO+TT&}XYEj;gLmb`vwEqB9${WfY>1hqfm@Y1Y>^fcwVHQ(6T z$*iQ|nUoaK)UsM$`4^E{uiehkMqmWv*L*&IOy`Crt8HS!5-T75m$~J&!fCG1xe!vl zbcxq6Y@_+rMtY@~dZ`=Q9D_l2SKt?HQPS9GzT->8r7&|YS`yrtsS3ukQ`SN&`C;|o zXG>h3w5N#X>Jg+3aOPw2Rkyu-U0!;cUsjPxauC-PukWBv-XoV;>hq4z#+W zqUj5n--o8T@wOxl1bneiCMu_CUuV@8q8Tz$R*&i0KD@J5do6vj0`gF6$;7@HgG+nz{TVJ;eL%(BB&t5|v5>v-h4W0hY3nhosk@RL z#8aDYSRZ<)!4RCr>kO-{Hlh%Y={Xqo#?(ruRO^c4S$~|J5ERO9T9av@)2xhB0ul&x zr&^vNyGt9unGZ?@z+2}mgK8_(M-UY!fE_o_y60?bXx z%!2D>*$ut4`uw}f3jveyg(-B=h@UiWaq<^~&E34TY;26W{VJ_ti>k8wrnqMk_ziKuu76_9Z_0X& ze!oh`Psci`3Ue&2h@W&Hh`RTS0y0a4MpIUg4{zj1h1f^xr0Qi zG=#f{9rUsg?v}sc6^C$FGI(Vm+zr-156Hb1+VQeeydvNp36z~00LB4zoH*|23VqhX zkDUQg0zztr0B)7a(EP(L$oq5#r6^E$(i97x`b|}r&zbaL?cdCI>SvOsUsrgTOp_}025IJ z1?{1aK9Y1QeCG$!AAJl!`=yVf!G`|ahoacaI22`owZDv`SrCH1^rf&r>7!qq)7AX)T9UyMY7tN%O>PoXdCku38f7iZBI z83andj3aRJMJ_>-FLDW*cpb-3FZLr+uQnyv*F7Rp<^>;#rC#k!JnL(lqN0MJp3MTM qR{l$Nd<_{I2H;^XuXHbfAt(R3%+|6+({{a*LUC|w9IW)Jp8o-O|M~3z diff --git a/docs/zookeeperStarted.html b/docs/zookeeperStarted.html deleted file mode 100644 index 23e2c3332aa..00000000000 --- a/docs/zookeeperStarted.html +++ /dev/null @@ -1,643 +0,0 @@ - - - - - - - -ZooKeeper Getting Started Guide - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

ZooKeeper Getting Started Guide

- - - - - - - -

Getting Started: Coordinating Distributed Applications with - ZooKeeper

-
-

This document contains information to get you started quickly with - ZooKeeper. It is aimed primarily at developers hoping to try it out, and - contains simple installation instructions for a single ZooKeeper server, a - few commands to verify that it is running, and a simple programming - example. Finally, as a convenience, there are a few sections regarding - more complicated installations, for example running replicated - deployments, and optimizing the transaction log. However for the complete - instructions for commercial deployments, please refer to the ZooKeeper - Administrator's Guide.

- -

Pre-requisites

-

See - System Requirements in the Admin guide.

- -

Download

-

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

- -

Standalone Operation

-

Setting up a ZooKeeper server in standalone mode is - straightforward. The server is contained in a single JAR file, - so installation consists of creating a configuration.

-

Once you've downloaded a stable ZooKeeper release unpack - it and cd to the root

-

To start ZooKeeper you need a configuration file. Here is a sample, - create it in conf/zoo.cfg:

-
-tickTime=2000
-dataDir=/var/zookeeper
-clientPort=2181
-
-

This file can be called anything, but for the sake of this - discussion call - it conf/zoo.cfg. Change the - value of dataDir to specify an - existing (empty to start with) directory. Here are the meanings - for each of the fields:

-
- -
- -tickTime - -
-
-

the basic time unit in milliseconds used by ZooKeeper. It is - used to do heartbeats and the minimum session timeout will be - twice the tickTime.

-
- -
-
- -
- -dataDir - -
-
-

the location to store the in-memory database snapshots and, - unless specified otherwise, the transaction log of updates to the - database.

-
- - -
- -clientPort - -
-
-

the port to listen for client connections

-
- -
-

Now that you created the configuration file, you can start - ZooKeeper:

-
bin/zkServer.sh start
-

ZooKeeper logs messages using log4j -- more detail - available in the - Logging - section of the Programmer's Guide. You will see log messages - coming to the console (default) and/or a log file depending on - the log4j configuration.

-

The steps outlined here run ZooKeeper in standalone mode. There is - no replication, so if ZooKeeper process fails, the service will go down. - This is fine for most development situations, but to run ZooKeeper in - replicated mode, please see Running Replicated - ZooKeeper.

- -

Managing ZooKeeper Storage

-

For long running production systems ZooKeeper storage must - be managed externally (dataDir and logs). See the section on - maintenance for - more details.

- -

Connecting to ZooKeeper

-

Once ZooKeeper is running, you have several options for connection - to it:

-
    - -
  • - -

    -Java: Use

    - - -
    bin/zkCli.sh -server 127.0.0.1:2181
    - - -

    This lets you perform simple, file-like operations.

    - -
  • - - -
  • - -

    -C: compile cli_mt - (multi-threaded) or cli_st (single-threaded) by running - make cli_mt or make - cli_st in - the src/c subdirectory in - the ZooKeeper sources. See the README contained within - src/c for full details.

    - - -

    You can run the program - from src/c using:

    - - -
    LD_LIBRARY_PATH=. cli_mt 127.0.0.1:2181
    - - -

    or

    - - -
    LD_LIBRARY_PATH=. cli_st 127.0.0.1:2181
    - -

    This will give you a simple shell to execute file - system like operations on ZooKeeper.

    - -
  • - -
-

Once you have connected, you should see something like: -

-
-
-Connecting to localhost:2181
-log4j:WARN No appenders could be found for logger (org.apache.zookeeper.ZooKeeper).
-log4j:WARN Please initialize the log4j system properly.
-Welcome to ZooKeeper!
-JLine support is enabled
-[zkshell: 0]
-        
-

- From the shell, type help to get a listing of commands that can be executed from the client, as in: -

-
-
-[zkshell: 0] help
-ZooKeeper host:port cmd args
-        get path [watch]
-        ls path [watch]
-        set path data [version]
-        delquota [-n|-b] path
-        quit
-        printwatches on|off
-        createpath data acl
-        stat path [watch]
-        listquota path
-        history
-        setAcl path acl
-        getAcl path
-        sync path
-        redo cmdno
-        addauth scheme auth
-        delete path [version]
-        setquota -n|-b val path
-
-        
-

From here, you can try a few simple commands to get a feel for this simple command line interface. First, start by issuing the list command, as - in ls, yielding: -

-
-
-[zkshell: 8] ls /
-[zookeeper]
-        
-

Next, create a new znode by running create /zk_test my_data. This creates a new znode and associates the string "my_data" with the node. - You should see:

-
-
-[zkshell: 9] create /zk_test my_data
-Created /zk_test
-      
-

Issue another ls / command to see what the directory looks like: -

-
-
-[zkshell: 11] ls /
-[zookeeper, zk_test]
-
-        
-

- Notice that the zk_test directory has now been created. -

-

Next, verify that the data was associated with the znode by running the get command, as in: -

-
-
-[zkshell: 12] get /zk_test
-my_data
-cZxid = 5
-ctime = Fri Jun 05 13:57:06 PDT 2009
-mZxid = 5
-mtime = Fri Jun 05 13:57:06 PDT 2009
-pZxid = 5
-cversion = 0
-dataVersion = 0
-aclVersion = 0
-ephemeralOwner = 0
-dataLength = 7
-numChildren = 0
-        
-

We can change the data associated with zk_test by issuing the set command, as in: -

-
-
-[zkshell: 14] set /zk_test junk
-cZxid = 5
-ctime = Fri Jun 05 13:57:06 PDT 2009
-mZxid = 6
-mtime = Fri Jun 05 14:01:52 PDT 2009
-pZxid = 5
-cversion = 0
-dataVersion = 1
-aclVersion = 0
-ephemeralOwner = 0
-dataLength = 4
-numChildren = 0
-[zkshell: 15] get /zk_test
-junk
-cZxid = 5
-ctime = Fri Jun 05 13:57:06 PDT 2009
-mZxid = 6
-mtime = Fri Jun 05 14:01:52 PDT 2009
-pZxid = 5
-cversion = 0
-dataVersion = 1
-aclVersion = 0
-ephemeralOwner = 0
-dataLength = 4
-numChildren = 0
-      
-

- (Notice we did a get after setting the data and it did, indeed, change.

-

Finally, let's delete the node by issuing: -

-
-
-[zkshell: 16] delete /zk_test
-[zkshell: 17] ls /
-[zookeeper]
-[zkshell: 18]
-
-

That's it for now. To explore more, continue with the rest of this document and see the Programmer's Guide.

- -

Programming to ZooKeeper

-

ZooKeeper has a Java bindings and C bindings. They are - functionally equivalent. The C bindings exist in two variants: single - threaded and multi-threaded. These differ only in how the messaging loop - is done. For more information, see the Programming - Examples in the ZooKeeper Programmer's Guide for - sample code using of the different APIs.

- -

Running Replicated ZooKeeper

-

Running ZooKeeper in standalone mode is convenient for evaluation, - some development, and testing. But in production, you should run - ZooKeeper in replicated mode. A replicated group of servers in the same - application is called a quorum, and in replicated - mode, all servers in the quorum have copies of the same configuration - file. The file is similar to the one used in standalone mode, but with a - few differences. Here is an example:

-
-tickTime=2000
-dataDir=/var/zookeeper
-clientPort=2181
-initLimit=5
-syncLimit=2
-server.1=zoo1:2888:3888
-server.2=zoo2:2888:3888
-server.3=zoo3:2888:3888
-
-

The new entry, initLimit is - timeouts ZooKeeper uses to limit the length of time the ZooKeeper - servers in quorum have to connect to a leader. The entry syncLimit limits how far out of date a server can - be from a leader.

-

With both of these timeouts, you specify the unit of time using - tickTime. In this example, the timeout - for initLimit is 5 ticks at 2000 milleseconds a tick, or 10 - seconds.

-

The entries of the form server.X list the - servers that make up the ZooKeeper service. When the server starts up, - it knows which server it is by looking for the file - myid in the data directory. That file has the - contains the server number, in ASCII.

-

Finally, note the two port numbers after each server - name: " 2888" and "3888". Peers use the former port to connect - to other peers. Such a connection is necessary so that peers - can communicate, for example, to agree upon the order of - updates. More specifically, a ZooKeeper server uses this port - to connect followers to the leader. When a new leader arises, a - follower opens a TCP connection to the leader using this - port. Because the default leader election also uses TCP, we - currently require another port for leader election. This is the - second port in the server entry. -

-
-
Note
-
- -

If you want to test multiple servers on a single - machine, specify the servername - as localhost with unique quorum & - leader election ports (i.e. 2888:3888, 2889:3889, 2890:3890 in - the example above) for each server.X in that server's config - file. Of course separate dataDirs and - distinct clientPorts are also necessary - (in the above replicated example, running on a - single localhost, you would still have - three config files).

- -
-
- -

Other Optimizations

-

There are a couple of other configuration parameters that can - greatly increase performance:

-
    - -
  • - -

    To get low latencies on updates it is important to - have a dedicated transaction log directory. By default - transaction logs are put in the same directory as the data - snapshots and myid file. The dataLogDir - parameters indicates a different directory to use for the - transaction logs.

    - -
  • - - -
  • - -

    -[tbd: what is the other config param?] -

    - -
  • - -
-
- -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperStarted.pdf b/docs/zookeeperStarted.pdf deleted file mode 100644 index 46475fd898db4400fdf24c91fde700aa176d5843..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23209 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 GU;cmDB;axY diff --git a/docs/zookeeperTutorial.html b/docs/zookeeperTutorial.html deleted file mode 100644 index 99075b99155..00000000000 --- a/docs/zookeeperTutorial.html +++ /dev/null @@ -1,892 +0,0 @@ - - - - - - - -Programming with ZooKeeper - A basic tutorial - - - - - - - - - -
- - - -
- - - - - - - - - - - - -
-
-
-
- -
- - -
- -
- -   -
- - - - - -
- -

Programming with ZooKeeper - A basic tutorial

- - - - - - - -

Introduction

-
-

In this tutorial, we show simple implementations of barriers and - producer-consumer queues using ZooKeeper. We call the respective classes Barrier and Queue. - These examples assume that you have at least one ZooKeeper server running.

-

Both primitives use the following common excerpt of code:

-
-    static ZooKeeper zk = null;
-    static Integer mutex;
-
-    String root;
-
-    SyncPrimitive(String address) {
-        if(zk == null){
-            try {
-                System.out.println("Starting ZK:");
-                zk = new ZooKeeper(address, 3000, this);
-                mutex = new Integer(-1);
-                System.out.println("Finished starting ZK: " + zk);
-            } catch (IOException e) {
-                System.out.println(e.toString());
-                zk = null;
-            }
-        }
-    }
-
-    synchronized public void process(WatchedEvent event) {
-        synchronized (mutex) {
-            mutex.notify();
-        }
-    }
-
-

Both classes extend SyncPrimitive. In this way, we execute steps that are -common to all primitives in the constructor of SyncPrimitive. To keep the examples -simple, we create a ZooKeeper object the first time we instantiate either a barrier -object or a queue object, and we declare a static variable that is a reference -to this object. The subsequent instances of Barrier and Queue check whether a -ZooKeeper object exists. Alternatively, we could have the application creating a -ZooKeeper object and passing it to the constructor of Barrier and Queue.

-

-We use the process() method to process notifications triggered due to watches. -In the following discussion, we present code that sets watches. A watch is internal -structure that enables ZooKeeper to notify a client of a change to a node. For example, -if a client is waiting for other clients to leave a barrier, then it can set a watch and -wait for modifications to a particular node, which can indicate that it is the end of the wait. -This point becomes clear once we go over the examples. -

-
- - - -

Barriers

-
-

- A barrier is a primitive that enables a group of processes to synchronize the - beginning and the end of a computation. The general idea of this implementation - is to have a barrier node that serves the purpose of being a parent for individual - process nodes. Suppose that we call the barrier node "/b1". Each process "p" then - creates a node "/b1/p". Once enough processes have created their corresponding - nodes, joined processes can start the computation. -

-

In this example, each process instantiates a Barrier object, and its constructor takes as parameters:

-
    -
  • -

    the address of a ZooKeeper server (e.g., "zoo1.foo.com:2181")

    -
  • - -
  • -

    the path of the barrier node on ZooKeeper (e.g., "/b1")

    -
  • - -
  • -

    the size of the group of processes

    -
  • - -
-

The constructor of Barrier passes the address of the Zookeeper server to the -constructor of the parent class. The parent class creates a ZooKeeper instance if -one does not exist. The constructor of Barrier then creates a -barrier node on ZooKeeper, which is the parent node of all process nodes, and -we call root (Note: This is not the ZooKeeper root "/").

-
-        /**
-         * Barrier constructor
-         *
-         * @param address
-         * @param root
-         * @param size
-         */
-        Barrier(String address, String root, int size) {
-            super(address);
-            this.root = root;
-            this.size = size;
-
-            // Create barrier node
-            if (zk != null) {
-                try {
-                    Stat s = zk.exists(root, false);
-                    if (s == null) {
-                        zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
-                                CreateMode.PERSISTENT);
-                    }
-                } catch (KeeperException e) {
-                    System.out
-                            .println("Keeper exception when instantiating queue: "
-                                    + e.toString());
-                } catch (InterruptedException e) {
-                    System.out.println("Interrupted exception");
-                }
-            }
-
-            // My node name
-            try {
-                name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
-            } catch (UnknownHostException e) {
-                System.out.println(e.toString());
-            }
-
-        }
-
-

-To enter the barrier, a process calls enter(). The process creates a node under -the root to represent it, using its host name to form the node name. It then wait -until enough processes have entered the barrier. A process does it by checking -the number of children the root node has with "getChildren()", and waiting for -notifications in the case it does not have enough. To receive a notification when -there is a change to the root node, a process has to set a watch, and does it -through the call to "getChildren()". In the code, we have that "getChildren()" -has two parameters. The first one states the node to read from, and the second is -a boolean flag that enables the process to set a watch. In the code the flag is true. -

-
-        /**
-         * Join barrier
-         *
-         * @return
-         * @throws KeeperException
-         * @throws InterruptedException
-         */
-
-        boolean enter() throws KeeperException, InterruptedException{
-            zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
-                    CreateMode.EPHEMERAL_SEQUENTIAL);
-            while (true) {
-                synchronized (mutex) {
-                    List<String> list = zk.getChildren(root, true);
-
-                    if (list.size() < size) {
-                        mutex.wait();
-                    } else {
-                        return true;
-                    }
-                }
-            }
-        }
-
-

-Note that enter() throws both KeeperException and InterruptedException, so it is -the reponsability of the application to catch and handle such exceptions.

-

-Once the computation is finished, a process calls leave() to leave the barrier. -First it deletes its corresponding node, and then it gets the children of the root -node. If there is at least one child, then it waits for a notification (obs: note -that the second parameter of the call to getChildren() is true, meaning that -ZooKeeper has to set a watch on the the root node). Upon reception of a notification, -it checks once more whether the root node has any child.

-
-        /**
-         * Wait until all reach barrier
-         *
-         * @return
-         * @throws KeeperException
-         * @throws InterruptedException
-         */
-
-        boolean leave() throws KeeperException, InterruptedException{
-            zk.delete(root + "/" + name, 0);
-            while (true) {
-                synchronized (mutex) {
-                    List<String> list = zk.getChildren(root, true);
-                        if (list.size() > 0) {
-                            mutex.wait();
-                        } else {
-                            return true;
-                        }
-                    }
-                }
-        }
-    }
-
-
- - -

Producer-Consumer Queues

-
-

-A producer-consumer queue is a distributed data estructure thata group of processes -use to generate and consume items. Producer processes create new elements and add -them to the queue. Consumer processes remove elements from the list, and process them. -In this implementation, the elements are simple integers. The queue is represented -by a root node, and to add an element to the queue, a producer process creates a new node, -a child of the root node. -

-

-The following excerpt of code corresponds to the constructor of the object. As -with Barrier objects, it first calls the constructor of the parent class, SyncPrimitive, -that creates a ZooKeeper object if one doesn't exist. It then verifies if the root -node of the queue exists, and creates if it doesn't. -

-
-        /**
-         * Constructor of producer-consumer queue
-         *
-         * @param address
-         * @param name
-         */
-        Queue(String address, String name) {
-            super(address);
-            this.root = name;
-            // Create ZK node name
-            if (zk != null) {
-                try {
-                    Stat s = zk.exists(root, false);
-                    if (s == null) {
-                        zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
-                                CreateMode.PERSISTENT);
-                    }
-                } catch (KeeperException e) {
-                    System.out
-                            .println("Keeper exception when instantiating queue: "
-                                    + e.toString());
-                } catch (InterruptedException e) {
-                    System.out.println("Interrupted exception");
-                }
-            }
-        }
-
-

-A producer process calls "produce()" to add an element to the queue, and passes -an integer as an argument. To add an element to the queue, the method creates a -new node using "create()", and uses the SEQUENCE flag to instruct ZooKeeper to -append the value of the sequencer counter associated to the root node. In this way, -we impose a total order on the elements of the queue, thus guaranteeing that the -oldest element of the queue is the next one consumed. -

-
-        /**
-         * Add element to the queue.
-         *
-         * @param i
-         * @return
-         */
-
-        boolean produce(int i) throws KeeperException, InterruptedException{
-            ByteBuffer b = ByteBuffer.allocate(4);
-            byte[] value;
-
-            // Add child with value i
-            b.putInt(i);
-            value = b.array();
-            zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE,
-                        CreateMode.PERSISTENT_SEQUENTIAL);
-
-            return true;
-        }
-
-

-To consume an element, a consumer process obtains the children of the root node, -reads the node with smallest counter value, and returns the element. Note that -if there is a conflict, then one of the two contending processes won't be able to -delete the node and the delete operation will throw an exception.

-

-A call to getChildren() returns the list of children in lexicographic order. -As lexicographic order does not necessary follow the numerical order of the counter -values, we need to decide which element is the smallest. To decide which one has -the smallest counter value, we traverse the list, and remove the prefix "element" -from each one.

-
-        /**
-         * Remove first element from the queue.
-         *
-         * @return
-         * @throws KeeperException
-         * @throws InterruptedException
-         */
-        int consume() throws KeeperException, InterruptedException{
-            int retvalue = -1;
-            Stat stat = null;
-
-            // Get the first element available
-            while (true) {
-                synchronized (mutex) {
-                    List<String> list = zk.getChildren(root, true);
-                    if (list.size() == 0) {
-                        System.out.println("Going to wait");
-                        mutex.wait();
-                    } else {
-                        Integer min = new Integer(list.get(0).substring(7));
-                        for(String s : list){
-                            Integer tempValue = new Integer(s.substring(7));
-                            //System.out.println("Temporary value: " + tempValue);
-                            if(tempValue < min) min = tempValue;
-                        }
-                        System.out.println("Temporary value: " + root + "/element" + min);
-                        byte[] b = zk.getData(root + "/element" + min,
-                                    false, stat);
-                        zk.delete(root + "/element" + min, 0);
-                        ByteBuffer buffer = ByteBuffer.wrap(b);
-                        retvalue = buffer.getInt();
-
-                        return retvalue;
-                    }
-                }
-            }
-        }
-    }
-
-
- - -

Complete Source Listing

-
-
-
SyncPrimitive.Java
-
- -SyncPrimitive.Java - -
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Random;
-
-import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.KeeperException;
-import org.apache.zookeeper.WatchedEvent;
-import org.apache.zookeeper.Watcher;
-import org.apache.zookeeper.ZooKeeper;
-import org.apache.zookeeper.ZooDefs.Ids;
-import org.apache.zookeeper.data.Stat;
-
-public class SyncPrimitive implements Watcher {
-
-    static ZooKeeper zk = null;
-    static Integer mutex;
-
-    String root;
-
-    SyncPrimitive(String address) {
-        if(zk == null){
-            try {
-                System.out.println("Starting ZK:");
-                zk = new ZooKeeper(address, 3000, this);
-                mutex = new Integer(-1);
-                System.out.println("Finished starting ZK: " + zk);
-            } catch (IOException e) {
-                System.out.println(e.toString());
-                zk = null;
-            }
-        }
-        //else mutex = new Integer(-1);
-    }
-
-    synchronized public void process(WatchedEvent event) {
-        synchronized (mutex) {
-            //System.out.println("Process: " + event.getType());
-            mutex.notify();
-        }
-    }
-
-    /**
-     * Barrier
-     */
-    static public class Barrier extends SyncPrimitive {
-        int size;
-        String name;
-
-        /**
-         * Barrier constructor
-         *
-         * @param address
-         * @param root
-         * @param size
-         */
-        Barrier(String address, String root, int size) {
-            super(address);
-            this.root = root;
-            this.size = size;
-
-            // Create barrier node
-            if (zk != null) {
-                try {
-                    Stat s = zk.exists(root, false);
-                    if (s == null) {
-                        zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
-                                CreateMode.PERSISTENT);
-                    }
-                } catch (KeeperException e) {
-                    System.out
-                            .println("Keeper exception when instantiating queue: "
-                                    + e.toString());
-                } catch (InterruptedException e) {
-                    System.out.println("Interrupted exception");
-                }
-            }
-
-            // My node name
-            try {
-                name = new String(InetAddress.getLocalHost().getCanonicalHostName().toString());
-            } catch (UnknownHostException e) {
-                System.out.println(e.toString());
-            }
-
-        }
-
-        /**
-         * Join barrier
-         *
-         * @return
-         * @throws KeeperException
-         * @throws InterruptedException
-         */
-
-        boolean enter() throws KeeperException, InterruptedException{
-            zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE,
-                    CreateMode.EPHEMERAL_SEQUENTIAL);
-            while (true) {
-                synchronized (mutex) {
-                    List<String> list = zk.getChildren(root, true);
-
-                    if (list.size() < size) {
-                        mutex.wait();
-                    } else {
-                        return true;
-                    }
-                }
-            }
-        }
-
-        /**
-         * Wait until all reach barrier
-         *
-         * @return
-         * @throws KeeperException
-         * @throws InterruptedException
-         */
-
-        boolean leave() throws KeeperException, InterruptedException{
-            zk.delete(root + "/" + name, 0);
-            while (true) {
-                synchronized (mutex) {
-                    List<String> list = zk.getChildren(root, true);
-                        if (list.size() > 0) {
-                            mutex.wait();
-                        } else {
-                            return true;
-                        }
-                    }
-                }
-        }
-    }
-
-    /**
-     * Producer-Consumer queue
-     */
-    static public class Queue extends SyncPrimitive {
-
-        /**
-         * Constructor of producer-consumer queue
-         *
-         * @param address
-         * @param name
-         */
-        Queue(String address, String name) {
-            super(address);
-            this.root = name;
-            // Create ZK node name
-            if (zk != null) {
-                try {
-                    Stat s = zk.exists(root, false);
-                    if (s == null) {
-                        zk.create(root, new byte[0], Ids.OPEN_ACL_UNSAFE,
-                                CreateMode.PERSISTENT);
-                    }
-                } catch (KeeperException e) {
-                    System.out
-                            .println("Keeper exception when instantiating queue: "
-                                    + e.toString());
-                } catch (InterruptedException e) {
-                    System.out.println("Interrupted exception");
-                }
-            }
-        }
-
-        /**
-         * Add element to the queue.
-         *
-         * @param i
-         * @return
-         */
-
-        boolean produce(int i) throws KeeperException, InterruptedException{
-            ByteBuffer b = ByteBuffer.allocate(4);
-            byte[] value;
-
-            // Add child with value i
-            b.putInt(i);
-            value = b.array();
-            zk.create(root + "/element", value, Ids.OPEN_ACL_UNSAFE,
-                        CreateMode.PERSISTENT_SEQUENTIAL);
-
-            return true;
-        }
-
-
-        /**
-         * Remove first element from the queue.
-         *
-         * @return
-         * @throws KeeperException
-         * @throws InterruptedException
-         */
-        int consume() throws KeeperException, InterruptedException{
-            int retvalue = -1;
-            Stat stat = null;
-
-            // Get the first element available
-            while (true) {
-                synchronized (mutex) {
-                    List<String> list = zk.getChildren(root, true);
-                    if (list.size() == 0) {
-                        System.out.println("Going to wait");
-                        mutex.wait();
-                    } else {
-                        Integer min = new Integer(list.get(0).substring(7));
-                        for(String s : list){
-                            Integer tempValue = new Integer(s.substring(7));
-                            //System.out.println("Temporary value: " + tempValue);
-                            if(tempValue < min) min = tempValue;
-                        }
-                        System.out.println("Temporary value: " + root + "/element" + min);
-                        byte[] b = zk.getData(root + "/element" + min,
-                                    false, stat);
-                        zk.delete(root + "/element" + min, 0);
-                        ByteBuffer buffer = ByteBuffer.wrap(b);
-                        retvalue = buffer.getInt();
-
-                        return retvalue;
-                    }
-                }
-            }
-        }
-    }
-
-    public static void main(String args[]) {
-        if (args[0].equals("qTest"))
-            queueTest(args);
-        else
-            barrierTest(args);
-
-    }
-
-    public static void queueTest(String args[]) {
-        Queue q = new Queue(args[1], "/app1");
-
-        System.out.println("Input: " + args[1]);
-        int i;
-        Integer max = new Integer(args[2]);
-
-        if (args[3].equals("p")) {
-            System.out.println("Producer");
-            for (i = 0; i < max; i++)
-                try{
-                    q.produce(10 + i);
-                } catch (KeeperException e){
-
-                } catch (InterruptedException e){
-
-                }
-        } else {
-            System.out.println("Consumer");
-
-            for (i = 0; i < max; i++) {
-                try{
-                    int r = q.consume();
-                    System.out.println("Item: " + r);
-                } catch (KeeperException e){
-                    i--;
-                } catch (InterruptedException e){
-
-                }
-            }
-        }
-    }
-
-    public static void barrierTest(String args[]) {
-        Barrier b = new Barrier(args[1], "/b1", new Integer(args[2]));
-        try{
-            boolean flag = b.enter();
-            System.out.println("Entered barrier: " + args[2]);
-            if(!flag) System.out.println("Error when entering the barrier");
-        } catch (KeeperException e){
-
-        } catch (InterruptedException e){
-
-        }
-
-        // Generate random integer
-        Random rand = new Random();
-        int r = rand.nextInt(100);
-        // Loop for rand iterations
-        for (int i = 0; i < r; i++) {
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException e) {
-
-            }
-        }
-        try{
-            b.leave();
-        } catch (KeeperException e){
-
-        } catch (InterruptedException e){
-
-        }
-        System.out.println("Left barrier");
-    }
-}
-
-
-
-
- - -

- -

-
- -
 
-
- - - diff --git a/docs/zookeeperTutorial.pdf b/docs/zookeeperTutorial.pdf deleted file mode 100644 index bd11227a50f77645cabc6e3921252b77385865c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28912 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 diff --git a/ivy.xml b/ivy.xml deleted file mode 100644 index 9dded65285a..00000000000 --- a/ivy.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - ZooKeeper - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ivysettings.xml b/ivysettings.xml deleted file mode 100644 index 52cfa52dfb8..00000000000 --- a/ivysettings.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/java/main/overview.html b/overview.html similarity index 100% rename from src/java/main/overview.html rename to overview.html diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000000..6d4b58cc259 --- /dev/null +++ b/pom.xml @@ -0,0 +1,71 @@ + + + + + 4.0.0 + org.apache.zookeeper + zookeeper + jar + 3.3.5 + + + log4j + log4j + 1.2.15 + compile + + + jline + jline + 0.9.94 + compile + + + junit + junit + 4.8.1 + test + + + checkstyle + checkstyle + 5.0 + test + + + jdiff + jdiff + 1.0.9 + true + + + xerces + xerces + 1.4.4 + true + + + org.apache.rat + apache-rat-tasks + 0.6 + true + + + commons-lang + commons-lang + 2.4 + true + + + commons-collections + commons-collections + 3.1 + true + + + diff --git a/src/c/ChangeLog b/src/c/ChangeLog deleted file mode 100644 index c85a6c0ad86..00000000000 --- a/src/c/ChangeLog +++ /dev/null @@ -1,116 +0,0 @@ -Release 2.1.1 -2008-04-30 Andrew Kornev - - * changed the distributino package name to "c-client-src" - -Release 2.1.0 -2008-04-30 Andrew Kornev - - * added the client latency diagnostics; the client prints a warning when the - reponse latency exceeds 20ms - - * modified logging format to report the znode path for which the zookeeper - operation is called - - * fixed a minor bug where error messages were missing for some of the newer - zookeeper error codes (ZCLOSING and ZNOTHING). - - * improved logging by adding the XID to the message to make it easy to match - requests to responses - - * fixed the bug causing sporadic session termination and timeouts - - * added a new return code to zookeeper_process() -- ZNOTHING -- - that indicates that the socket has no more data to read - - * more unit tests added - -Release 1.1.3 -2008-02-07 Andrew Kornev - - * get_xid() is not thread-safe (xid initialization race condition - in the multi-threaded mode). - - * the I/O thread doesn’t automatically terminate on AUTH_FAILURE and - SESSION_EXPIRED events. - - * all session events should be processed on the completion thread. - - * PING operation doesn’t atomically enqueue the completion and - send buffers like other operations do. - - * corrected zookeeper_init() doxygen docs. - - * new unit tests added. - - -Release 1.1.2 -2008-01-24 Andrew Kornev - - * fixed a race condition caused by the code in zookeeper_process() - and free_completions() setting sc->complete to 1 without proper - synchronization; - - * fixed zoo_get() not updating buffer_len value with the actual - buffer length on return; added missing enter_critical/leave_critical - calls to the async ZK operations. - - * Replaced select() with poll() to fix the problem with the FD_SET - macro causing stack corruption for FDs higher than 1024 - - * Added zoo_set_log_stream() to the public API. The function allows - applications to specify a different log file. - - * Removed unused declarations from zookeeper.h (ACL related) - - * changed zoo_get() signature to take a pointer to buffer length. - The function sets the parameter to the actual data length upon return. - - * the watcher callback now takes the zhandle as its first parameter. This - is to avoid a race condition in the multi-threaded client when a watcher - is called before zookeeper_init() has returned. - - * fixed zookeeper_close() resource leaks and race conditions, - fixed the race condition causing xid mismatch. - - * added support for cppunit, added new targets: "check" and "run-check" - to build and run unit tests. - - * Changed the signature of zookeeper_init(): it now takes a context pointer - as a parameter. This is to avoid a race condition in the multi-threaded client. - - * Using a self-pipe rather than SIGUSR1 to wake up select() in the I/O thread - - * Added the doxygen target to the autoconf scripts - - * Pulled out the logging functionality from zookeeper.c to zk_log.c/.h. - Fixed a minor issue with PING responses being unnecessarily put on - the completion queue rather than simply dropped. Make use of DLL_EXPORT - symbol for building shared lib on cygwin. - - * Implemented new Zookeeper operation sync() to flush the leader channel - to ensure that all updates have reached the followers. - - * Synchronous methods not being handled properly on disconnect - - * breed: fixed an incorrect parameter passed to zookeeper API by - the Sync API wrapper functions - - * breed: the set and delete commands now support both Sync and Async API. - Prefix the command name with an 'a' to call the Async API: aset, adelete - - * Make sure mutexes and condition variables are properly initialized - and destroyed - - * Fixed zookeeper_close() causing core dumps with mt_adaptor - - -Release 1.0.0 -2007-11-27 Andrew Kornev - - * configure.ac and Makefile.am added support for GNU autotools - - * recordio.c/.h updated jute IO routines to use bit-explicit integer types - (int32_t vs. int, and int64_t vs. long long) - - * README rough draft \ No newline at end of file diff --git a/src/c/INSTALL b/src/c/INSTALL deleted file mode 100644 index 5458714e1e2..00000000000 --- a/src/c/INSTALL +++ /dev/null @@ -1,234 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006 Free Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - -Basic Installation -================== - -Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - -You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - -Installation Names -================== - -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - -Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - -`configure' recognizes the following options to control how it operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/src/c/LICENSE b/src/c/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/src/c/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/c/Makefile.am b/src/c/Makefile.am deleted file mode 100644 index 65830fe47ea..00000000000 --- a/src/c/Makefile.am +++ /dev/null @@ -1,108 +0,0 @@ -# need this for Doxygen integration -include $(top_srcdir)/aminclude.am - -AM_CPPFLAGS = -I${srcdir}/include -I${srcdir}/tests -I${srcdir}/generated -AM_CFLAGS = -Wall -Werror -AM_CXXFLAGS = -Wall $(USEIPV6) - -LIB_LDFLAGS = -no-undefined -version-info 2 - -pkginclude_HEADERS = include/zookeeper.h include/zookeeper_version.h include/zookeeper_log.h include/recordio.h generated/zookeeper.jute.h -EXTRA_DIST=LICENSE - -HASHTABLE_SRC = src/hashtable/hashtable_itr.h src/hashtable/hashtable_itr.c \ - src/hashtable/hashtable_private.h src/hashtable/hashtable.h src/hashtable/hashtable.c - -noinst_LTLIBRARIES = libhashtable.la -libhashtable_la_SOURCES = $(HASHTABLE_SRC) - -COMMON_SRC = src/zookeeper.c include/zookeeper.h include/zookeeper_version.h include/zookeeper_log.h\ - src/recordio.c include/recordio.h include/proto.h \ - src/zk_adaptor.h generated/zookeeper.jute.c \ - src/zookeeper_log.h src/zk_log.c src/zk_hashtable.h src/zk_hashtable.c - -# These are the symbols (classes, mostly) we want to export from our library. -EXPORT_SYMBOLS = '(zoo_|zookeeper_|zhandle|Z|format_log_message|log_message|logLevel|deallocate_|zerror|is_unrecoverable)' -noinst_LTLIBRARIES += libzkst.la -libzkst_la_SOURCES =$(COMMON_SRC) src/st_adaptor.c -libzkst_la_LIBADD = -lm - -lib_LTLIBRARIES = libzookeeper_st.la -libzookeeper_st_la_SOURCES = -libzookeeper_st_la_LIBADD=libzkst.la libhashtable.la -libzookeeper_st_la_DEPENDENCIES=libzkst.la libhashtable.la -libzookeeper_st_la_LDFLAGS = $(LIB_LDFLAGS) -export-symbols-regex $(EXPORT_SYMBOLS) - -if WANT_SYNCAPI -noinst_LTLIBRARIES += libzkmt.la -libzkmt_la_SOURCES =$(COMMON_SRC) src/mt_adaptor.c -libzkmt_la_CFLAGS = -DTHREADED -libzkmt_la_LIBADD = -lm - -lib_LTLIBRARIES += libzookeeper_mt.la -libzookeeper_mt_la_SOURCES = -libzookeeper_mt_la_LIBADD=libzkmt.la libhashtable.la -lpthread -libzookeeper_mt_la_DEPENDENCIES=libzkmt.la libhashtable.la -libzookeeper_mt_la_LDFLAGS = $(LIB_LDFLAGS) -export-symbols-regex $(EXPORT_SYMBOLS) -endif - -bin_PROGRAMS = cli_st - -cli_st_SOURCES = src/cli.c -cli_st_LDADD = libzookeeper_st.la - -if WANT_SYNCAPI -bin_PROGRAMS += cli_mt load_gen - -cli_mt_SOURCES = src/cli.c -cli_mt_LDADD = libzookeeper_mt.la -cli_mt_CFLAGS = -DTHREADED - -load_gen_SOURCES = src/load_gen.c -load_gen_LDADD = libzookeeper_mt.la -load_gen_CFLAGS = -DTHREADED - -endif - -######################################################################### -# build and run unit tests - -EXTRA_DIST+=$(wildcard ${srcdir}/tests/*.cc) $(wildcard ${srcdir}/tests/*.h) \ - ${srcdir}/tests/wrappers.opt ${srcdir}/tests/wrappers-mt.opt - -TEST_SOURCES = tests/TestDriver.cc tests/LibCMocks.cc tests/LibCSymTable.cc \ - tests/MocksBase.cc tests/ZKMocks.cc tests/Util.cc tests/ThreadingUtil.cc \ - tests/TestClientRetry.cc \ - tests/TestOperations.cc tests/TestZookeeperInit.cc \ - tests/TestZookeeperClose.cc tests/TestClient.cc \ - tests/TestWatchers.cc - - -SYMBOL_WRAPPERS=$(shell cat ${srcdir}/tests/wrappers.opt) - -check_PROGRAMS = zktest-st -nodist_zktest_st_SOURCES = $(TEST_SOURCES) -zktest_st_LDADD = libzkst.la libhashtable.la $(CPPUNIT_LIBS) -zktest_st_CXXFLAGS = -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(USEIPV6) -zktest_st_LDFLAGS = -static-libtool-libs $(SYMBOL_WRAPPERS) - -if WANT_SYNCAPI - check_PROGRAMS += zktest-mt - nodist_zktest_mt_SOURCES = $(TEST_SOURCES) tests/PthreadMocks.cc - zktest_mt_LDADD = libzkmt.la libhashtable.la -lpthread $(CPPUNIT_LIBS) - zktest_mt_CXXFLAGS = -DUSE_STATIC_LIB -DTHREADED $(CPPUNIT_CFLAGS) $(USEIPV6) - SYMBOL_WRAPPERS_MT=$(SYMBOL_WRAPPERS) $(shell cat ${srcdir}/tests/wrappers-mt.opt) - zktest_mt_LDFLAGS = -static-libtool-libs $(SYMBOL_WRAPPERS_MT) -endif - -run-check: check - ./zktest-st $(TEST_OPTIONS) -if WANT_SYNCAPI - ./zktest-mt $(TEST_OPTIONS) -endif - -clean-local: clean-check - $(RM) $(DX_CLEANFILES) - -clean-check: - $(RM) $(nodist_zktest_st_OBJECTS) $(nodist_zktest_mt_OBJECTS) diff --git a/src/c/README b/src/c/README deleted file mode 100644 index 397d04c0d04..00000000000 --- a/src/c/README +++ /dev/null @@ -1,124 +0,0 @@ - Zookeeper C client library - - -This package provides a C client interface to Zookeeper server. - -For the latest information about ZooKeeper, please visit our website at: - http://hadoop.apache.org/zookeeper/ -and our wiki, at: - http://wiki.apache.org/hadoop/ZooKeeper - -Full documentation for this release can also be found in ../../docs/index.html - - -OVERVIEW - -The client supports two types of APIs -- synchronous and asynchronous. - -Asynchronous API provides non-blocking operations with completion callbacks and -relies on the application to implement event multiplexing on its behalf. - -On the other hand, Synchronous API provides a blocking flavor of -zookeeper operations and runs its own event loop in a separate thread. - -Sync and Async APIs can be mixed and matched within the same application. - -The package includes two shared libraries: zookeeper_st and -zookeeper_mt. The former only provides the Async API and is not -thread-safe. The only reason this library exists is to support the -platforms were pthread library is not available or unstable -(i.e. FreeBSD 4.x). In all other cases the application developers are -advised to link against zookeeper_mt as it includes support for both -Sync and Async API. - - -INSTALLATION - -If you're building the client from a source checkout you need to -follow the steps outlined below. If you're building from a release -tar downloaded from Apache please skip to step 2. - -1) do a "ant compile_jute" from the zookeeper top level directory (.../trunk). - This will create a directory named "generated" under src/c. Skip to step 3. -2) unzip/untar the source tarball and cd to the zookeeper-x.x.x/src/c directory -3) change directory to src/c and do a "autoreconf -if" to bootstrap - autoconf, automake and libtool. Please make sure you have autoconf - version 2.59 or greater installed. -4) do a "./configure [OPTIONS]" to generate the makefile. See INSTALL - for general information about running configure. Additionally, the - configure supports the following options: - --enable-debug enables optimization and enables debug info compiler - options, disabled by default - --without-syncapi disables Sync API support; zookeeper_mt library won't - be built, enabled by default - --disable-static do not build static libraries, enabled by default - --disable-shared do not build shared libraries, enabled by default - --without-cppunit do not build the test library, enabled by default. - -5) do a "make" or "make install" to build the libraries and install them. - Alternatively, you can also build and run a unit test suite (and - you probably should). Please make sure you have cppunit-1.10.x or - higher installed before you execute step 4. Once ./configure has - finished, do a "make run-check". It will build the libraries, build - the tests and run them. -6) to generate doxygen documentation do a "make doxygen-doc". All - documentations will be placed to a new subfolder named docs. By - default only HTML documentation is generated. For information on - other document formats please use "./configure --help" - - -USING THE CLIENT - -You can test your client by running a zookeeper server (see -instructions on the project wiki page on how to run it) and connecting -to it using the zookeeper shell application cli that is built as part -of the installation procedure. - -cli_mt (multithreaded, built against zookeeper_mt library) is shown in -this example, but you could also use cli_st (singlethreaded, built -against zookeeper_st library): - -$ cli_mt zookeeper_host:9876 - -This is a client application that gives you a shell for executing -simple zookeeper commands. Once successfully started and connected to -the server it displays a shell prompt. - -You can now enter zookeeper commands. For example, to create a node: - -> create /my_new_node - -To verify that the node's been created: - -> ls / - -You should see a list of nodes who are the children of the root node "/". - -Here's a list of command supported by the cli shell: - -ls -- list children of a znode identified by . The - command set a children watch on the znode. -get -- get the value of a znode at -set -- set the value of a znode at to -create [+e|+s] -- create a znode as a child of znode ; - use +e option to create an ephemeral znode, - use +s option to create a znode with a sequence number - appended to the name. The operation will fail if - the parent znode (the one identified by ) doesn't - exist. -delete -- delete the znode at . The command will fail if the znode - has children. -sync -- make sure all pending updates have been applied to znode at -exists -- returns a result code indicating whether the znode at - exists. The command also sets a znode watch. -myid -- prints out the current zookeeper session id. -quit -- exit the shell. - -In order to be able to use the zookeeper API in your application you have to -1) remember to include the zookeeper header - #include -2) use -DTHREADED compiler option to enable Sync API; in this case you should - be linking your code against zookeeper_mt library - -Please take a look at cli.c to understand how to use the two API types. -(TODO: some kind of short tutorial would be helpful, I guess) diff --git a/src/c/acinclude.m4 b/src/c/acinclude.m4 deleted file mode 100644 index d0041d8c26a..00000000000 --- a/src/c/acinclude.m4 +++ /dev/null @@ -1,312 +0,0 @@ -# This file is part of Autoconf. -*- Autoconf -*- - -# Copyright (C) 2004 Oren Ben-Kiki -# This file is distributed under the same terms as the Autoconf macro files. - -# Generate automatic documentation using Doxygen. Works in concert with the -# aminclude.m4 file and a compatible doxygen configuration file. Defines the -# following public macros: -# -# DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature. -# Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics, -# 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI' -# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF', -# 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment -# variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide' -# paper size. -# -# By default, HTML, PDF and PS documentation is generated as this seems to be -# the most popular and portable combination. MAN pages created by Doxygen are -# usually problematic, though by picking an appropriate subset and doing some -# massaging they might be better than nothing. CHM and RTF are specific for MS -# (note that you can't generate both HTML and CHM at the same time). The XML is -# rather useless unless you apply specialized post-processing to it. -# -# The macro mainly controls the default state of the feature. The use can -# override the default by specifying --enable or --disable. The macros ensure -# that contradictory flags are not given (e.g., --enable-doxygen-html and -# --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.) -# Finally, each feature will be automatically disabled (with a warning) if the -# required programs are missing. -# -# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with -# the following parameters: a one-word name for the project for use as a -# filename base etc., an optional configuration file name (the default is -# 'Doxyfile', the same as Doxygen's default), and an optional output directory -# name (the default is 'doxygen-doc'). - -## ----------## -## Defaults. ## -## ----------## - -DX_ENV="" -AC_DEFUN([DX_FEATURE_doc], ON) -AC_DEFUN([DX_FEATURE_dot], ON) -AC_DEFUN([DX_FEATURE_man], OFF) -AC_DEFUN([DX_FEATURE_html], ON) -AC_DEFUN([DX_FEATURE_chm], OFF) -AC_DEFUN([DX_FEATURE_chi], OFF) -AC_DEFUN([DX_FEATURE_rtf], OFF) -AC_DEFUN([DX_FEATURE_xml], OFF) -AC_DEFUN([DX_FEATURE_pdf], ON) -AC_DEFUN([DX_FEATURE_ps], ON) - -## --------------- ## -## Private macros. ## -## --------------- ## - -# DX_ENV_APPEND(VARIABLE, VALUE) -# ------------------------------ -# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. -AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) - -# DX_DIRNAME_EXPR -# --------------- -# Expand into a shell expression prints the directory part of a path. -AC_DEFUN([DX_DIRNAME_EXPR], - [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) - -# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) -# ------------------------------------- -# Expands according to the M4 (static) status of the feature. -AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) - -# DX_REQUIRE_PROG(VARIABLE, PROGRAM) -# ---------------------------------- -# Require the specified program to be found for the DX_CURRENT_FEATURE to work. -AC_DEFUN([DX_REQUIRE_PROG], [ -AC_PATH_TOOL([$1], [$2]) -if test "$DX_FLAG_$[DX_CURRENT_FEATURE$$1]" = 1; then - AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) - AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0) -fi -]) - -# DX_TEST_FEATURE(FEATURE) -# ------------------------ -# Expand to a shell expression testing whether the feature is active. -AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) - -# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) -# ------------------------------------------------- -# Verify that a required features has the right state before trying to turn on -# the DX_CURRENT_FEATURE. -AC_DEFUN([DX_CHECK_DEPEND], [ -test "$DX_FLAG_$1" = "$2" \ -|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, - requires, contradicts) doxygen-DX_CURRENT_FEATURE]) -]) - -# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) -# ---------------------------------------------------------- -# Turn off the DX_CURRENT_FEATURE if the required feature is off. -AC_DEFUN([DX_CLEAR_DEPEND], [ -test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0) -]) - -# DX_FEATURE_ARG(FEATURE, DESCRIPTION, -# CHECK_DEPEND, CLEAR_DEPEND, -# REQUIRE, DO-IF-ON, DO-IF-OFF) -# -------------------------------------------- -# Parse the command-line option controlling a feature. CHECK_DEPEND is called -# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), -# otherwise CLEAR_DEPEND is called to turn off the default state if a required -# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional -# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and -# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. -AC_DEFUN([DX_ARG_ABLE], [ - AC_DEFUN([DX_CURRENT_FEATURE], [$1]) - AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) - AC_ARG_ENABLE(doxygen-$1, - [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], - [--enable-doxygen-$1]), - DX_IF_FEATURE([$1], [don't $2], [$2]))], - [ -case "$enableval" in -#( -y|Y|yes|Yes|YES) - AC_SUBST([DX_FLAG_$1], 1) - $3 -;; #( -n|N|no|No|NO) - AC_SUBST([DX_FLAG_$1], 0) -;; #( -*) - AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) -;; -esac -], [ -AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) -$4 -]) -if DX_TEST_FEATURE([$1]); then - $5 - : -fi -if DX_TEST_FEATURE([$1]); then - AM_CONDITIONAL(DX_COND_$1, :) - $6 - : -else - AM_CONDITIONAL(DX_COND_$1, false) - $7 - : -fi -]) - -## -------------- ## -## Public macros. ## -## -------------- ## - -# DX_XXX_FEATURE(DEFAULT_STATE) -# ----------------------------- -AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) -AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) -AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) -AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) -AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) -AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) -AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) -AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) -AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) -AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) - -# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) -# --------------------------------------------------------- -# PROJECT also serves as the base name for the documentation files. -# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". -AC_DEFUN([DX_INIT_DOXYGEN], [ - -# Files: -AC_SUBST([DX_PROJECT], [$1]) -AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) -AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) - -# Environment variables used inside doxygen.cfg: -DX_ENV_APPEND(SRCDIR, $srcdir) -DX_ENV_APPEND(PROJECT, $DX_PROJECT) -DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) -DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) - -# Doxygen itself: -DX_ARG_ABLE(doc, [generate any doxygen documentation], - [], - [], - [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) - DX_REQUIRE_PROG([DX_PERL], perl)], - [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) - -# Dot for graphics: -DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_DOT], dot)], - [DX_ENV_APPEND(HAVE_DOT, YES) - DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], - [DX_ENV_APPEND(HAVE_DOT, NO)]) - -# Man pages generation: -DX_ARG_ABLE(man, [generate doxygen manual pages], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [], - [DX_ENV_APPEND(GENERATE_MAN, YES)], - [DX_ENV_APPEND(GENERATE_MAN, NO)]) - -# RTF file generation: -DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [], - [DX_ENV_APPEND(GENERATE_RTF, YES)], - [DX_ENV_APPEND(GENERATE_RTF, NO)]) - -# XML file generation: -DX_ARG_ABLE(xml, [generate doxygen XML documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [], - [DX_ENV_APPEND(GENERATE_XML, YES)], - [DX_ENV_APPEND(GENERATE_XML, NO)]) - -# (Compressed) HTML help generation: -DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_HHC], hhc)], - [DX_ENV_APPEND(HHC_PATH, $DX_HHC) - DX_ENV_APPEND(GENERATE_HTML, YES) - DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], - [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) - -# Seperate CHI file generation. -DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], - [DX_CHECK_DEPEND(chm, 1)], - [DX_CLEAR_DEPEND(chm, 1)], - [], - [DX_ENV_APPEND(GENERATE_CHI, YES)], - [DX_ENV_APPEND(GENERATE_CHI, NO)]) - -# Plain HTML pages generation: -DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], - [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], - [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], - [], - [DX_ENV_APPEND(GENERATE_HTML, YES)], - [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) - -# PostScript file generation: -DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_LATEX], latex) - DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) - DX_REQUIRE_PROG([DX_DVIPS], dvips) - DX_REQUIRE_PROG([DX_EGREP], egrep)]) - -# PDF file generation: -DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) - DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) - DX_REQUIRE_PROG([DX_EGREP], egrep)]) - -# LaTeX generation for PS and/or PDF: -if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then - AM_CONDITIONAL(DX_COND_latex, :) - DX_ENV_APPEND(GENERATE_LATEX, YES) -else - AM_CONDITIONAL(DX_COND_latex, false) - DX_ENV_APPEND(GENERATE_LATEX, NO) -fi - -# Paper size for PS and/or PDF: -AC_ARG_VAR(DOXYGEN_PAPER_SIZE, - [a4wide (default), a4, letter, legal or executive]) -case "$DOXYGEN_PAPER_SIZE" in -#( -"") - AC_SUBST(DOXYGEN_PAPER_SIZE, "") -;; #( -a4wide|a4|letter|legal|executive) - DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) -;; #( -*) - AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) -;; -esac - -#For debugging: -#echo DX_FLAG_doc=$DX_FLAG_doc -#echo DX_FLAG_dot=$DX_FLAG_dot -#echo DX_FLAG_man=$DX_FLAG_man -#echo DX_FLAG_html=$DX_FLAG_html -#echo DX_FLAG_chm=$DX_FLAG_chm -#echo DX_FLAG_chi=$DX_FLAG_chi -#echo DX_FLAG_rtf=$DX_FLAG_rtf -#echo DX_FLAG_xml=$DX_FLAG_xml -#echo DX_FLAG_pdf=$DX_FLAG_pdf -#echo DX_FLAG_ps=$DX_FLAG_ps -#echo DX_ENV=$DX_ENV -]) diff --git a/src/c/aminclude.am b/src/c/aminclude.am deleted file mode 100644 index 420049eca35..00000000000 --- a/src/c/aminclude.am +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (C) 2004 Oren Ben-Kiki -# This file is distributed under the same terms as the Automake macro files. - -# Generate automatic documentation using Doxygen. Goals and variables values -# are controlled by the various DX_COND_??? conditionals set by autoconf. -# -# The provided goals are: -# doxygen-doc: Generate all doxygen documentation. -# doxygen-run: Run doxygen, which will generate some of the documentation -# (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post -# processing required for the rest of it (PS, PDF, and some MAN). -# doxygen-man: Rename some doxygen generated man pages. -# doxygen-ps: Generate doxygen PostScript documentation. -# doxygen-pdf: Generate doxygen PDF documentation. -# -# Note that by default these are not integrated into the automake goals. If -# doxygen is used to generate man pages, you can achieve this integration by -# setting man3_MANS to the list of man pages generated and then adding the -# dependency: -# -# $(man3_MANS): doxygen-doc -# -# This will cause make to run doxygen and generate all the documentation. -# -# The following variable is intended for use in Makefile.am: -# -# DX_CLEANFILES = everything to clean. -# -# This is usually added to MOSTLYCLEANFILES. - -## --------------------------------- ## -## Format-independent Doxygen rules. ## -## --------------------------------- ## - -if DX_COND_doc - -## ------------------------------- ## -## Rules specific for HTML output. ## -## ------------------------------- ## - -if DX_COND_html - -DX_CLEAN_HTML = @DX_DOCDIR@/html - -endif DX_COND_html - -## ------------------------------ ## -## Rules specific for CHM output. ## -## ------------------------------ ## - -if DX_COND_chm - -DX_CLEAN_CHM = @DX_DOCDIR@/chm - -if DX_COND_chi - -DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi - -endif DX_COND_chi - -endif DX_COND_chm - -## ------------------------------ ## -## Rules specific for MAN output. ## -## ------------------------------ ## - -if DX_COND_man - -DX_CLEAN_MAN = @DX_DOCDIR@/man - -endif DX_COND_man - -## ------------------------------ ## -## Rules specific for RTF output. ## -## ------------------------------ ## - -if DX_COND_rtf - -DX_CLEAN_RTF = @DX_DOCDIR@/rtf - -endif DX_COND_rtf - -## ------------------------------ ## -## Rules specific for XML output. ## -## ------------------------------ ## - -if DX_COND_xml - -DX_CLEAN_XML = @DX_DOCDIR@/xml - -endif DX_COND_xml - -## ----------------------------- ## -## Rules specific for PS output. ## -## ----------------------------- ## - -if DX_COND_ps - -DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps - -DX_PS_GOAL = doxygen-ps - -doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps - -@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag - cd @DX_DOCDIR@/latex; \ - rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ - $(DX_LATEX) refman.tex; \ - $(MAKEINDEX_PATH) refman.idx; \ - $(DX_LATEX) refman.tex; \ - countdown=5; \ - while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ - refman.log > /dev/null 2>&1 \ - && test $$countdown -gt 0; do \ - $(DX_LATEX) refman.tex; \ - countdown=`expr $$countdown - 1`; \ - done; \ - $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi - -endif DX_COND_ps - -## ------------------------------ ## -## Rules specific for PDF output. ## -## ------------------------------ ## - -if DX_COND_pdf - -DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf - -DX_PDF_GOAL = doxygen-pdf - -doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf - -@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag - cd @DX_DOCDIR@/latex; \ - rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ - $(DX_PDFLATEX) refman.tex; \ - $(DX_MAKEINDEX) refman.idx; \ - $(DX_PDFLATEX) refman.tex; \ - countdown=5; \ - while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ - refman.log > /dev/null 2>&1 \ - && test $$countdown -gt 0; do \ - $(DX_PDFLATEX) refman.tex; \ - countdown=`expr $$countdown - 1`; \ - done; \ - mv refman.pdf ../@PACKAGE@.pdf - -endif DX_COND_pdf - -## ------------------------------------------------- ## -## Rules specific for LaTeX (shared for PS and PDF). ## -## ------------------------------------------------- ## - -if DX_COND_latex - -DX_CLEAN_LATEX = @DX_DOCDIR@/latex - -endif DX_COND_latex - -.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) - -.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) - -doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag - -doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) - -@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS) - rm -rf @DX_DOCDIR@ - $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) - -DX_CLEANFILES = \ - @DX_DOCDIR@/@PACKAGE@.tag \ - -r \ - $(DX_CLEAN_HTML) \ - $(DX_CLEAN_CHM) \ - $(DX_CLEAN_CHI) \ - $(DX_CLEAN_MAN) \ - $(DX_CLEAN_RTF) \ - $(DX_CLEAN_XML) \ - $(DX_CLEAN_PS) \ - $(DX_CLEAN_PDF) \ - $(DX_CLEAN_LATEX) - -endif DX_COND_doc diff --git a/src/c/c-doc.Doxyfile b/src/c/c-doc.Doxyfile deleted file mode 100644 index e5f9e317865..00000000000 --- a/src/c/c-doc.Doxyfile +++ /dev/null @@ -1,1252 +0,0 @@ -# Doxyfile 1.4.7 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = $(PROJECT)-$(VERSION) - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = $(DOCDIR) - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, -# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, -# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, -# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, -# Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# This tag can be used to specify the encoding used in the generated output. -# The encoding is not always determined by the language that is chosen, -# but also whether or not the output is meant for Windows or non-Windows users. -# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES -# forces the Windows encoding (this is the default for the Windows binary), -# whereas setting the tag to NO uses a Unix-style encoding (the default for -# all platforms other than Windows). - -USE_WINDOWS_ENCODING = NO - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like the Qt-style comments (thus requiring an -# explicit @brief command for a brief description. - -JAVADOC_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to -# include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST = YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from the -# version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = include/zookeeper.h - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = $(GENERATE_HTML) - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = $(GENERATE_HTMLHELP) - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = ../$(PROJECT).chm - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = $(HHC_PATH) - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = $(GENERATE_CHI) - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = $(GENERATE_LATEX) - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = $(PAPER_SIZE) - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = $(GENERATE_PDF) - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = $(GENERATE_RTF) - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = $(GENERATE_MAN) - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = $(GENERATE_XML) - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = $(DOCDIR)/$(PROJECT).tag - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = $(HAVE_DOT) - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a caller dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = $(DOT_PATH) - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_WIDTH = 1024 - -# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_HEIGHT = 1024 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that a graph may be further truncated if the graph's -# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH -# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), -# the graph is not depth-constrained. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/src/c/configure.ac b/src/c/configure.ac deleted file mode 100644 index cdea8980a40..00000000000 --- a/src/c/configure.ac +++ /dev/null @@ -1,149 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) - -AC_INIT([zookeeper C client],3.4.0,[zookeeper-user@hadoop.apache.org],[c-client-src]) -AC_CONFIG_SRCDIR([src/zookeeper.c]) - -# Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX -init_cflags="$CFLAGS" -init_cxxflags="$CXXFLAGS" - -# initialize Doxygen support -DX_HTML_FEATURE(ON) -DX_CHM_FEATURE(OFF) -DX_CHI_FEATURE(OFF) -DX_MAN_FEATURE(OFF) -DX_RTF_FEATURE(OFF) -DX_XML_FEATURE(OFF) -DX_PDF_FEATURE(OFF) -DX_PS_FEATURE(OFF) -DX_INIT_DOXYGEN([zookeeper],[c-doc.Doxyfile],[docs]) - -# initialize automake -AM_INIT_AUTOMAKE([-Wall foreign]) -AC_CONFIG_HEADER([config.h]) - -# Checks for programs. -AC_ARG_WITH(cppunit, - [ --without-cppunit do not use CPPUNIT]) - -if test "$with_cppunit" = "no" ; then - CPPUNIT_PATH="No_CPPUNIT" - CPPUNIT_INCLUDE= - CPPUNIT_LIBS= -else - AM_PATH_CPPUNIT(1.10.2) -fi - -if test "$CALLER" = "ANT" ; then -CPPUNIT_CFLAGS="$CPPUNIT_CFLAGS -DZKSERVER_CMD=\"\\\"${base_dir}/src/c/tests/zkServer.sh\\\"\"" -else -CPPUNIT_CFLAGS="$CPPUNIT_CFLAGS -DZKSERVER_CMD=\"\\\"./tests/zkServer.sh\\\"\"" -AC_CHECK_FILES([generated/zookeeper.jute.c generated/zookeeper.jute.h],[], - [AC_MSG_ERROR([jute files are missing! Please run "ant compile_jute" while in the zookeeper top level directory.]) -]) -fi -AC_SUBST(CPPUNIT_CFLAGS) - -AC_PROG_CC -AM_PROG_CC_C_O -AC_PROG_CXX -AC_PROG_INSTALL -AC_PROG_LN_S - -# AC_DISABLE_SHARED -AC_PROG_LIBTOOL - -#enable -D_GNU_SOURCE since the return code value of getaddrinfo -#ifdefed with __USE_GNU -#features.h header undef's __USE_GNU and defines it only if _GNU_SOURCE is defined -#hence this define for gcc -AC_ARG_ENABLE([debug], - [AS_HELP_STRING([--enable-debug],[enable debug build [default=no]])], - [],[enable_debug=no]) - -if test "x$enable_debug" = xyes; then - if test "x$init_cflags" = x; then - CFLAGS="" - fi - CFLAGS="$CFLAGS -g -O0 -D_GNU_SOURCE" -else - if test "x$init_cflags" = x; then - CFLAGS="-g -O2 -D_GNU_SOURCE" - fi -fi - -if test "x$enable_debug" = xyes; then - if test "x$init_cxxflags" = x; then - CXXFLAGS="" - fi - CXXFLAGS="$CXXFLAGS -g -O0" -else - if test "x$init_cxxflags" = x; then - CXXFLAGS="-g -O2" - fi -fi - -AC_ARG_WITH([syncapi], - [AS_HELP_STRING([--with-syncapi],[build with support for SyncAPI [default=yes]])], - [],[with_syncapi=yes]) - -# Checks for libraries. -AC_CHECK_LIB([pthread], [pthread_mutex_lock],[have_pthread=yes],[have_pthread=no]) - -if test "x$with_syncapi" != xno && test "x$have_pthread" = xno; then - AC_MSG_WARN([cannot build SyncAPI -- pthread not found]) - with_syncapi=no -fi -if test "x$with_syncapi" != xno; then - AC_MSG_NOTICE([building with SyncAPI support]) -else - AC_MSG_NOTICE([building without SyncAPI support]) -fi - -AM_CONDITIONAL([WANT_SYNCAPI],[test "x$with_syncapi" != xno]) - -# Checks for header files. -AC_HEADER_STDC -AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h unistd.h sys/utsname.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_C_INLINE -AC_HEADER_TIME -AC_CHECK_TYPE([nfds_t], - [AC_DEFINE([POLL_NFDS_TYPE],[nfds_t],[poll() second argument type])], - [AC_DEFINE([POLL_NFDS_TYPE],[unsigned int],[poll() second argument type])], - [#include ]) - -AC_MSG_CHECKING([whether to enable ipv6]) - -AC_TRY_RUN([ /* is AF_INET6 available? */ -#include -#include -main() -{ - if (socket(AF_INET6, SOCK_STREAM, 0) < 0) - exit(1); - else - exit(0); -} -], AC_MSG_RESULT(yes) - ipv6=yes, - AC_MSG_RESULT(no) - ipv6=no, - AC_MSG_RESULT(no) - ipv6=no) - -if test x"$ipv6" = xyes; then - USEIPV6="-DZOO_IPV6_ENABLED" - AC_SUBST(USEIPV6) -fi - -# Checks for library functions. -AC_CHECK_FUNCS([getcwd gethostbyname gethostname getlogin getpwuid_r gettimeofday getuid memmove memset poll socket strchr strdup strerror strtol]) - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/src/c/include/proto.h b/src/c/include/proto.h deleted file mode 100644 index 843032f5729..00000000000 --- a/src/c/include/proto.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef PROTO_H_ -#define PROTO_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -static const int NOTIFY_OP=0; -static const int CREATE_OP=1; -static const int DELETE_OP=2; -static const int EXISTS_OP=3; -static const int GETDATA_OP=4; -static const int SETDATA_OP=5; -static const int GETACL_OP=6; -static const int SETACL_OP=7; -static const int GETCHILDREN_OP=8; -static const int SYNC_OP=9; -static const int PING_OP=11; -static const int GETCHILDREN2_OP=12; -static const int CLOSE_OP=-11; -static const int SETAUTH_OP=100; -static const int SETWATCHES_OP=101; - -#ifdef __cplusplus -} -#endif - -#endif /*PROTO_H_*/ diff --git a/src/c/include/recordio.h b/src/c/include/recordio.h deleted file mode 100644 index 33b8c707dae..00000000000 --- a/src/c/include/recordio.h +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __RECORDIO_H__ -#define __RECORDIO_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct buffer { - int32_t len; - char *buff; -}; - -void deallocate_String(char **s); -void deallocate_Buffer(struct buffer *b); -void deallocate_vector(void *d); -struct iarchive { - int (*start_record)(struct iarchive *ia, const char *tag); - int (*end_record)(struct iarchive *ia, const char *tag); - int (*start_vector)(struct iarchive *ia, const char *tag, int32_t *count); - int (*end_vector)(struct iarchive *ia, const char *tag); - int (*deserialize_Bool)(struct iarchive *ia, const char *name, int32_t *); - int (*deserialize_Int)(struct iarchive *ia, const char *name, int32_t *); - int (*deserialize_Long)(struct iarchive *ia, const char *name, int64_t *); - int (*deserialize_Buffer)(struct iarchive *ia, const char *name, - struct buffer *); - int (*deserialize_String)(struct iarchive *ia, const char *name, char **); - void *priv; -}; -struct oarchive { - int (*start_record)(struct oarchive *oa, const char *tag); - int (*end_record)(struct oarchive *oa, const char *tag); - int (*start_vector)(struct oarchive *oa, const char *tag, const int32_t *count); - int (*end_vector)(struct oarchive *oa, const char *tag); - int (*serialize_Bool)(struct oarchive *oa, const char *name, const int32_t *); - int (*serialize_Int)(struct oarchive *oa, const char *name, const int32_t *); - int (*serialize_Long)(struct oarchive *oa, const char *name, - const int64_t *); - int (*serialize_Buffer)(struct oarchive *oa, const char *name, - const struct buffer *); - int (*serialize_String)(struct oarchive *oa, const char *name, char **); - void *priv; -}; - -struct oarchive *create_buffer_oarchive(void); -void close_buffer_oarchive(struct oarchive **oa, int free_buffer); -struct iarchive *create_buffer_iarchive(char *buffer, int len); -void close_buffer_iarchive(struct iarchive **ia); -char *get_buffer(struct oarchive *); -int get_buffer_len(struct oarchive *); - -int64_t htonll(int64_t v); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/c/include/zookeeper.h b/src/c/include/zookeeper.h deleted file mode 100644 index 3eea1ea2058..00000000000 --- a/src/c/include/zookeeper.h +++ /dev/null @@ -1,1402 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ZOOKEEPER_H_ -#define ZOOKEEPER_H_ - -#include -#include -#include -#include - -#include "zookeeper_version.h" -#include "recordio.h" -#include "zookeeper.jute.h" - -/** - * \file zookeeper.h - * \brief ZooKeeper functions and definitions. - * - * ZooKeeper is a network service that may be backed by a cluster of - * synchronized servers. The data in the service is represented as a tree - * of data nodes. Each node has data, children, an ACL, and status information. - * The data for a node is read and write in its entirety. - * - * ZooKeeper clients can leave watches when they queries the data or children - * of a node. If a watch is left, that client will be notified of the change. - * The notification is a one time trigger. Subsequent chances to the node will - * not trigger a notification unless the client issues a querity with the watch - * flag set. If the client is ever disconnected from the service, even if the - * disconnection is temporary, the watches of the client will be removed from - * the service, so a client must treat a disconnect notification as an implicit - * trigger of all outstanding watches. - * - * When a node is created, it may be flagged as an ephemeral node. Ephemeral - * nodes are automatically removed when a client session is closed or when - * a session times out due to inactivity (the ZooKeeper runtime fills in - * periods of inactivity with pings). Ephemeral nodes cannot have children. - * - * ZooKeeper clients are identified by a server assigned session id. For - * security reasons The server - * also generates a corresponding password for a session. A client may save its - * id and corresponding password to persistent storage in order to use the - * session across program invocation boundaries. - */ - -/* Support for building on various platforms */ - -// on cygwin we should take care of exporting/importing symbols properly -#ifdef DLL_EXPORT -# define ZOOAPI __declspec(dllexport) -#else -# if defined(__CYGWIN__) && !defined(USE_STATIC_LIB) -# define ZOOAPI __declspec(dllimport) -# else -# define ZOOAPI -# endif -#endif - -/** zookeeper return constants **/ - -enum ZOO_ERRORS { - ZOK = 0, /*!< Everything is OK */ - - /** System and server-side errors. - * This is never thrown by the server, it shouldn't be used other than - * to indicate a range. Specifically error codes greater than this - * value, but lesser than {@link #ZAPIERROR}, are system errors. */ - ZSYSTEMERROR = -1, - ZRUNTIMEINCONSISTENCY = -2, /*!< A runtime inconsistency was found */ - ZDATAINCONSISTENCY = -3, /*!< A data inconsistency was found */ - ZCONNECTIONLOSS = -4, /*!< Connection to the server has been lost */ - ZMARSHALLINGERROR = -5, /*!< Error while marshalling or unmarshalling data */ - ZUNIMPLEMENTED = -6, /*!< Operation is unimplemented */ - ZOPERATIONTIMEOUT = -7, /*!< Operation timeout */ - ZBADARGUMENTS = -8, /*!< Invalid arguments */ - ZINVALIDSTATE = -9, /*!< Invliad zhandle state */ - - /** API errors. - * This is never thrown by the server, it shouldn't be used other than - * to indicate a range. Specifically error codes greater than this - * value are API errors (while values less than this indicate a - * {@link #ZSYSTEMERROR}). - */ - ZAPIERROR = -100, - ZNONODE = -101, /*!< Node does not exist */ - ZNOAUTH = -102, /*!< Not authenticated */ - ZBADVERSION = -103, /*!< Version conflict */ - ZNOCHILDRENFOREPHEMERALS = -108, /*!< Ephemeral nodes may not have children */ - ZNODEEXISTS = -110, /*!< The node already exists */ - ZNOTEMPTY = -111, /*!< The node has children */ - ZSESSIONEXPIRED = -112, /*!< The session has been expired by the server */ - ZINVALIDCALLBACK = -113, /*!< Invalid callback specified */ - ZINVALIDACL = -114, /*!< Invalid ACL specified */ - ZAUTHFAILED = -115, /*!< Client authentication failed */ - ZCLOSING = -116, /*!< ZooKeeper is closing */ - ZNOTHING = -117, /*!< (not error) no server responses to process */ - ZSESSIONMOVED = -118 /*! - * The legacy style, an application wishing to receive events from ZooKeeper must - * first implement a function with this signature and pass a pointer to the function - * to \ref zookeeper_init. Next, the application sets a watch by calling one of - * the getter API that accept the watch integer flag (for example, \ref zoo_aexists, - * \ref zoo_get, etc). - *

- * The watcher object style uses an instance of a "watcher object" which in - * the C world is represented by a pair: a pointer to a function implementing this - * signature and a pointer to watcher context -- handback user-specific data. - * When a watch is triggered this function will be called along with - * the watcher context. An application wishing to use this style must use - * the getter API functions with the "w" prefix in their names (for example, \ref - * zoo_awexists, \ref zoo_wget, etc). - * - * \param zh zookeeper handle - * \param type event type. This is one of the *_EVENT constants. - * \param state connection state. The state value will be one of the *_STATE constants. - * \param path znode path for which the watcher is triggered. NULL if the event - * type is ZOO_SESSION_EVENT - * \param watcherCtx watcher context. - */ -typedef void (*watcher_fn)(zhandle_t *zh, int type, - int state, const char *path,void *watcherCtx); - -/** - * \brief create a handle to used communicate with zookeeper. - * - * This method creates a new handle and a zookeeper session that corresponds - * to that handle. Session establishment is asynchronous, meaning that the - * session should not be considered established until (and unless) an - * event of state ZOO_CONNECTED_STATE is received. - * \param host comma separated host:port pairs, each corresponding to a zk - * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" - * \param fn the global watcher callback function. When notifications are - * triggered this function will be invoked. - * \param clientid the id of a previously established session that this - * client will be reconnecting to. Pass 0 if not reconnecting to a previous - * session. Clients can access the session id of an established, valid, - * connection by calling \ref zoo_client_id. If the session corresponding to - * the specified clientid has expired, or if the clientid is invalid for - * any reason, the returned zhandle_t will be invalid -- the zhandle_t - * state will indicate the reason for failure (typically - * ZOO_EXPIRED_SESSION_STATE). - * \param context the handback object that will be associated with this instance - * of zhandle_t. Application can access it (for example, in the watcher - * callback) using \ref zoo_get_context. The object is not used by zookeeper - * internally and can be null. - * \param flags reserved for future use. Should be set to zero. - * \return a pointer to the opaque zhandle structure. If it fails to create - * a new zhandle the function returns NULL and the errno variable - * indicates the reason. - */ -ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn, - int recv_timeout, const clientid_t *clientid, void *context, int flags); - -/** - * \brief close the zookeeper handle and free up any resources. - * - * After this call, the client session will no longer be valid. The function - * will flush any outstanding send requests before return. As a result it may - * block. - * - * This method should only be called only once on a zookeeper handle. Calling - * twice will cause undefined (and probably undesirable behavior). Calling any other - * zookeeper method after calling close is undefined behaviour and should be avoided. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \return a result code. Regardless of the error code returned, the zhandle - * will be destroyed and all resources freed. - * - * ZOK - success - * ZBADARGUMENTS - invalid input parameters - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZOPERATIONTIMEOUT - failed to flush the buffers within the specified timeout. - * ZCONNECTIONLOSS - a network error occured while attempting to send request to server - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details - */ -ZOOAPI int zookeeper_close(zhandle_t *zh); - -/** - * \brief return the client session id, only valid if the connections - * is currently connected (ie. last watcher state is ZOO_CONNECTED_STATE) - */ -ZOOAPI const clientid_t *zoo_client_id(zhandle_t *zh); - -/** - * \brief return the timeout for this session, only valid if the connections - * is currently connected (ie. last watcher state is ZOO_CONNECTED_STATE). This - * value may change after a server re-connect. - */ -ZOOAPI int zoo_recv_timeout(zhandle_t *zh); - -/** - * \brief return the context for this handle. - */ -ZOOAPI const void *zoo_get_context(zhandle_t *zh); - -/** - * \brief set the context for this handle. - */ -ZOOAPI void zoo_set_context(zhandle_t *zh, void *context); - -/** - * \brief set a watcher function - * \return previous watcher function - */ -ZOOAPI watcher_fn zoo_set_watcher(zhandle_t *zh,watcher_fn newFn); - -#ifndef THREADED -/** - * \brief Returns the events that zookeeper is interested in. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param fd is the file descriptor of interest - * \param interest is an or of the ZOOKEEPER_WRITE and ZOOKEEPER_READ flags to - * indicate the I/O of interest on fd. - * \param tv a timeout value to be used with select/poll system call - * \return a result code. - * ZOK - success - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZCONNECTIONLOSS - a network error occured while attempting to establish - * a connection to the server - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZOPERATIONTIMEOUT - hasn't received anything from the server for 2/3 of the - * timeout value specified in zookeeper_init() - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details - */ -ZOOAPI int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, - struct timeval *tv); - -/** - * \brief Notifies zookeeper that an event of interest has happened. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param events will be an OR of the ZOOKEEPER_WRITE and ZOOKEEPER_READ flags. - * \return a result code. - * ZOK - success - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZCONNECTIONLOSS - a network error occured while attempting to send request to server - * ZSESSIONEXPIRED - connection attempt failed -- the session's expired - * ZAUTHFAILED - authentication request failed, e.i. invalid credentials - * ZRUNTIMEINCONSISTENCY - a server response came out of order - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details - * ZNOTHING -- not an error; simply indicates that there no more data from the server - * to be processed (when called with ZOOKEEPER_READ flag). - */ -ZOOAPI int zookeeper_process(zhandle_t *zh, int events); -#endif - -/** - * \brief signature of a completion function for a call that returns void. - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void (*void_completion_t)(int rc, const void *data); - -/** - * \brief signature of a completion function that returns a Stat structure. - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param stat a pointer to the stat information for the node involved in - * this function. If a non zero error code is returned, the content of - * stat is undefined. The programmer is NOT responsible for freeing stat. - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void (*stat_completion_t)(int rc, const struct Stat *stat, - const void *data); - -/** - * \brief signature of a completion function that returns data. - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param value the value of the information returned by the asynchronous call. - * If a non zero error code is returned, the content of value is undefined. - * The programmer is NOT responsible for freeing value. - * \param value_len the number of bytes in value. - * \param stat a pointer to the stat information for the node involved in - * this function. If a non zero error code is returned, the content of - * stat is undefined. The programmer is NOT responsible for freeing stat. - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void (*data_completion_t)(int rc, const char *value, int value_len, - const struct Stat *stat, const void *data); - -/** - * \brief signature of a completion function that returns a list of strings. - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param strings a pointer to the structure containng the list of strings of the - * names of the children of a node. If a non zero error code is returned, - * the content of strings is undefined. The programmer is NOT responsible - * for freeing strings. - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void (*strings_completion_t)(int rc, - const struct String_vector *strings, const void *data); - -/** - * \brief signature of a completion function that returns a list of strings and stat. - * . - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param strings a pointer to the structure containng the list of strings of the - * names of the children of a node. If a non zero error code is returned, - * the content of strings is undefined. The programmer is NOT responsible - * for freeing strings. - * \param stat a pointer to the stat information for the node involved in - * this function. If a non zero error code is returned, the content of - * stat is undefined. The programmer is NOT responsible for freeing stat. - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void (*strings_stat_completion_t)(int rc, - const struct String_vector *strings, const struct Stat *stat, - const void *data); - -/** - * \brief signature of a completion function that returns a list of strings. - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param value the value of the string returned. - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void - (*string_completion_t)(int rc, const char *value, const void *data); - -/** - * \brief signature of a completion function that returns an ACL. - * - * This method will be invoked at the end of a asynchronous call and also as - * a result of connection loss or timeout. - * \param rc the error code of the call. Connection loss/timeout triggers - * the completion with one of the following error codes: - * ZCONNECTIONLOSS -- lost connection to the server - * ZOPERATIONTIMEOUT -- connection timed out - * Data related events trigger the completion with error codes listed the - * Exceptions section of the documentation of the function that initiated the - * call. (Zero indicates call was successful.) - * \param acl a pointer to the structure containng the ACL of a node. If a non - * zero error code is returned, the content of strings is undefined. The - * programmer is NOT responsible for freeing acl. - * \param stat a pointer to the stat information for the node involved in - * this function. If a non zero error code is returned, the content of - * stat is undefined. The programmer is NOT responsible for freeing stat. - * \param data the pointer that was passed by the caller when the function - * that this completion corresponds to was invoked. The programmer - * is responsible for any memory freeing associated with the data - * pointer. - */ -typedef void (*acl_completion_t)(int rc, struct ACL_vector *acl, - struct Stat *stat, const void *data); - -/** - * \brief get the state of the zookeeper connection. - * - * The return value will be one of the \ref State Consts. - */ -ZOOAPI int zoo_state(zhandle_t *zh); - -/** - * \brief create a node. - * - * This method will create a node in ZooKeeper. A node can only be created if - * it does not already exists. The Create Flags affect the creation of nodes. - * If ZOO_EPHEMERAL flag is set, the node will automatically get removed if the - * client session goes away. If the ZOO_SEQUENCE flag is set, a unique - * monotonically increasing sequence number is appended to the path name. The - * sequence number is always fixed length of 10 digits, 0 padded. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path The name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param value The data to be stored in the node. - * \param valuelen The number of bytes in data. - * \param acl The initial ACL of the node. The ACL must not be null or empty. - * \param flags this parameter can be set to 0 for normal create or an OR - * of the Create Flags - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the parent node does not exist. - * ZNODEEXISTS the node already exists - * ZNOAUTH the client does not have permission. - * ZNOCHILDRENFOREPHEMERALS cannot create children of ephemeral nodes. - * \param data The data that will be passed to the completion routine when the - * function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_acreate(zhandle_t *zh, const char *path, const char *value, - int valuelen, const struct ACL_vector *acl, int flags, - string_completion_t completion, const void *data); - -/** - * \brief delete a node in zookeeper. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param version the expected version of the node. The function will fail if the - * actual version of the node does not match the expected version. - * If -1 is used the version check will not take place. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADVERSION expected version does not match actual version. - * ZNOTEMPTY children are present; node cannot be deleted. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_adelete(zhandle_t *zh, const char *path, int version, - void_completion_t completion, const void *data); - -/** - * \brief checks the existence of a node in zookeeper. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify the - * client if the node changes. The watch will be set even if the node does not - * exist. This allows clients to watch for nodes to appear. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when the - * function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aexists(zhandle_t *zh, const char *path, int watch, - stat_completion_t completion, const void *data); - -/** - * \brief checks the existence of a node in zookeeper. - * - * This function is similar to \ref zoo_axists except it allows one specify - * a watcher object - a function pointer and associated context. The function - * will be called once the watch has fired. The associated context data will be - * passed to the function as the watcher context parameter. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null a watch will set on the specified znode on the server. - * The watch will be set even if the node does not exist. This allows clients - * to watch for nodes to appear. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when the - * function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_awexists(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - stat_completion_t completion, const void *data); - -/** - * \brief gets the data associated with a node. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify - * the client if the node changes. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aget(zhandle_t *zh, const char *path, int watch, - data_completion_t completion, const void *data); - -/** - * \brief gets the data associated with a node. - * - * This function is similar to \ref zoo_aget except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null, a watch will be set at the server to notify - * the client if the node changes. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_awget(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - data_completion_t completion, const void *data); - -/** - * \brief sets the data associated with a node. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param buffer the buffer holding data to be written to the node. - * \param buflen the number of bytes from buffer to write. - * \param version the expected version of the node. The function will fail if - * the actual version of the node does not match the expected version. If -1 is - * used the version check will not take place. * completion: If null, - * the function will execute synchronously. Otherwise, the function will return - * immediately and invoke the completion routine when the request completes. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADVERSION expected version does not match actual version. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aset(zhandle_t *zh, const char *path, const char *buffer, int buflen, - int version, stat_completion_t completion, const void *data); - -/** - * \brief lists the children of a node. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify - * the client if the node changes. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aget_children(zhandle_t *zh, const char *path, int watch, - strings_completion_t completion, const void *data); - -/** - * \brief lists the children of a node. - * - * This function is similar to \ref zoo_aget_children except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null, a watch will be set at the server to notify - * the client if the node changes. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_awget_children(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - strings_completion_t completion, const void *data); - -/** - * \brief lists the children of a node, and get the parent stat. - * - * This function is new in version 3.3.0 - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify - * the client if the node changes. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aget_children2(zhandle_t *zh, const char *path, int watch, - strings_stat_completion_t completion, const void *data); - -/** - * \brief lists the children of a node, and get the parent stat. - * - * This function is similar to \ref zoo_aget_children2 except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * This function is new in version 3.3.0 - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null, a watch will be set at the server to notify - * the client if the node changes. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_awget_children2(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - strings_stat_completion_t completion, const void *data); - -/** - * \brief Flush leader channel. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ - -ZOOAPI int zoo_async(zhandle_t *zh, const char *path, - string_completion_t completion, const void *data); - - -/** - * \brief gets the acl associated with a node. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aget_acl(zhandle_t *zh, const char *path, acl_completion_t completion, - const void *data); - -/** - * \brief sets the acl associated with a node. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param buffer the buffer holding the acls to be written to the node. - * \param buflen the number of bytes from buffer to write. - * \param completion the routine to invoke when the request completes. The completion - * will be triggered with one of the following codes passed in as the rc argument: - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZINVALIDACL invalid ACL specified - * ZBADVERSION expected version does not match actual version. - * \param data the data that will be passed to the completion routine when - * the function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_aset_acl(zhandle_t *zh, const char *path, int version, - struct ACL_vector *acl, void_completion_t, const void *data); - -/** - * \brief return an error string. - * - * \param return code - * \return string corresponding to the return code - */ -ZOOAPI const char* zerror(int c); - -/** - * \brief specify application credentials. - * - * The application calls this function to specify its credentials for purposes - * of authentication. The server will use the security provider specified by - * the scheme parameter to authenticate the client connection. If the - * authentication request has failed: - * - the server connection is dropped - * - the watcher is called with the ZOO_AUTH_FAILED_STATE value as the state - * parameter. - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param scheme the id of authentication scheme. Natively supported: - * "digest" password-based authentication - * \param cert application credentials. The actual value depends on the scheme. - * \param certLen the length of the data parameter - * \param completion the routine to invoke when the request completes. One of - * the following result codes may be passed into the completion callback: - * ZOK operation completed successfully - * ZAUTHFAILED authentication failed - * \param data the data that will be passed to the completion routine when the - * function completes. - * \return ZOK on success or one of the following errcodes on failure: - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZSYSTEMERROR - a system error occured - */ -ZOOAPI int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert, - int certLen, void_completion_t completion, const void *data); - -/** - * \brief checks if the current zookeeper connection state can't be recovered. - * - * The application must close the zhandle and try to reconnect. - * - * \param zh the zookeeper handle (see \ref zookeeper_init) - * \return ZINVALIDSTATE if connection is unrecoverable - */ -ZOOAPI int is_unrecoverable(zhandle_t *zh); - -/** - * \brief sets the debugging level for the library - */ -ZOOAPI void zoo_set_debug_level(ZooLogLevel logLevel); - -/** - * \brief sets the stream to be used by the library for logging - * - * The zookeeper library uses stderr as its default log stream. Application - * must make sure the stream is writable. Passing in NULL resets the stream - * to its default value (stderr). - */ -ZOOAPI void zoo_set_log_stream(FILE* logStream); - -/** - * \brief enable/disable quorum endpoint order randomization - * - * Note: typically this method should NOT be used outside of testing. - * - * If passed a non-zero value, will make the client connect to quorum peers - * in the order as specified in the zookeeper_init() call. - * A zero value causes zookeeper_init() to permute the peer endpoints - * which is good for more even client connection distribution among the - * quorum peers. - */ -ZOOAPI void zoo_deterministic_conn_order(int yesOrNo); - -/** - * \brief create a node synchronously. - * - * This method will create a node in ZooKeeper. A node can only be created if - * it does not already exists. The Create Flags affect the creation of nodes. - * If ZOO_EPHEMERAL flag is set, the node will automatically get removed if the - * client session goes away. If the ZOO_SEQUENCE flag is set, a unique - * monotonically increasing sequence number is appended to the path name. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path The name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param value The data to be stored in the node. - * \param valuelen The number of bytes in data. To set the data to be NULL use - * value as NULL and valuelen as -1. - * \param acl The initial ACL of the node. The ACL must not be null or empty. - * \param flags this parameter can be set to 0 for normal create or an OR - * of the Create Flags - * \param path_buffer Buffer which will be filled with the path of the - * new node (this might be different than the supplied path - * because of the ZOO_SEQUENCE flag). The path string will always be - * null-terminated. - * \param path_buffer_len Size of path buffer; if the path of the new - * node (including space for the null terminator) exceeds the buffer size, - * the path string will be truncated to fit. The actual path of the - * new node in the server will not be affected by the truncation. - * The path string will always be null-terminated. - * \return one of the following codes are returned: - * ZOK operation completed successfully - * ZNONODE the parent node does not exist. - * ZNODEEXISTS the node already exists - * ZNOAUTH the client does not have permission. - * ZNOCHILDRENFOREPHEMERALS cannot create children of ephemeral nodes. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_create(zhandle_t *zh, const char *path, const char *value, - int valuelen, const struct ACL_vector *acl, int flags, - char *path_buffer, int path_buffer_len); - -/** - * \brief delete a node in zookeeper synchronously. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param version the expected version of the node. The function will fail if the - * actual version of the node does not match the expected version. - * If -1 is used the version check will not take place. - * \return one of the following values is returned. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADVERSION expected version does not match actual version. - * ZNOTEMPTY children are present; node cannot be deleted. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_delete(zhandle_t *zh, const char *path, int version); - - -/** - * \brief checks the existence of a node in zookeeper synchronously. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify the - * client if the node changes. The watch will be set even if the node does not - * exist. This allows clients to watch for nodes to appear. - * \param the return stat value of the node. - * \return return code of the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_exists(zhandle_t *zh, const char *path, int watch, struct Stat *stat); - -/** - * \brief checks the existence of a node in zookeeper synchronously. - * - * This function is similar to \ref zoo_exists except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null a watch will set on the specified znode on the server. - * The watch will be set even if the node does not exist. This allows clients - * to watch for nodes to appear. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param the return stat value of the node. - * \return return code of the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_wexists(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, struct Stat *stat); - -/** - * \brief gets the data associated with a node synchronously. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify - * the client if the node changes. - * \param buffer the buffer holding the node data returned by the server - * \param buffer_len is the size of the buffer pointed to by the buffer parameter. - * It'll be set to the actual data length upon return. If the data is NULL, length is -1. - * \param stat if not NULL, will hold the value of stat for the path on return. - * \return return value of the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_get(zhandle_t *zh, const char *path, int watch, char *buffer, - int* buffer_len, struct Stat *stat); - -/** - * \brief gets the data associated with a node synchronously. - * - * This function is similar to \ref zoo_get except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null, a watch will be set at the server to notify - * the client if the node changes. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param buffer the buffer holding the node data returned by the server - * \param buffer_len is the size of the buffer pointed to by the buffer parameter. - * It'll be set to the actual data length upon return. If the data is NULL, length is -1. - * \param stat if not NULL, will hold the value of stat for the path on return. - * \return return value of the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either in ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_wget(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - char *buffer, int* buffer_len, struct Stat *stat); - -/** - * \brief sets the data associated with a node. See zoo_set2 function if - * you require access to the stat information associated with the znode. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param buffer the buffer holding data to be written to the node. - * \param buflen the number of bytes from buffer to write. To set NULL as data - * use buffer as NULL and buflen as -1. - * \param version the expected version of the node. The function will fail if - * the actual version of the node does not match the expected version. If -1 is - * used the version check will not take place. - * \return the return code for the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADVERSION expected version does not match actual version. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_set(zhandle_t *zh, const char *path, const char *buffer, - int buflen, int version); - -/** - * \brief sets the data associated with a node. This function is the same - * as zoo_set except that it also provides access to stat information - * associated with the znode. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param buffer the buffer holding data to be written to the node. - * \param buflen the number of bytes from buffer to write. To set NULL as data - * use buffer as NULL and buflen as -1. - * \param version the expected version of the node. The function will fail if - * the actual version of the node does not match the expected version. If -1 is - * used the version check will not take place. - * \param stat if not NULL, will hold the value of stat for the path on return. - * \return the return code for the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADVERSION expected version does not match actual version. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_set2(zhandle_t *zh, const char *path, const char *buffer, - int buflen, int version, struct Stat *stat); - -/** - * \brief lists the children of a node synchronously. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify - * the client if the node changes. - * \param strings return value of children paths. - * \return the return code of the function. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_get_children(zhandle_t *zh, const char *path, int watch, - struct String_vector *strings); - -/** - * \brief lists the children of a node synchronously. - * - * This function is similar to \ref zoo_get_children except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null, a watch will be set at the server to notify - * the client if the node changes. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param strings return value of children paths. - * \return the return code of the function. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_wget_children(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - struct String_vector *strings); - -/** - * \brief lists the children of a node and get its stat synchronously. - * - * This function is new in version 3.3.0 - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watch if nonzero, a watch will be set at the server to notify - * the client if the node changes. - * \param strings return value of children paths. - * \param stat return value of node stat. - * \return the return code of the function. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_get_children2(zhandle_t *zh, const char *path, int watch, - struct String_vector *strings, struct Stat *stat); - -/** - * \brief lists the children of a node and get its stat synchronously. - * - * This function is similar to \ref zoo_get_children except it allows one specify - * a watcher object rather than a boolean watch flag. - * - * This function is new in version 3.3.0 - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param watcher if non-null, a watch will be set at the server to notify - * the client if the node changes. - * \param watcherCtx user specific data, will be passed to the watcher callback. - * Unlike the global context set by \ref zookeeper_init, this watcher context - * is associated with the given instance of the watcher only. - * \param strings return value of children paths. - * \param stat return value of node stat. - * \return the return code of the function. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_wget_children2(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - struct String_vector *strings, struct Stat *stat); - -/** - * \brief gets the acl associated with a node synchronously. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param acl the return value of acls on the path. - * \param stat returns the stat of the path specified. - * \return the return code for the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_get_acl(zhandle_t *zh, const char *path, struct ACL_vector *acl, - struct Stat *stat); - -/** - * \brief sets the acl associated with a node synchronously. - * - * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init - * \param path the name of the node. Expressed as a file name with slashes - * separating ancestors of the node. - * \param version the expected version of the path. - * \param acl the acl to be set on the path. - * \return the return code for the function call. - * ZOK operation completed successfully - * ZNONODE the node does not exist. - * ZNOAUTH the client does not have permission. - * ZINVALIDACL invalid ACL specified - * ZBADVERSION expected version does not match actual version. - * ZBADARGUMENTS - invalid input parameters - * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - */ -ZOOAPI int zoo_set_acl(zhandle_t *zh, const char *path, int version, - const struct ACL_vector *acl); - -#ifdef __cplusplus -} -#endif - -#endif /*ZOOKEEPER_H_*/ diff --git a/src/c/include/zookeeper_log.h b/src/c/include/zookeeper_log.h deleted file mode 100644 index e5917cbc64e..00000000000 --- a/src/c/include/zookeeper_log.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ZK_LOG_H_ -#define ZK_LOG_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern ZOOAPI ZooLogLevel logLevel; -#define LOGSTREAM getLogStream() - -#define LOG_ERROR(x) if(logLevel>=ZOO_LOG_LEVEL_ERROR) \ - log_message(ZOO_LOG_LEVEL_ERROR,__LINE__,__func__,format_log_message x) -#define LOG_WARN(x) if(logLevel>=ZOO_LOG_LEVEL_WARN) \ - log_message(ZOO_LOG_LEVEL_WARN,__LINE__,__func__,format_log_message x) -#define LOG_INFO(x) if(logLevel>=ZOO_LOG_LEVEL_INFO) \ - log_message(ZOO_LOG_LEVEL_INFO,__LINE__,__func__,format_log_message x) -#define LOG_DEBUG(x) if(logLevel==ZOO_LOG_LEVEL_DEBUG) \ - log_message(ZOO_LOG_LEVEL_DEBUG,__LINE__,__func__,format_log_message x) - -ZOOAPI void log_message(ZooLogLevel curLevel, int line,const char* funcName, - const char* message); - -ZOOAPI const char* format_log_message(const char* format,...); - -FILE* getLogStream(); - -#ifdef __cplusplus -} -#endif - -#endif /*ZK_LOG_H_*/ diff --git a/src/c/include/zookeeper_version.h b/src/c/include/zookeeper_version.h deleted file mode 100644 index 789653f0c16..00000000000 --- a/src/c/include/zookeeper_version.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef ZOOKEEPER_VERSION_H_ -#define ZOOKEEPER_VERSION_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#define ZOO_MAJOR_VERSION 3 -#define ZOO_MINOR_VERSION 4 -#define ZOO_PATCH_VERSION 0 - -#ifdef __cplusplus -} -#endif - -#endif /* ZOOKEEPER_VERSION_H_ */ diff --git a/src/c/src/cli.c b/src/c/src/cli.c deleted file mode 100644 index 6ee0298ec80..00000000000 --- a/src/c/src/cli.c +++ /dev/null @@ -1,625 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef YCA -#include -#endif - -#define _LL_CAST_ (long long) - -static zhandle_t *zh; -static clientid_t myid; -static const char *clientIdFile = 0; -struct timeval startTime; -static char cmd[1024]; -static int batchMode=0; - -static int to_send=0; -static int sent=0; -static int recvd=0; - -static int shutdownThisThing=0; - -static __attribute__ ((unused)) void -printProfileInfo(struct timeval start, struct timeval end, int thres, - const char* msg) -{ - int delay=(end.tv_sec*1000+end.tv_usec/1000)- - (start.tv_sec*1000+start.tv_usec/1000); - if(delay>thres) - fprintf(stderr,"%s: execution time=%dms\n",msg,delay); -} - -static const char* state2String(int state){ - if (state == 0) - return "CLOSED_STATE"; - if (state == ZOO_CONNECTING_STATE) - return "CONNECTING_STATE"; - if (state == ZOO_ASSOCIATING_STATE) - return "ASSOCIATING_STATE"; - if (state == ZOO_CONNECTED_STATE) - return "CONNECTED_STATE"; - if (state == ZOO_EXPIRED_SESSION_STATE) - return "EXPIRED_SESSION_STATE"; - if (state == ZOO_AUTH_FAILED_STATE) - return "AUTH_FAILED_STATE"; - - return "INVALID_STATE"; -} - -void watcher(zhandle_t *zzh, int type, int state, const char *path, - void* context) -{ - /* Be careful using zh here rather than zzh - as this may be mt code - * the client lib may call the watcher before zookeeper_init returns */ - - fprintf(stderr, "Watcher %d state = %s", type, state2String(state)); - if (path && strlen(path) > 0) { - fprintf(stderr, " for path %s", path); - } - fprintf(stderr, "\n"); - - if (type == ZOO_SESSION_EVENT) { - if (state == ZOO_CONNECTED_STATE) { - const clientid_t *id = zoo_client_id(zzh); - if (myid.client_id == 0 || myid.client_id != id->client_id) { - myid = *id; - fprintf(stderr, "Got a new session id: 0x%llx\n", - _LL_CAST_ myid.client_id); - if (clientIdFile) { - FILE *fh = fopen(clientIdFile, "w"); - if (!fh) { - perror(clientIdFile); - } else { - int rc = fwrite(&myid, sizeof(myid), 1, fh); - if (rc != sizeof(myid)) { - perror("writing client id"); - } - fclose(fh); - } - } - } - } else if (state == ZOO_AUTH_FAILED_STATE) { - fprintf(stderr, "Authentication failure. Shutting down...\n"); - zookeeper_close(zzh); - shutdownThisThing=1; - zh=0; - } else if (state == ZOO_EXPIRED_SESSION_STATE) { - fprintf(stderr, "Session expired. Shutting down...\n"); - zookeeper_close(zzh); - shutdownThisThing=1; - zh=0; - } - } -} - -void dumpStat(const struct Stat *stat) { - char tctimes[40]; - char tmtimes[40]; - time_t tctime; - time_t tmtime; - - if (!stat) { - fprintf(stderr,"null\n"); - return; - } - tctime = stat->ctime/1000; - tmtime = stat->mtime/1000; - fprintf(stderr, "\tctime = %s\tczxid=%llx\n" - "\tmtime=%s\tmzxid=%llx\n" - "\tversion=%x\taversion=%x\n" - "\tephemeralOwner = %llx\n", - ctime_r(&tctime, tctimes), _LL_CAST_ stat->czxid, ctime_r(&tmtime, tmtimes), - _LL_CAST_ stat->mzxid, - (unsigned int)stat->version, (unsigned int)stat->aversion, - _LL_CAST_ stat->ephemeralOwner); -} - -void my_string_completion(int rc, const char *name, const void *data) { - fprintf(stderr, "[%s]: rc = %d\n", (char*)(data==0?"null":data), rc); - if (!rc) { - fprintf(stderr, "\tname = %s\n", name); - } - if(batchMode) - shutdownThisThing=1; -} - -void my_data_completion(int rc, const char *value, int value_len, - const struct Stat *stat, const void *data) { - struct timeval tv; - int sec; - int usec; - gettimeofday(&tv, 0); - sec = tv.tv_sec - startTime.tv_sec; - usec = tv.tv_usec - startTime.tv_usec; - fprintf(stderr, "time = %d msec\n", sec*1000 + usec/1000); - fprintf(stderr, "%s: rc = %d\n", (char*)data, rc); - if (value) { - fprintf(stderr, " value_len = %d\n", value_len); - assert(write(2, value, value_len) == value_len); - } - fprintf(stderr, "\nStat:\n"); - dumpStat(stat); - free((void*)data); - if(batchMode) - shutdownThisThing=1; -} - -void my_silent_data_completion(int rc, const char *value, int value_len, - const struct Stat *stat, const void *data) { - recvd++; - fprintf(stderr, "Data completion %s rc = %d\n",(char*)data,rc); - free((void*)data); - if (recvd==to_send) { - fprintf(stderr,"Recvd %d responses for %d requests sent\n",recvd,to_send); - if(batchMode) - shutdownThisThing=1; - } -} - -void my_strings_completion(int rc, const struct String_vector *strings, - const void *data) { - struct timeval tv; - int sec; - int usec; - int i; - - gettimeofday(&tv, 0); - sec = tv.tv_sec - startTime.tv_sec; - usec = tv.tv_usec - startTime.tv_usec; - fprintf(stderr, "time = %d msec\n", sec*1000 + usec/1000); - fprintf(stderr, "%s: rc = %d\n", (char*)data, rc); - if (strings) - for (i=0; i < strings->count; i++) { - fprintf(stderr, "\t%s\n", strings->data[i]); - } - free((void*)data); - gettimeofday(&tv, 0); - sec = tv.tv_sec - startTime.tv_sec; - usec = tv.tv_usec - startTime.tv_usec; - fprintf(stderr, "time = %d msec\n", sec*1000 + usec/1000); - if(batchMode) - shutdownThisThing=1; -} - -void my_strings_stat_completion(int rc, const struct String_vector *strings, - const struct Stat *stat, const void *data) { - my_strings_completion(rc, strings, data); - dumpStat(stat); - if(batchMode) - shutdownThisThing=1; -} - -void my_void_completion(int rc, const void *data) { - fprintf(stderr, "%s: rc = %d\n", (char*)data, rc); - free((void*)data); - if(batchMode) - shutdownThisThing=1; -} - -void my_stat_completion(int rc, const struct Stat *stat, const void *data) { - fprintf(stderr, "%s: rc = %d Stat:\n", (char*)data, rc); - dumpStat(stat); - free((void*)data); - if(batchMode) - shutdownThisThing=1; -} - -void my_silent_stat_completion(int rc, const struct Stat *stat, - const void *data) { - // fprintf(stderr, "State completion: [%s] rc = %d\n", (char*)data, rc); - sent++; - free((void*)data); -} - -static void sendRequest(const char* data) { - zoo_aset(zh, "/od", data, strlen(data), -1, my_silent_stat_completion, - strdup("/od")); - zoo_aget(zh, "/od", 1, my_silent_data_completion, strdup("/od")); -} - -void od_completion(int rc, const struct Stat *stat, const void *data) { - int i; - fprintf(stderr, "od command response: rc = %d Stat:\n", rc); - dumpStat(stat); - // send a whole bunch of requests - recvd=0; - sent=0; - to_send=200; - for (i=0; i\n"); - fprintf(stderr, " delete \n"); - fprintf(stderr, " set \n"); - fprintf(stderr, " get \n"); - fprintf(stderr, " ls \n"); - fprintf(stderr, " ls2 \n"); - fprintf(stderr, " sync \n"); - fprintf(stderr, " exists \n"); - fprintf(stderr, " myid\n"); - fprintf(stderr, " verbose\n"); - fprintf(stderr, " addauth \n"); - fprintf(stderr, " quit\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " prefix the command with the character 'a' to run the command asynchronously.\n"); - fprintf(stderr, " run the 'verbose' command to toggle verbose logging.\n"); - fprintf(stderr, " i.e. 'aget /foo' to get /foo asynchronously\n"); - } else if (startsWith(line, "verbose")) { - if (verbose) { - verbose = 0; - zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); - fprintf(stderr, "logging level set to WARN\n"); - } else { - verbose = 1; - zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); - fprintf(stderr, "logging level set to DEBUG\n"); - } - } else if (startsWith(line, "get ")) { - line += 4; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - gettimeofday(&startTime, 0); - rc = zoo_aget(zh, line, 1, my_data_completion, strdup(line)); - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "set ")) { - char *ptr; - line += 4; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - ptr = strchr(line, ' '); - if (!ptr) { - fprintf(stderr, "No data found after path\n"); - return; - } - *ptr = '\0'; - ptr++; - if (async) { - rc = zoo_aset(zh, line, ptr, strlen(ptr), -1, my_stat_completion, - strdup(line)); - } else { - struct Stat stat; - rc = zoo_set2(zh, line, ptr, strlen(ptr), -1, &stat); - } - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "ls ")) { - line += 3; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - gettimeofday(&startTime, 0); - rc= zoo_aget_children(zh, line, 1, my_strings_completion, strdup(line)); - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "ls2 ")) { - line += 4; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - gettimeofday(&startTime, 0); - rc= zoo_aget_children2(zh, line, 1, my_strings_stat_completion, strdup(line)); - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "create ")) { - int flags = 0; - line += 7; - if (line[0] == '+') { - line++; - if (line[0] == 'e') { - flags |= ZOO_EPHEMERAL; - line++; - } - if (line[0] == 's') { - flags |= ZOO_SEQUENCE; - line++; - } - line++; - } - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - fprintf(stderr, "Creating [%s] node\n", line); -// { -// struct ACL _CREATE_ONLY_ACL_ACL[] = {{ZOO_PERM_CREATE, ZOO_ANYONE_ID_UNSAFE}}; -// struct ACL_vector CREATE_ONLY_ACL = {1,_CREATE_ONLY_ACL_ACL}; -// rc = zoo_acreate(zh, line, "new", 3, &CREATE_ONLY_ACL, flags, -// my_string_completion, strdup(line)); -// } - rc = zoo_acreate(zh, line, "new", 3, &ZOO_OPEN_ACL_UNSAFE, flags, - my_string_completion, strdup(line)); - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "delete ")) { - line += 7; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - if (async) { - rc = zoo_adelete(zh, line, -1, my_void_completion, strdup(line)); - } else { - rc = zoo_delete(zh, line, -1); - } - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "sync ")) { - line += 5; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } - rc = zoo_async(zh, line, my_string_completion, strdup(line)); - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (startsWith(line, "exists ")) { -#ifdef THREADED - struct Stat stat; -#endif - line += 7; - if (line[0] != '/') { - fprintf(stderr, "Path must start with /, found: %s\n", line); - return; - } -#ifndef THREADED - rc = zoo_aexists(zh, line, 1, my_stat_completion, strdup(line)); -#else - rc = zoo_exists(zh, line, 1, &stat); -#endif - if (rc) { - fprintf(stderr, "Error %d for %s\n", rc, line); - } - } else if (strcmp(line, "myid") == 0) { - printf("session Id = %llx\n", _LL_CAST_ zoo_client_id(zh)->client_id); - } else if (strcmp(line, "reinit") == 0) { - zookeeper_close(zh); - // we can't send myid to the server here -- zookeeper_close() removes - // the session on the server. We must start anew. - zh = zookeeper_init(hostPort, watcher, 30000, 0, 0, 0); - } else if (startsWith(line, "quit")) { - fprintf(stderr, "Quitting...\n"); - shutdownThisThing=1; - } else if (startsWith(line, "od")) { - const char val[]="fire off"; - fprintf(stderr, "Overdosing...\n"); - rc = zoo_aset(zh, "/od", val, sizeof(val)-1, -1, od_completion, 0); - if (rc) - fprintf(stderr, "od command failed: %d\n", rc); - } else if (startsWith(line, "addauth ")) { - char *ptr; - line += 8; - ptr = strchr(line, ' '); - if (ptr) { - *ptr = '\0'; - ptr++; - } - zoo_add_auth(zh, line, ptr, ptr ? strlen(ptr)-1 : 0, NULL, NULL); - } -} - -int main(int argc, char **argv) { -#ifndef THREADED - fd_set rfds, wfds, efds; - int processed=0; -#endif - char buffer[4096]; - char p[2048]; -#ifdef YCA - char *cert=0; - char appId[64]; -#endif - int bufoff = 0; - FILE *fh; - - if (argc < 2) { - fprintf(stderr, - "USAGE %s zookeeper_host_list [clientid_file|cmd:(ls|ls2|create|od|...)]\n", - argv[0]); - fprintf(stderr, - "Version: ZooKeeper cli (c client) version %d.%d.%d\n", - ZOO_MAJOR_VERSION, - ZOO_MINOR_VERSION, - ZOO_PATCH_VERSION); - return 2; - } - if (argc > 2) { - if(strncmp("cmd:",argv[2],4)==0){ - strcpy(cmd,argv[2]+4); - batchMode=1; - fprintf(stderr,"Batch mode: %s\n",cmd); - }else{ - clientIdFile = argv[2]; - fh = fopen(clientIdFile, "r"); - if (fh) { - if (fread(&myid, sizeof(myid), 1, fh) != sizeof(myid)) { - memset(&myid, 0, sizeof(myid)); - } - fclose(fh); - } - } - } -#ifdef YCA - strcpy(appId,"yahoo.example.yca_test"); - cert = yca_get_cert_once(appId); - if(cert!=0) { - fprintf(stderr,"Certificate for appid [%s] is [%s]\n",appId,cert); - strncpy(p,cert,sizeof(p)-1); - free(cert); - } else { - fprintf(stderr,"Certificate for appid [%s] not found\n",appId); - strcpy(p,"dummy"); - } -#else - strcpy(p, "dummy"); -#endif - verbose = 0; - zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); - zoo_deterministic_conn_order(1); // enable deterministic order - hostPort = argv[1]; - zh = zookeeper_init(hostPort, watcher, 30000, &myid, 0, 0); - if (!zh) { - return errno; - } - -#ifdef YCA - if(zoo_add_auth(zh,"yca",p,strlen(p),0,0)!=ZOK) - return 2; -#endif - -#ifdef THREADED - while(!shutdownThisThing) { - int rc; - int len = sizeof(buffer) - bufoff -1; - if (len <= 0) { - fprintf(stderr, "Can't handle lines that long!\n"); - exit(2); - } - rc = read(0, buffer+bufoff, len); - if (rc <= 0) { - fprintf(stderr, "bye\n"); - shutdownThisThing=1; - break; - } - bufoff += rc; - buffer[bufoff] = '\0'; - while (strchr(buffer, '\n')) { - char *ptr = strchr(buffer, '\n'); - *ptr = '\0'; - processline(buffer); - ptr++; - memmove(buffer, ptr, strlen(ptr)+1); - bufoff = 0; - } - } -#else - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - while (!shutdownThisThing) { - int fd; - int interest; - int events; - struct timeval tv; - int rc; - zookeeper_interest(zh, &fd, &interest, &tv); - if (fd != -1) { - if (interest&ZOOKEEPER_READ) { - FD_SET(fd, &rfds); - } else { - FD_CLR(fd, &rfds); - } - if (interest&ZOOKEEPER_WRITE) { - FD_SET(fd, &wfds); - } else { - FD_CLR(fd, &wfds); - } - } else { - fd = 0; - } - FD_SET(0, &rfds); - rc = select(fd+1, &rfds, &wfds, &efds, &tv); - events = 0; - if (FD_ISSET(fd, &rfds)) { - events |= ZOOKEEPER_READ; - } - if (FD_ISSET(fd, &wfds)) { - events |= ZOOKEEPER_WRITE; - } - if(batchMode && processed==0){ - //batch mode - processline(cmd); - processed=1; - } - if (FD_ISSET(0, &rfds)) { - int rc; - int len = sizeof(buffer) - bufoff -1; - if (len <= 0) { - fprintf(stderr, "Can't handle lines that long!\n"); - exit(2); - } - rc = read(0, buffer+bufoff, len); - if (rc <= 0) { - fprintf(stderr, "bye\n"); - break; - } - bufoff += rc; - buffer[bufoff] = '\0'; - while (strchr(buffer, '\n')) { - char *ptr = strchr(buffer, '\n'); - *ptr = '\0'; - processline(buffer); - ptr++; - memmove(buffer, ptr, strlen(ptr)+1); - bufoff = 0; - } - } - zookeeper_process(zh, events); - } -#endif - if (to_send!=0) - fprintf(stderr,"Recvd %d responses for %d requests sent\n",recvd,sent); - zookeeper_close(zh); - return 0; -} diff --git a/src/c/src/hashtable/LICENSE.txt b/src/c/src/hashtable/LICENSE.txt deleted file mode 100644 index 674a6245648..00000000000 --- a/src/c/src/hashtable/LICENSE.txt +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2002, 2004, Christopher Clark -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - - * Neither the name of the original author; nor the names of any contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/c/src/hashtable/hashtable.c b/src/c/src/hashtable/hashtable.c deleted file mode 100644 index 763357edce5..00000000000 --- a/src/c/src/hashtable/hashtable.c +++ /dev/null @@ -1,274 +0,0 @@ -/* Copyright (C) 2004 Christopher Clark */ - -#include "hashtable.h" -#include "hashtable_private.h" -#include -#include -#include -#include - -/* -Credit for primes table: Aaron Krowne - http://br.endernet.org/~akrowne/ - http://planetmath.org/encyclopedia/GoodHashTablePrimes.html -*/ -static const unsigned int primes[] = { -53, 97, 193, 389, -769, 1543, 3079, 6151, -12289, 24593, 49157, 98317, -196613, 393241, 786433, 1572869, -3145739, 6291469, 12582917, 25165843, -50331653, 100663319, 201326611, 402653189, -805306457, 1610612741 -}; -const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); -const float max_load_factor = 0.65; - -/*****************************************************************************/ -struct hashtable * -create_hashtable(unsigned int minsize, - unsigned int (*hashf) (void*), - int (*eqf) (void*,void*)) -{ - struct hashtable *h; - unsigned int pindex, size = primes[0]; - /* Check requested hashtable isn't too large */ - if (minsize > (1u << 30)) return NULL; - /* Enforce size as prime */ - for (pindex=0; pindex < prime_table_length; pindex++) { - if (primes[pindex] > minsize) { size = primes[pindex]; break; } - } - h = (struct hashtable *)malloc(sizeof(struct hashtable)); - if (NULL == h) return NULL; /*oom*/ - h->table = (struct entry **)malloc(sizeof(struct entry*) * size); - if (NULL == h->table) { free(h); return NULL; } /*oom*/ - memset(h->table, 0, size * sizeof(struct entry *)); - h->tablelength = size; - h->primeindex = pindex; - h->entrycount = 0; - h->hashfn = hashf; - h->eqfn = eqf; - h->loadlimit = (unsigned int) ceil(size * max_load_factor); - return h; -} - -/*****************************************************************************/ -unsigned int -hash(struct hashtable *h, void *k) -{ - /* Aim to protect against poor hash functions by adding logic here - * - logic taken from java 1.4 hashtable source */ - unsigned int i = h->hashfn(k); - i += ~(i << 9); - i ^= ((i >> 14) | (i << 18)); /* >>> */ - i += (i << 4); - i ^= ((i >> 10) | (i << 22)); /* >>> */ - return i; -} - -/*****************************************************************************/ -static int -hashtable_expand(struct hashtable *h) -{ - /* Double the size of the table to accomodate more entries */ - struct entry **newtable; - struct entry *e; - struct entry **pE; - unsigned int newsize, i, index; - /* Check we're not hitting max capacity */ - if (h->primeindex == (prime_table_length - 1)) return 0; - newsize = primes[++(h->primeindex)]; - - newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); - if (NULL != newtable) - { - memset(newtable, 0, newsize * sizeof(struct entry *)); - /* This algorithm is not 'stable'. ie. it reverses the list - * when it transfers entries between the tables */ - for (i = 0; i < h->tablelength; i++) { - while (NULL != (e = h->table[i])) { - h->table[i] = e->next; - index = indexFor(newsize,e->h); - e->next = newtable[index]; - newtable[index] = e; - } - } - free(h->table); - h->table = newtable; - } - /* Plan B: realloc instead */ - else - { - newtable = (struct entry **) - realloc(h->table, newsize * sizeof(struct entry *)); - if (NULL == newtable) { (h->primeindex)--; return 0; } - h->table = newtable; - memset(newtable[h->tablelength], 0, newsize - h->tablelength); - for (i = 0; i < h->tablelength; i++) { - for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { - index = indexFor(newsize,e->h); - if (index == i) - { - pE = &(e->next); - } - else - { - *pE = e->next; - e->next = newtable[index]; - newtable[index] = e; - } - } - } - } - h->tablelength = newsize; - h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); - return -1; -} - -/*****************************************************************************/ -unsigned int -hashtable_count(struct hashtable *h) -{ - return h->entrycount; -} - -/*****************************************************************************/ -int -hashtable_insert(struct hashtable *h, void *k, void *v) -{ - /* This method allows duplicate keys - but they shouldn't be used */ - unsigned int index; - struct entry *e; - if (++(h->entrycount) > h->loadlimit) - { - /* Ignore the return value. If expand fails, we should - * still try cramming just this value into the existing table - * -- we may not have memory for a larger table, but one more - * element may be ok. Next time we insert, we'll try expanding again.*/ - hashtable_expand(h); - } - e = (struct entry *)malloc(sizeof(struct entry)); - if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ - e->h = hash(h,k); - index = indexFor(h->tablelength,e->h); - e->k = k; - e->v = v; - e->next = h->table[index]; - h->table[index] = e; - return -1; -} - -/*****************************************************************************/ -void * /* returns value associated with key */ -hashtable_search(struct hashtable *h, void *k) -{ - struct entry *e; - unsigned int hashvalue, index; - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - e = h->table[index]; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; - e = e->next; - } - return NULL; -} - -/*****************************************************************************/ -void * /* returns value associated with key */ -hashtable_remove(struct hashtable *h, void *k) -{ - /* TODO: consider compacting the table when the load factor drops enough, - * or provide a 'compact' method. */ - - struct entry *e; - struct entry **pE; - void *v; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hash(h,k)); - pE = &(h->table[index]); - e = *pE; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - { - *pE = e->next; - h->entrycount--; - v = e->v; - freekey(e->k); - free(e); - return v; - } - pE = &(e->next); - e = e->next; - } - return NULL; -} - -/*****************************************************************************/ -/* destroy */ -void -hashtable_destroy(struct hashtable *h, int free_values) -{ - unsigned int i; - struct entry *e, *f; - struct entry **table = h->table; - if (free_values) - { - for (i = 0; i < h->tablelength; i++) - { - e = table[i]; - while (NULL != e) - { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } - } - } - else - { - for (i = 0; i < h->tablelength; i++) - { - e = table[i]; - while (NULL != e) - { f = e; e = e->next; freekey(f->k); free(f); } - } - } - free(h->table); - free(h); -} - -/* - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/src/c/src/hashtable/hashtable.h b/src/c/src/hashtable/hashtable.h deleted file mode 100644 index 1fe874534a0..00000000000 --- a/src/c/src/hashtable/hashtable.h +++ /dev/null @@ -1,207 +0,0 @@ -/* Copyright (C) 2002 Christopher Clark */ - -#ifndef __HASHTABLE_CWC22_H__ -#define __HASHTABLE_CWC22_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -struct hashtable; - -/* Example of use: - * - * struct hashtable *h; - * struct some_key *k; - * struct some_value *v; - * - * static unsigned int hash_from_key_fn( void *k ); - * static int keys_equal_fn ( void *key1, void *key2 ); - * - * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); - * k = (struct some_key *) malloc(sizeof(struct some_key)); - * v = (struct some_value *) malloc(sizeof(struct some_value)); - * - * (initialise k and v to suitable values) - * - * if (! hashtable_insert(h,k,v) ) - * { exit(-1); } - * - * if (NULL == (found = hashtable_search(h,k) )) - * { printf("not found!"); } - * - * if (NULL == (found = hashtable_remove(h,k) )) - * { printf("Not found\n"); } - * - */ - -/* Macros may be used to define type-safe(r) hashtable access functions, with - * methods specialized to take known key and value types as parameters. - * - * Example: - * - * Insert this at the start of your file: - * - * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); - * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); - * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); - * - * This defines the functions 'insert_some', 'search_some' and 'remove_some'. - * These operate just like hashtable_insert etc., with the same parameters, - * but their function signatures have 'struct some_key *' rather than - * 'void *', and hence can generate compile time errors if your program is - * supplying incorrect data as a key (and similarly for value). - * - * Note that the hash and key equality functions passed to create_hashtable - * still take 'void *' parameters instead of 'some key *'. This shouldn't be - * a difficult issue as they're only defined and passed once, and the other - * functions will ensure that only valid keys are supplied to them. - * - * The cost for this checking is increased code size and runtime overhead - * - if performance is important, it may be worth switching back to the - * unsafe methods once your program has been debugged with the safe methods. - * This just requires switching to some simple alternative defines - eg: - * #define insert_some hashtable_insert - * - */ - -/***************************************************************************** - * create_hashtable - - * @name create_hashtable - * @param minsize minimum initial size of hashtable - * @param hashfunction function for hashing keys - * @param key_eq_fn function for determining key equality - * @return newly created hashtable or NULL on failure - */ - -struct hashtable * -create_hashtable(unsigned int minsize, - unsigned int (*hashfunction) (void*), - int (*key_eq_fn) (void*,void*)); - -/***************************************************************************** - * hashtable_insert - - * @name hashtable_insert - * @param h the hashtable to insert into - * @param k the key - hashtable claims ownership and will free on removal - * @param v the value - does not claim ownership - * @return non-zero for successful insertion - * - * This function will cause the table to expand if the insertion would take - * the ratio of entries to table size over the maximum load factor. - * - * This function does not check for repeated insertions with a duplicate key. - * The value returned when using a duplicate key is undefined -- when - * the hashtable changes size, the order of retrieval of duplicate key - * entries is reversed. - * If in doubt, remove before insert. - */ - -int -hashtable_insert(struct hashtable *h, void *k, void *v); - -#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ -int fnname (struct hashtable *h, keytype *k, valuetype *v) \ -{ \ - return hashtable_insert(h,k,v); \ -} - -/***************************************************************************** - * hashtable_search - - * @name hashtable_search - * @param h the hashtable to search - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ - -void * -hashtable_search(struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ -valuetype * fnname (struct hashtable *h, keytype *k) \ -{ \ - return (valuetype *) (hashtable_search(h,k)); \ -} - -/***************************************************************************** - * hashtable_remove - - * @name hashtable_remove - * @param h the hashtable to remove the item from - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ - -void * /* returns value */ -hashtable_remove(struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ -valuetype * fnname (struct hashtable *h, keytype *k) \ -{ \ - return (valuetype *) (hashtable_remove(h,k)); \ -} - - -/***************************************************************************** - * hashtable_count - - * @name hashtable_count - * @param h the hashtable - * @return the number of items stored in the hashtable - */ -unsigned int -hashtable_count(struct hashtable *h); - - -/***************************************************************************** - * hashtable_destroy - - * @name hashtable_destroy - * @param h the hashtable - * @param free_values whether to call 'free' on the remaining values - */ - -void -hashtable_destroy(struct hashtable *h, int free_values); - -#ifdef __cplusplus -} -#endif - -#endif /* __HASHTABLE_CWC22_H__ */ - -/* - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/src/c/src/hashtable/hashtable_itr.c b/src/c/src/hashtable/hashtable_itr.c deleted file mode 100644 index 5dced841f31..00000000000 --- a/src/c/src/hashtable/hashtable_itr.c +++ /dev/null @@ -1,188 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark */ - -#include "hashtable.h" -#include "hashtable_private.h" -#include "hashtable_itr.h" -#include /* defines NULL */ - -/*****************************************************************************/ -/* hashtable_iterator - iterator constructor */ - -struct hashtable_itr * -hashtable_iterator(struct hashtable *h) -{ - unsigned int i, tablelength; - struct hashtable_itr *itr = (struct hashtable_itr *) - malloc(sizeof(struct hashtable_itr)); - if (NULL == itr) return NULL; - itr->h = h; - itr->e = NULL; - itr->parent = NULL; - tablelength = h->tablelength; - itr->index = tablelength; - if (0 == h->entrycount) return itr; - - for (i = 0; i < tablelength; i++) - { - if (NULL != h->table[i]) - { - itr->e = h->table[i]; - itr->index = i; - break; - } - } - return itr; -} - -/*****************************************************************************/ -/* key - return the key of the (key,value) pair at the current position */ -/* value - return the value of the (key,value) pair at the current position */ - -void * -hashtable_iterator_key(struct hashtable_itr *i) -{ return i->e->k; } - -void * -hashtable_iterator_value(struct hashtable_itr *i) -{ return i->e->v; } - -/*****************************************************************************/ -/* advance - advance the iterator to the next element - * returns zero if advanced to end of table */ - -int -hashtable_iterator_advance(struct hashtable_itr *itr) -{ - unsigned int j,tablelength; - struct entry **table; - struct entry *next; - if (NULL == itr->e) return 0; /* stupidity check */ - - next = itr->e->next; - if (NULL != next) - { - itr->parent = itr->e; - itr->e = next; - return -1; - } - tablelength = itr->h->tablelength; - itr->parent = NULL; - if (tablelength <= (j = ++(itr->index))) - { - itr->e = NULL; - return 0; - } - table = itr->h->table; - while (NULL == (next = table[j])) - { - if (++j >= tablelength) - { - itr->index = tablelength; - itr->e = NULL; - return 0; - } - } - itr->index = j; - itr->e = next; - return -1; -} - -/*****************************************************************************/ -/* remove - remove the entry at the current iterator position - * and advance the iterator, if there is a successive - * element. - * If you want the value, read it before you remove: - * beware memory leaks if you don't. - * Returns zero if end of iteration. */ - -int -hashtable_iterator_remove(struct hashtable_itr *itr) -{ - struct entry *remember_e, *remember_parent; - int ret; - - /* Do the removal */ - if (NULL == (itr->parent)) - { - /* element is head of a chain */ - itr->h->table[itr->index] = itr->e->next; - } else { - /* element is mid-chain */ - itr->parent->next = itr->e->next; - } - /* itr->e is now outside the hashtable */ - remember_e = itr->e; - itr->h->entrycount--; - freekey(remember_e->k); - - /* Advance the iterator, correcting the parent */ - remember_parent = itr->parent; - ret = hashtable_iterator_advance(itr); - if (itr->parent == remember_e) { itr->parent = remember_parent; } - free(remember_e); - return ret; -} - -/*****************************************************************************/ -int /* returns zero if not found */ -hashtable_iterator_search(struct hashtable_itr *itr, - struct hashtable *h, void *k) -{ - struct entry *e, *parent; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - - e = h->table[index]; - parent = NULL; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - { - itr->index = index; - itr->e = e; - itr->parent = parent; - itr->h = h; - return -1; - } - parent = e; - e = e->next; - } - return 0; -} - - -/* - * Copyright (c) 2002, 2004, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/src/c/src/hashtable/hashtable_itr.h b/src/c/src/hashtable/hashtable_itr.h deleted file mode 100644 index 234d3e3b9d0..00000000000 --- a/src/c/src/hashtable/hashtable_itr.h +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark */ - -#ifndef __HASHTABLE_ITR_CWC22__ -#define __HASHTABLE_ITR_CWC22__ -#include "hashtable.h" -#include "hashtable_private.h" /* needed to enable inlining */ - -#ifdef __cplusplus -extern "C" { -#endif - -/*****************************************************************************/ -/* This struct is only concrete here to allow the inlining of two of the - * accessor functions. */ -struct hashtable_itr -{ - struct hashtable *h; - struct entry *e; - struct entry *parent; - unsigned int index; -}; - - -/*****************************************************************************/ -/* hashtable_iterator - */ - -struct hashtable_itr * -hashtable_iterator(struct hashtable *h); - -/*****************************************************************************/ -/* hashtable_iterator_key - * - return the value of the (key,value) pair at the current position */ - -extern inline void * -hashtable_iterator_key(struct hashtable_itr *i) -{ - return i->e->k; -} - -/*****************************************************************************/ -/* value - return the value of the (key,value) pair at the current position */ - -extern inline void * -hashtable_iterator_value(struct hashtable_itr *i) -{ - return i->e->v; -} - -/*****************************************************************************/ -/* advance - advance the iterator to the next element - * returns zero if advanced to end of table */ - -int -hashtable_iterator_advance(struct hashtable_itr *itr); - -/*****************************************************************************/ -/* remove - remove current element and advance the iterator to the next element - * NB: if you need the value to free it, read it before - * removing. ie: beware memory leaks! - * returns zero if advanced to end of table */ - -int -hashtable_iterator_remove(struct hashtable_itr *itr); - -/*****************************************************************************/ -/* search - overwrite the supplied iterator, to point to the entry - * matching the supplied key. - h points to the hashtable to be searched. - * returns zero if not found. */ -int -hashtable_iterator_search(struct hashtable_itr *itr, - struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ -int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ -{ \ - return (hashtable_iterator_search(i,h,k)); \ -} - - -#ifdef __cplusplus -} -#endif - -#endif /* __HASHTABLE_ITR_CWC22__*/ - -/* - * Copyright (c) 2002, 2004, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/src/c/src/hashtable/hashtable_private.h b/src/c/src/hashtable/hashtable_private.h deleted file mode 100644 index 3e95f600577..00000000000 --- a/src/c/src/hashtable/hashtable_private.h +++ /dev/null @@ -1,85 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark */ - -#ifndef __HASHTABLE_PRIVATE_CWC22_H__ -#define __HASHTABLE_PRIVATE_CWC22_H__ - -#include "hashtable.h" - -/*****************************************************************************/ -struct entry -{ - void *k, *v; - unsigned int h; - struct entry *next; -}; - -struct hashtable { - unsigned int tablelength; - struct entry **table; - unsigned int entrycount; - unsigned int loadlimit; - unsigned int primeindex; - unsigned int (*hashfn) (void *k); - int (*eqfn) (void *k1, void *k2); -}; - -/*****************************************************************************/ -unsigned int -hash(struct hashtable *h, void *k); - -/*****************************************************************************/ -/* indexFor */ -static inline unsigned int -indexFor(unsigned int tablelength, unsigned int hashvalue) { - return (hashvalue % tablelength); -}; - -/* Only works if tablelength == 2^N */ -/*static inline unsigned int -indexFor(unsigned int tablelength, unsigned int hashvalue) -{ - return (hashvalue & (tablelength - 1u)); -} -*/ - -/*****************************************************************************/ -#define freekey(X) free(X) -/*define freekey(X) ; */ - - -/*****************************************************************************/ - -#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ - -/* - * Copyright (c) 2002, Christopher Clark - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/src/c/src/load_gen.c b/src/c/src/load_gen.c deleted file mode 100644 index 9c61a47805a..00000000000 --- a/src/c/src/load_gen.c +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "zookeeper_log.h" -#include -#include -#include -#include - -static zhandle_t *zh; - -static int shutdownThisThing=0; - -// ***************************************************************************** -// -static pthread_cond_t cond=PTHREAD_COND_INITIALIZER; -static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER; - -static pthread_cond_t counterCond=PTHREAD_COND_INITIALIZER; -static pthread_mutex_t counterLock=PTHREAD_MUTEX_INITIALIZER; -static int counter; - - - -void ensureConnected(){ - pthread_mutex_lock(&lock); - while (zoo_state(zh)!=ZOO_CONNECTED_STATE) { - pthread_cond_wait(&cond,&lock); - } - pthread_mutex_unlock(&lock); -} - -void incCounter(int delta){ - pthread_mutex_lock(&counterLock); - counter+=delta; - pthread_cond_broadcast(&counterCond); - pthread_mutex_unlock(&counterLock); - -} -void setCounter(int cnt){ - pthread_mutex_lock(&counterLock); - counter=cnt; - pthread_cond_broadcast(&counterCond); - pthread_mutex_unlock(&counterLock); - -} -void waitCounter(){ - pthread_mutex_lock(&counterLock); - while (counter>0) { - pthread_cond_wait(&counterCond,&counterLock); - } - pthread_mutex_unlock(&counterLock); -} - -void listener(zhandle_t *zzh, int type, int state, const char *path,void* ctx) { - if(type == ZOO_SESSION_EVENT){ - if(state == ZOO_CONNECTED_STATE){ - pthread_mutex_lock(&lock); - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&lock); - } - setCounter(0); - } -} - -void create_completion(int rc, const char *name, const void *data) { - incCounter(-1); - if(rc!=ZOK){ - LOG_ERROR(("Failed to create a node rc=%d",rc)); - } -} - -int doCreateNodes(const char* root, int count){ - char nodeName[1024]; - int i; - for(i=0; idata) { - int32_t i; - for(i=0;icount; i++) { - free(v->data[i]); - } - free(v->data); - v->data = 0; - } - return 0; -} - -static int deletedCounter; - -int recursiveDelete(const char* root){ - struct String_vector children; - int i; - int rc=zoo_get_children(zh,root,0,&children); - if(rc!=ZNONODE){ - if(rc!=ZOK){ - LOG_ERROR(("Failed to get children of %s, rc=%d",root,rc)); - return rc; - } - for(i=0;i -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void zoo_lock_auth(zhandle_t *zh) -{ - pthread_mutex_lock(&zh->auth_h.lock); -} -void zoo_unlock_auth(zhandle_t *zh) -{ - pthread_mutex_unlock(&zh->auth_h.lock); -} -void lock_buffer_list(buffer_head_t *l) -{ - pthread_mutex_lock(&l->lock); -} -void unlock_buffer_list(buffer_head_t *l) -{ - pthread_mutex_unlock(&l->lock); -} -void lock_completion_list(completion_head_t *l) -{ - pthread_mutex_lock(&l->lock); -} -void unlock_completion_list(completion_head_t *l) -{ - pthread_cond_broadcast(&l->cond); - pthread_mutex_unlock(&l->lock); -} -struct sync_completion *alloc_sync_completion(void) -{ - struct sync_completion *sc = (struct sync_completion*)calloc(1, sizeof(struct sync_completion)); - if (sc) { - pthread_cond_init(&sc->cond, 0); - pthread_mutex_init(&sc->lock, 0); - } - return sc; -} -int wait_sync_completion(struct sync_completion *sc) -{ - pthread_mutex_lock(&sc->lock); - while (!sc->complete) { - pthread_cond_wait(&sc->cond, &sc->lock); - } - pthread_mutex_unlock(&sc->lock); - return 0; -} - -void free_sync_completion(struct sync_completion *sc) -{ - if (sc) { - pthread_mutex_destroy(&sc->lock); - pthread_cond_destroy(&sc->cond); - free(sc); - } -} - -void notify_sync_completion(struct sync_completion *sc) -{ - pthread_mutex_lock(&sc->lock); - sc->complete = 1; - pthread_cond_broadcast(&sc->cond); - pthread_mutex_unlock(&sc->lock); -} - -int process_async(int outstanding_sync) -{ - return 0; -} - -void *do_io(void *); -void *do_completion(void *); -int wakeup_io_thread(zhandle_t *zh); - -static int set_nonblock(int fd){ - long l = fcntl(fd, F_GETFL); - if(l & O_NONBLOCK) return 0; - return fcntl(fd, F_SETFL, l | O_NONBLOCK); -} - -void wait_for_others(zhandle_t* zh) -{ - struct adaptor_threads* adaptor=zh->adaptor_priv; - pthread_mutex_lock(&adaptor->lock); - while(adaptor->threadsToWait>0) - pthread_cond_wait(&adaptor->cond,&adaptor->lock); - pthread_mutex_unlock(&adaptor->lock); -} - -void notify_thread_ready(zhandle_t* zh) -{ - struct adaptor_threads* adaptor=zh->adaptor_priv; - pthread_mutex_lock(&adaptor->lock); - adaptor->threadsToWait--; - pthread_cond_broadcast(&adaptor->cond); - while(adaptor->threadsToWait>0) - pthread_cond_wait(&adaptor->cond,&adaptor->lock); - pthread_mutex_unlock(&adaptor->lock); -} - - -void start_threads(zhandle_t* zh) -{ - int rc = 0; - struct adaptor_threads* adaptor=zh->adaptor_priv; - pthread_cond_init(&adaptor->cond,0); - pthread_mutex_init(&adaptor->lock,0); - adaptor->threadsToWait=2; // wait for 2 threads before opening the barrier - - // use api_prolog() to make sure zhandle doesn't get destroyed - // while initialization is in progress - api_prolog(zh); - LOG_DEBUG(("starting threads...")); - rc=pthread_create(&adaptor->io, 0, do_io, zh); - assert("pthread_create() failed for the IO thread"&&!rc); - rc=pthread_create(&adaptor->completion, 0, do_completion, zh); - assert("pthread_create() failed for the completion thread"&&!rc); - wait_for_others(zh); - api_epilog(zh, 0); -} - -int adaptor_init(zhandle_t *zh) -{ - pthread_mutexattr_t recursive_mx_attr; - struct adaptor_threads *adaptor_threads = calloc(1, sizeof(*adaptor_threads)); - if (!adaptor_threads) { - LOG_ERROR(("Out of memory")); - return -1; - } - - /* We use a pipe for interrupting select() */ - if(pipe(adaptor_threads->self_pipe)==-1) { - LOG_ERROR(("Can't make a pipe %d",errno)); - free(adaptor_threads); - return -1; - } - set_nonblock(adaptor_threads->self_pipe[1]); - set_nonblock(adaptor_threads->self_pipe[0]); - - pthread_mutex_init(&zh->auth_h.lock,0); - - zh->adaptor_priv = adaptor_threads; - pthread_mutex_init(&zh->to_process.lock,0); - pthread_mutex_init(&adaptor_threads->zh_lock,0); - // to_send must be recursive mutex - pthread_mutexattr_init(&recursive_mx_attr); - pthread_mutexattr_settype(&recursive_mx_attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&zh->to_send.lock,&recursive_mx_attr); - pthread_mutexattr_destroy(&recursive_mx_attr); - - pthread_mutex_init(&zh->sent_requests.lock,0); - pthread_cond_init(&zh->sent_requests.cond,0); - pthread_mutex_init(&zh->completions_to_process.lock,0); - pthread_cond_init(&zh->completions_to_process.cond,0); - start_threads(zh); - return 0; -} - -void adaptor_finish(zhandle_t *zh) -{ - struct adaptor_threads *adaptor_threads; - // make sure zh doesn't get destroyed until after we're done here - api_prolog(zh); - adaptor_threads = zh->adaptor_priv; - if(adaptor_threads==0) { - api_epilog(zh,0); - return; - } - - if(!pthread_equal(adaptor_threads->io,pthread_self())){ - wakeup_io_thread(zh); - pthread_join(adaptor_threads->io, 0); - }else - pthread_detach(adaptor_threads->io); - - if(!pthread_equal(adaptor_threads->completion,pthread_self())){ - pthread_mutex_lock(&zh->completions_to_process.lock); - pthread_cond_broadcast(&zh->completions_to_process.cond); - pthread_mutex_unlock(&zh->completions_to_process.lock); - pthread_join(adaptor_threads->completion, 0); - }else - pthread_detach(adaptor_threads->completion); - - api_epilog(zh,0); -} - -void adaptor_destroy(zhandle_t *zh) -{ - struct adaptor_threads *adaptor = zh->adaptor_priv; - if(adaptor==0) return; - - pthread_cond_destroy(&adaptor->cond); - pthread_mutex_destroy(&adaptor->lock); - pthread_mutex_destroy(&zh->to_process.lock); - pthread_mutex_destroy(&zh->to_send.lock); - pthread_mutex_destroy(&zh->sent_requests.lock); - pthread_cond_destroy(&zh->sent_requests.cond); - pthread_mutex_destroy(&zh->completions_to_process.lock); - pthread_cond_destroy(&zh->completions_to_process.cond); - pthread_mutex_destroy(&adaptor->zh_lock); - - pthread_mutex_destroy(&zh->auth_h.lock); - - close(adaptor->self_pipe[0]); - close(adaptor->self_pipe[1]); - free(adaptor); - zh->adaptor_priv=0; -} - -int wakeup_io_thread(zhandle_t *zh) -{ - struct adaptor_threads *adaptor_threads = zh->adaptor_priv; - char c=0; - return write(adaptor_threads->self_pipe[1],&c,1)==1? ZOK: ZSYSTEMERROR; -} - -int adaptor_send_queue(zhandle_t *zh, int timeout) -{ - if(!zh->close_requested) - return wakeup_io_thread(zh); - // don't rely on the IO thread to send the messages if the app has - // requested to close - return flush_send_queue(zh, timeout); -} - -/* These two are declared here because we will run the event loop - * and not the client */ -int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, - struct timeval *tv); -int zookeeper_process(zhandle_t *zh, int events); - -void *do_io(void *v) -{ - zhandle_t *zh = (zhandle_t*)v; - struct pollfd fds[2]; - struct adaptor_threads *adaptor_threads = zh->adaptor_priv; - - api_prolog(zh); - notify_thread_ready(zh); - LOG_DEBUG(("started IO thread")); - fds[0].fd=adaptor_threads->self_pipe[0]; - fds[0].events=POLLIN; - while(!zh->close_requested) { - struct timeval tv; - int fd; - int interest; - int timeout; - int maxfd=1; - int rc; - - zookeeper_interest(zh, &fd, &interest, &tv); - if (fd != -1) { - fds[1].fd=fd; - fds[1].events=(interest&ZOOKEEPER_READ)?POLLIN:0; - fds[1].events|=(interest&ZOOKEEPER_WRITE)?POLLOUT:0; - maxfd=2; - } - timeout=tv.tv_sec * 1000 + (tv.tv_usec/1000); - - poll(fds,maxfd,timeout); - if (fd != -1) { - interest=(fds[1].revents&POLLIN)?ZOOKEEPER_READ:0; - interest|=((fds[1].revents&POLLOUT)||(fds[1].revents&POLLHUP))?ZOOKEEPER_WRITE:0; - } - if(fds[0].revents&POLLIN){ - // flush the pipe - char b[128]; - while(read(adaptor_threads->self_pipe[0],b,sizeof(b))==sizeof(b)){} - } - // dispatch zookeeper events - rc = zookeeper_process(zh, interest); - // check the current state of the zhandle and terminate - // if it is_unrecoverable() - if(is_unrecoverable(zh)) - break; - } - api_epilog(zh, 0); - LOG_DEBUG(("IO thread terminated")); - return 0; -} - -void *do_completion(void *v) -{ - zhandle_t *zh = v; - api_prolog(zh); - notify_thread_ready(zh); - LOG_DEBUG(("started completion thread")); - while(!zh->close_requested) { - pthread_mutex_lock(&zh->completions_to_process.lock); - while(!zh->completions_to_process.head && !zh->close_requested) { - pthread_cond_wait(&zh->completions_to_process.cond, &zh->completions_to_process.lock); - } - pthread_mutex_unlock(&zh->completions_to_process.lock); - process_completions(zh); - } - api_epilog(zh, 0); - LOG_DEBUG(("completion thread terminated")); - return 0; -} - -int32_t inc_ref_counter(zhandle_t* zh,int i) -{ - int incr=(i<0?-1:(i>0?1:0)); - // fetch_and_add implements atomic post-increment - int v=fetch_and_add(&zh->ref_counter,incr); - // inc_ref_counter wants pre-increment - v+=incr; // simulate pre-increment - return v; -} - -int32_t fetch_and_add(volatile int32_t* operand, int incr) -{ - int32_t result; - asm __volatile__( - "lock xaddl %0,%1\n" - : "=r"(result), "=m"(*(int *)operand) - : "0"(incr) - : "memory"); - return result; -} - -// make sure the static xid is initialized before any threads started -__attribute__((constructor)) int32_t get_xid() -{ - static int32_t xid = -1; - if (xid == -1) { - xid = time(0); - } - return fetch_and_add(&xid,1); -} - -void enter_critical(zhandle_t* zh) -{ - struct adaptor_threads *adaptor = zh->adaptor_priv; - if(adaptor) - pthread_mutex_lock(&adaptor->zh_lock); -} - -void leave_critical(zhandle_t* zh) -{ - struct adaptor_threads *adaptor = zh->adaptor_priv; - if(adaptor) - pthread_mutex_unlock(&adaptor->zh_lock); -} diff --git a/src/c/src/recordio.c b/src/c/src/recordio.c deleted file mode 100644 index 7f190b8122c..00000000000 --- a/src/c/src/recordio.c +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include - -void deallocate_String(char **s) -{ - if (*s) - free(*s); - *s = 0; -} - -void deallocate_Buffer(struct buffer *b) -{ - if (b->buff) - free(b->buff); - b->buff = 0; -} - -struct buff_struct { - int32_t len; - int32_t off; - char *buffer; -}; - -static int resize_buffer(struct buff_struct *s, int newlen) -{ - char *buffer= NULL; - while (s->len < newlen) { - s->len *= 2; - } - buffer = (char*)realloc(s->buffer, s->len); - if (!buffer) { - s->buffer = 0; - return -ENOMEM; - } - s->buffer = buffer; - return 0; -} - -int oa_start_record(struct oarchive *oa, const char *tag) -{ - return 0; -} -int oa_end_record(struct oarchive *oa, const char *tag) -{ - return 0; -} -int oa_serialize_int(struct oarchive *oa, const char *tag, const int32_t *d) -{ - struct buff_struct *priv = oa->priv; - int32_t i = htonl(*d); - if ((priv->len - priv->off) < sizeof(i)) { - int rc = resize_buffer(priv, priv->len + sizeof(i)); - if (rc < 0) return rc; - } - memcpy(priv->buffer+priv->off, &i, sizeof(i)); - priv->off+=sizeof(i); - return 0; -} -int64_t htonll(int64_t v) -{ - int i = 0; - char *s = (char *)&v; - if (htonl(1) == 1) { - return v; - } - for (i = 0; i < 4; i++) { - int tmp = s[i]; - s[i] = s[8-i-1]; - s[8-i-1] = tmp; - } - - return v; -} - -int oa_serialize_long(struct oarchive *oa, const char *tag, const int64_t *d) -{ - const int64_t i = htonll(*d); - struct buff_struct *priv = oa->priv; - if ((priv->len - priv->off) < sizeof(i)) { - int rc = resize_buffer(priv, priv->len + sizeof(i)); - if (rc < 0) return rc; - } - memcpy(priv->buffer+priv->off, &i, sizeof(i)); - priv->off+=sizeof(i); - return 0; -} -int oa_start_vector(struct oarchive *oa, const char *tag, const int32_t *count) -{ - return oa_serialize_int(oa, tag, count); -} -int oa_end_vector(struct oarchive *oa, const char *tag) -{ - return 0; -} -int oa_serialize_bool(struct oarchive *oa, const char *name, const int32_t *i) -{ - //return oa_serialize_int(oa, name, i); - struct buff_struct *priv = oa->priv; - if ((priv->len - priv->off) < 1) { - int rc = resize_buffer(priv, priv->len + 1); - if (rc < 0) - return rc; - } - priv->buffer[priv->off] = (*i == 0 ? '\0' : '\1'); - priv->off++; - return 0; -} -static const int32_t negone = -1; -int oa_serialize_buffer(struct oarchive *oa, const char *name, - const struct buffer *b) -{ - struct buff_struct *priv = oa->priv; - int rc; - if (!b) { - return oa_serialize_int(oa, "len", &negone); - } - rc = oa_serialize_int(oa, "len", &b->len); - if (rc < 0) - return rc; - // this means a buffer of NUll - // with size of -1. This is - // waht we use in java serialization for NULL - if (b->len == -1) { - return rc; - } - if ((priv->len - priv->off) < b->len) { - rc = resize_buffer(priv, priv->len + b->len); - if (rc < 0) - return rc; - } - memcpy(priv->buffer+priv->off, b->buff, b->len); - priv->off += b->len; - return 0; -} -int oa_serialize_string(struct oarchive *oa, const char *name, char **s) -{ - struct buff_struct *priv = oa->priv; - int32_t len; - int rc; - if (!*s) { - oa_serialize_int(oa, "len", &negone); - return 0; - } - len = strlen(*s); - rc = oa_serialize_int(oa, "len", &len); - if (rc < 0) - return rc; - if ((priv->len - priv->off) < len) { - rc = resize_buffer(priv, priv->len + len); - if (rc < 0) - return rc; - } - memcpy(priv->buffer+priv->off, *s, len); - priv->off += len; - return 0; -} -int ia_start_record(struct iarchive *ia, const char *tag) -{ - return 0; -} -int ia_end_record(struct iarchive *ia, const char *tag) -{ - return 0; -} -int ia_deserialize_int(struct iarchive *ia, const char *tag, int32_t *count) -{ - struct buff_struct *priv = ia->priv; - if ((priv->len - priv->off) < sizeof(*count)) { - return -E2BIG; - } - memcpy(count, priv->buffer+priv->off, sizeof(*count)); - priv->off+=sizeof(*count); - *count = ntohl(*count); - return 0; -} - -int ia_deserialize_long(struct iarchive *ia, const char *tag, int64_t *count) -{ - struct buff_struct *priv = ia->priv; - int64_t v = 0; - if ((priv->len - priv->off) < sizeof(*count)) { - return -E2BIG; - } - memcpy(count, priv->buffer+priv->off, sizeof(*count)); - priv->off+=sizeof(*count); - v = htonll(*count); // htonll and ntohll do the same - *count = v; - return 0; -} -int ia_start_vector(struct iarchive *ia, const char *tag, int32_t *count) -{ - return ia_deserialize_int(ia, tag, count); -} -int ia_end_vector(struct iarchive *ia, const char *tag) -{ - return 0; -} -int ia_deserialize_bool(struct iarchive *ia, const char *name, int32_t *v) -{ - struct buff_struct *priv = ia->priv; - //fprintf(stderr, "Deserializing bool %d\n", priv->off); - //return ia_deserialize_int(ia, name, v); - if ((priv->len - priv->off) < 1) { - return -E2BIG; - } - *v = priv->buffer[priv->off]; - priv->off+=1; - //fprintf(stderr, "Deserializing bool end %d\n", priv->off); - return 0; -} -int ia_deserialize_buffer(struct iarchive *ia, const char *name, - struct buffer *b) -{ - struct buff_struct *priv = ia->priv; - int rc = ia_deserialize_int(ia, "len", &b->len); - if (rc < 0) - return rc; - if ((priv->len - priv->off) < b->len) { - return -E2BIG; - } - // set the buffer to null - if (b->len == -1) { - b->buff = NULL; - return rc; - } - b->buff = malloc(b->len); - if (!b->buff) { - return -ENOMEM; - } - memcpy(b->buff, priv->buffer+priv->off, b->len); - priv->off += b->len; - return 0; -} -int ia_deserialize_string(struct iarchive *ia, const char *name, char **s) -{ - struct buff_struct *priv = ia->priv; - int32_t len; - int rc = ia_deserialize_int(ia, "len", &len); - if (rc < 0) - return rc; - if ((priv->len - priv->off) < len) { - return -E2BIG; - } - if (len < 0) { - return -EINVAL; - } - *s = malloc(len+1); - if (!*s) { - return -ENOMEM; - } - memcpy(*s, priv->buffer+priv->off, len); - (*s)[len] = '\0'; - priv->off += len; - return 0; -} - -static struct iarchive ia_default = { .start_record = ia_start_record, - .end_record = ia_end_record, .start_vector = ia_start_vector, - .end_vector = ia_end_vector, .deserialize_Bool = ia_deserialize_bool, - .deserialize_Int = ia_deserialize_int, - .deserialize_Buffer = ia_deserialize_buffer, - .deserialize_String = ia_deserialize_string, - .deserialize_Long = ia_deserialize_long }; - -static struct oarchive oa_default = { .start_record = oa_start_record, - .end_record = oa_end_record, .start_vector = oa_start_vector, - .end_vector = oa_end_vector, .serialize_Bool = oa_serialize_bool, - .serialize_Int = oa_serialize_int, - .serialize_Buffer = oa_serialize_buffer, - .serialize_String = oa_serialize_string, - .serialize_Long = oa_serialize_long }; - -struct iarchive *create_buffer_iarchive(char *buffer, int len) -{ - struct iarchive *ia = malloc(sizeof(*ia)); - struct buff_struct *buff = malloc(sizeof(struct buff_struct)); - if (!ia) return 0; - if (!buff) { - free(ia); - return 0; - } - *ia = ia_default; - buff->off = 0; - buff->buffer = buffer; - buff->len = len; - ia->priv = buff; - return ia; -} - -struct oarchive *create_buffer_oarchive() -{ - struct oarchive *oa = malloc(sizeof(*oa)); - struct buff_struct *buff = malloc(sizeof(struct buff_struct)); - if (!oa) return 0; - if (!buff) { - free(oa); - return 0; - } - *oa = oa_default; - buff->off = 0; - buff->buffer = malloc(128); - buff->len = 128; - oa->priv = buff; - return oa; -} - -void close_buffer_iarchive(struct iarchive **ia) -{ - free((*ia)->priv); - free(*ia); - *ia = 0; -} - -void close_buffer_oarchive(struct oarchive **oa, int free_buffer) -{ - if (free_buffer) { - struct buff_struct *buff = (struct buff_struct *)(*oa)->priv; - if (buff->buffer) { - free(buff->buffer); - } - } - free((*oa)->priv); - free(*oa); - *oa = 0; -} - -char *get_buffer(struct oarchive *oa) -{ - struct buff_struct *buff = oa->priv; - return buff->buffer; -} -int get_buffer_len(struct oarchive *oa) -{ - struct buff_struct *buff = oa->priv; - return buff->off; -} diff --git a/src/c/src/st_adaptor.c b/src/c/src/st_adaptor.c deleted file mode 100644 index 7609edf8c0b..00000000000 --- a/src/c/src/st_adaptor.c +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DLL_EXPORT -# define USE_STATIC_LIB -#endif - -#include "zk_adaptor.h" -#include -#include - -void zoo_lock_auth(zhandle_t *zh) -{ -} -void zoo_unlock_auth(zhandle_t *zh) -{ -} -void lock_buffer_list(buffer_head_t *l) -{ -} -void unlock_buffer_list(buffer_head_t *l) -{ -} -void lock_completion_list(completion_head_t *l) -{ -} -void unlock_completion_list(completion_head_t *l) -{ -} -struct sync_completion *alloc_sync_completion(void) -{ - return (struct sync_completion*)calloc(1, sizeof(struct sync_completion)); -} -int wait_sync_completion(struct sync_completion *sc) -{ - return 0; -} - -void free_sync_completion(struct sync_completion *sc) -{ - free(sc); -} - -void notify_sync_completion(struct sync_completion *sc) -{ -} - -int process_async(int outstanding_sync) -{ - return outstanding_sync == 0; -} - -int adaptor_init(zhandle_t *zh) -{ - return 0; -} - -void adaptor_finish(zhandle_t *zh){} - -void adaptor_destroy(zhandle_t *zh){} - -int flush_send_queue(zhandle_t *, int); - -int adaptor_send_queue(zhandle_t *zh, int timeout) -{ - return flush_send_queue(zh, timeout); -} - -int32_t inc_ref_counter(zhandle_t* zh,int i) -{ - zh->ref_counter+=(i<0?-1:(i>0?1:0)); - return zh->ref_counter; -} - -int32_t get_xid() -{ - static int32_t xid = -1; - if (xid == -1) { - xid = time(0); - } - return xid++; -} -void enter_critical(zhandle_t* zh){} -void leave_critical(zhandle_t* zh){} diff --git a/src/c/src/zk_adaptor.h b/src/c/src/zk_adaptor.h deleted file mode 100644 index c7122c0a545..00000000000 --- a/src/c/src/zk_adaptor.h +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ZK_ADAPTOR_H_ -#define ZK_ADAPTOR_H_ -#include -#ifdef THREADED -#include -#endif -#include "zookeeper.h" -#include "zk_hashtable.h" - -/* predefined xid's values recognized as special by the server */ -#define WATCHER_EVENT_XID -1 -#define PING_XID -2 -#define AUTH_XID -4 -#define SET_WATCHES_XID -8 - -/* zookeeper state constants */ -#define EXPIRED_SESSION_STATE_DEF -112 -#define AUTH_FAILED_STATE_DEF -113 -#define CONNECTING_STATE_DEF 1 -#define ASSOCIATING_STATE_DEF 2 -#define CONNECTED_STATE_DEF 3 - -/* zookeeper event type constants */ -#define CREATED_EVENT_DEF 1 -#define DELETED_EVENT_DEF 2 -#define CHANGED_EVENT_DEF 3 -#define CHILD_EVENT_DEF 4 -#define SESSION_EVENT_DEF -1 -#define NOTWATCHING_EVENT_DEF -2 - -#ifdef __cplusplus -extern "C" { -#endif - -struct _buffer_list; -struct _completion_list; - -typedef struct _buffer_head { - struct _buffer_list *volatile head; - struct _buffer_list *last; -#ifdef THREADED - pthread_mutex_t lock; -#endif -} buffer_head_t; - -typedef struct _completion_head { - struct _completion_list *volatile head; - struct _completion_list *last; -#ifdef THREADED - pthread_cond_t cond; - pthread_mutex_t lock; -#endif -} completion_head_t; - -void lock_buffer_list(buffer_head_t *l); -void unlock_buffer_list(buffer_head_t *l); -void lock_completion_list(completion_head_t *l); -void unlock_completion_list(completion_head_t *l); - -struct sync_completion { - int rc; - union { - struct { - char *str; - int str_len; - } str; - struct Stat stat; - struct { - char *buffer; - int buff_len; - struct Stat stat; - } data; - struct { - struct ACL_vector acl; - struct Stat stat; - } acl; - struct String_vector strs2; - struct { - struct String_vector strs2; - struct Stat stat2; - } strs_stat; - } u; - int complete; -#ifdef THREADED - pthread_cond_t cond; - pthread_mutex_t lock; -#endif -}; - -typedef struct _auth_info { - int state; /* 0=>inactive, >0 => active */ - char* scheme; - struct buffer auth; - void_completion_t completion; - const char* data; - struct _auth_info *next; -} auth_info; - -/** - * This structure represents a packet being read or written. - */ -typedef struct _buffer_list { - char *buffer; - int len; /* This represents the length of sizeof(header) + length of buffer */ - int curr_offset; /* This is the offset into the header followed by offset into the buffer */ - struct _buffer_list *next; -} buffer_list_t; - -/* the size of connect request */ -#define HANDSHAKE_REQ_SIZE 44 -/* connect request */ -struct connect_req { - int32_t protocolVersion; - int64_t lastZxidSeen; - int32_t timeOut; - int64_t sessionId; - int32_t passwd_len; - char passwd[16]; -}; - -/* the connect response */ -struct prime_struct { - int32_t len; - int32_t protocolVersion; - int32_t timeOut; - int64_t sessionId; - int32_t passwd_len; - char passwd[16]; -}; - -#ifdef THREADED -/* this is used by mt_adaptor internally for thread management */ -struct adaptor_threads { - pthread_t io; - pthread_t completion; - int threadsToWait; // barrier - pthread_cond_t cond; // barrier's conditional - pthread_mutex_t lock; // ... and a lock - pthread_mutex_t zh_lock; // critical section lock - int self_pipe[2]; -}; -#endif - -/** the auth list for adding auth */ -typedef struct _auth_list_head { - auth_info *auth; -#ifdef THREADED - pthread_mutex_t lock; -#endif -} auth_list_head_t; - -/** - * This structure represents the connection to zookeeper. - */ - -struct _zhandle { - int fd; /* the descriptor used to talk to zookeeper */ - char *hostname; /* the hostname of zookeeper */ - struct sockaddr_storage *addrs; /* the addresses that correspond to the hostname */ - int addrs_count; /* The number of addresses in the addrs array */ - watcher_fn watcher; /* the registered watcher */ - struct timeval last_recv; /* The time that the last message was received */ - struct timeval last_send; /* The time that the last message was sent */ - struct timeval last_ping; /* The time that the last PING was sent */ - struct timeval next_deadline; /* The time of the next deadline */ - int recv_timeout; /* The maximum amount of time that can go by without - receiving anything from the zookeeper server */ - buffer_list_t *input_buffer; /* the current buffer being read in */ - buffer_head_t to_process; /* The buffers that have been read and are ready to be processed. */ - buffer_head_t to_send; /* The packets queued to send */ - completion_head_t sent_requests; /* The outstanding requests */ - completion_head_t completions_to_process; /* completions that are ready to run */ - int connect_index; /* The index of the address to connect to */ - clientid_t client_id; - long long last_zxid; - int outstanding_sync; /* Number of outstanding synchronous requests */ - struct _buffer_list primer_buffer; /* The buffer used for the handshake at the start of a connection */ - struct prime_struct primer_storage; /* the connect response */ - char primer_storage_buffer[40]; /* the true size of primer_storage */ - volatile int state; - void *context; - auth_list_head_t auth_h; /* authentication data list */ - /* zookeeper_close is not reentrant because it de-allocates the zhandler. - * This guard variable is used to defer the destruction of zhandle till - * right before top-level API call returns to the caller */ - int32_t ref_counter; - volatile int close_requested; - void *adaptor_priv; - /* Used for debugging only: non-zero value indicates the time when the zookeeper_process - * call returned while there was at least one unprocessed server response - * available in the socket recv buffer */ - struct timeval socket_readable; - - zk_hashtable* active_node_watchers; - zk_hashtable* active_exist_watchers; - zk_hashtable* active_child_watchers; - /** used for chroot path at the client side **/ - char *chroot; -}; - - -int adaptor_init(zhandle_t *zh); -void adaptor_finish(zhandle_t *zh); -void adaptor_destroy(zhandle_t *zh); -struct sync_completion *alloc_sync_completion(void); -int wait_sync_completion(struct sync_completion *sc); -void free_sync_completion(struct sync_completion *sc); -void notify_sync_completion(struct sync_completion *sc); -int adaptor_send_queue(zhandle_t *zh, int timeout); -int process_async(int outstanding_sync); -void process_completions(zhandle_t *zh); -int flush_send_queue(zhandle_t*zh, int timeout); -char* sub_string(zhandle_t *zh, const char* server_path); -void free_duplicate_path(const char* free_path, const char* path); -void zoo_lock_auth(zhandle_t *zh); -void zoo_unlock_auth(zhandle_t *zh); - -// critical section guards -void enter_critical(zhandle_t* zh); -void leave_critical(zhandle_t* zh); -// zhandle object reference counting -void api_prolog(zhandle_t* zh); -int api_epilog(zhandle_t *zh, int rc); -int32_t get_xid(); -// returns the new value of the ref counter -int32_t inc_ref_counter(zhandle_t* zh,int i); - -#ifdef THREADED -// atomic post-increment -int32_t fetch_and_add(volatile int32_t* operand, int incr); -// in mt mode process session event asynchronously by the completion thread -#define PROCESS_SESSION_EVENT(zh,newstate) queue_session_event(zh,newstate) -#else -// in single-threaded mode process session event immediately -//#define PROCESS_SESSION_EVENT(zh,newstate) deliverWatchers(zh,ZOO_SESSION_EVENT,newstate,0) -#define PROCESS_SESSION_EVENT(zh,newstate) queue_session_event(zh,newstate) -#endif - -#ifdef __cplusplus -} -#endif - -#endif /*ZK_ADAPTOR_H_*/ - - diff --git a/src/c/src/zk_hashtable.c b/src/c/src/zk_hashtable.c deleted file mode 100644 index 6ac80044bae..00000000000 --- a/src/c/src/zk_hashtable.c +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "zk_hashtable.h" -#include "zk_adaptor.h" -#include "hashtable/hashtable.h" -#include "hashtable/hashtable_itr.h" -#include -#include -#include - -typedef struct _watcher_object { - watcher_fn watcher; - void* context; - struct _watcher_object* next; -} watcher_object_t; - - -struct _zk_hashtable { - struct hashtable* ht; -}; - -struct watcher_object_list { - watcher_object_t* head; -}; - -/* the following functions are for testing only */ -typedef struct hashtable hashtable_impl; - -hashtable_impl* getImpl(zk_hashtable* ht){ - return ht->ht; -} - -watcher_object_t* getFirstWatcher(zk_hashtable* ht,const char* path) -{ - watcher_object_list_t* wl=hashtable_search(ht->ht,(void*)path); - if(wl!=0) - return wl->head; - return 0; -} -/* end of testing functions */ - -watcher_object_t* clone_watcher_object(watcher_object_t* wo) -{ - watcher_object_t* res=calloc(1,sizeof(watcher_object_t)); - assert(res); - res->watcher=wo->watcher; - res->context=wo->context; - return res; -} - -static unsigned int string_hash_djb2(void *str) -{ - unsigned int hash = 5381; - int c; - - while ((c = *(const char*)str++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - - return hash; -} - -static int string_equal(void *key1,void *key2) -{ - return strcmp((const char*)key1,(const char*)key2)==0; -} - -static watcher_object_t* create_watcher_object(watcher_fn watcher,void* ctx) -{ - watcher_object_t* wo=calloc(1,sizeof(watcher_object_t)); - assert(wo); - wo->watcher=watcher; - wo->context=ctx; - return wo; -} - -static watcher_object_list_t* create_watcher_object_list(watcher_object_t* head) -{ - watcher_object_list_t* wl=calloc(1,sizeof(watcher_object_list_t)); - assert(wl); - wl->head=head; - return wl; -} - -static void destroy_watcher_object_list(watcher_object_list_t* list) -{ - watcher_object_t* e = NULL; - - if(list==0) - return; - e=list->head; - while(e!=0){ - watcher_object_t* this=e; - e=e->next; - free(this); - } - free(list); -} - -zk_hashtable* create_zk_hashtable() -{ - struct _zk_hashtable *ht=calloc(1,sizeof(struct _zk_hashtable)); - assert(ht); - ht->ht=create_hashtable(32,string_hash_djb2,string_equal); - return ht; -} - -static void do_clean_hashtable(zk_hashtable* ht) -{ - struct hashtable_itr *it; - int hasMore; - if(hashtable_count(ht->ht)==0) - return; - it=hashtable_iterator(ht->ht); - do { - watcher_object_list_t* w=hashtable_iterator_value(it); - destroy_watcher_object_list(w); - hasMore=hashtable_iterator_remove(it); - } while(hasMore); - free(it); -} - -void destroy_zk_hashtable(zk_hashtable* ht) -{ - if(ht!=0){ - do_clean_hashtable(ht); - hashtable_destroy(ht->ht,0); - free(ht); - } -} - -// searches for a watcher object instance in a watcher object list; -// two watcher objects are equal if their watcher function and context pointers -// are equal -static watcher_object_t* search_watcher(watcher_object_list_t** wl,watcher_object_t* wo) -{ - watcher_object_t* wobj=(*wl)->head; - while(wobj!=0){ - if(wobj->watcher==wo->watcher && wobj->context==wo->context) - return wobj; - wobj=wobj->next; - } - return 0; -} - -static int add_to_list(watcher_object_list_t **wl, watcher_object_t *wo, - int clone) -{ - if (search_watcher(wl, wo)==0) { - watcher_object_t* cloned=wo; - if (clone) { - cloned = clone_watcher_object(wo); - assert(cloned); - } - cloned->next = (*wl)->head; - (*wl)->head = cloned; - return 1; - } else if (!clone) { - // If it's here and we aren't supposed to clone, we must destroy - free(wo); - } - return 0; -} - -static int do_insert_watcher_object(zk_hashtable *ht, const char *path, watcher_object_t* wo) -{ - int res=1; - watcher_object_list_t* wl; - - wl=hashtable_search(ht->ht,(void*)path); - if(wl==0){ - int res; - /* inserting a new path element */ - res=hashtable_insert(ht->ht,strdup(path),create_watcher_object_list(wo)); - assert(res); - }else{ - /* path already exists; check if the watcher already exists */ - res = add_to_list(&wl, wo, 1); - } - return res; -} - - -char **collect_keys(zk_hashtable *ht, int *count) -{ - char **list; - struct hashtable_itr *it; - int i; - - *count = hashtable_count(ht->ht); - list = calloc(*count, sizeof(char*)); - it=hashtable_iterator(ht->ht); - for(i = 0; i < *count; i++) { - list[i] = strdup(hashtable_iterator_key(it)); - hashtable_iterator_advance(it); - } - free(it); - return list; -} - -static int insert_watcher_object(zk_hashtable *ht, const char *path, - watcher_object_t* wo) -{ - int res; - res=do_insert_watcher_object(ht,path,wo); - return res; -} - -static void copy_watchers(watcher_object_list_t *from, watcher_object_list_t *to, int clone) -{ - watcher_object_t* wo=from->head; - while(wo){ - watcher_object_t *next = wo->next; - add_to_list(&to, wo, clone); - wo=next; - } -} - -static void copy_table(zk_hashtable *from, watcher_object_list_t *to) { - struct hashtable_itr *it; - int hasMore; - if(hashtable_count(from->ht)==0) - return; - it=hashtable_iterator(from->ht); - do { - watcher_object_list_t *w = hashtable_iterator_value(it); - copy_watchers(w, to, 1); - hasMore=hashtable_iterator_advance(it); - } while(hasMore); - free(it); -} - -static void collect_session_watchers(zhandle_t *zh, - watcher_object_list_t **list) -{ - copy_table(zh->active_node_watchers, *list); - copy_table(zh->active_exist_watchers, *list); - copy_table(zh->active_child_watchers, *list); -} - -static void add_for_event(zk_hashtable *ht, char *path, watcher_object_list_t **list) -{ - watcher_object_list_t* wl; - wl = (watcher_object_list_t*)hashtable_remove(ht->ht, path); - if (wl) { - copy_watchers(wl, *list, 0); - // Since we move, not clone the watch_objects, we just need to free the - // head pointer - free(wl); - } -} - -static void do_foreach_watcher(watcher_object_t* wo,zhandle_t* zh, - const char* path,int type,int state) -{ - // session event's don't have paths - const char *client_path = - (type != ZOO_SESSION_EVENT ? sub_string(zh, path) : path); - while(wo!=0){ - wo->watcher(zh,type,state,client_path,wo->context); - wo=wo->next; - } - free_duplicate_path(client_path, path); -} - -watcher_object_list_t *collectWatchers(zhandle_t *zh,int type, char *path) -{ - struct watcher_object_list *list = create_watcher_object_list(0); - - if(type==ZOO_SESSION_EVENT){ - watcher_object_t defWatcher; - defWatcher.watcher=zh->watcher; - defWatcher.context=zh->context; - add_to_list(&list, &defWatcher, 1); - collect_session_watchers(zh, &list); - return list; - } - switch(type){ - case CREATED_EVENT_DEF: - case CHANGED_EVENT_DEF: - // look up the watchers for the path and move them to a delivery list - add_for_event(zh->active_node_watchers,path,&list); - add_for_event(zh->active_exist_watchers,path,&list); - break; - case CHILD_EVENT_DEF: - // look up the watchers for the path and move them to a delivery list - add_for_event(zh->active_child_watchers,path,&list); - break; - case DELETED_EVENT_DEF: - // look up the watchers for the path and move them to a delivery list - add_for_event(zh->active_node_watchers,path,&list); - add_for_event(zh->active_exist_watchers,path,&list); - add_for_event(zh->active_child_watchers,path,&list); - break; - } - return list; -} - -void deliverWatchers(zhandle_t *zh, int type,int state, char *path, watcher_object_list_t **list) -{ - if (!list || !(*list)) return; - do_foreach_watcher((*list)->head, zh, path, type, state); - destroy_watcher_object_list(*list); - *list = 0; -} - -void activateWatcher(zhandle_t *zh, watcher_registration_t* reg, int rc) -{ - if(reg){ - /* in multithreaded lib, this code is executed - * by the IO thread */ - zk_hashtable *ht = reg->checker(zh, rc); - if(ht){ - insert_watcher_object(ht,reg->path, - create_watcher_object(reg->watcher, reg->context)); - } - } -} diff --git a/src/c/src/zk_hashtable.h b/src/c/src/zk_hashtable.h deleted file mode 100644 index 31109c11fbe..00000000000 --- a/src/c/src/zk_hashtable.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ZK_HASHTABLE_H_ -#define ZK_HASHTABLE_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - typedef struct watcher_object_list watcher_object_list_t; -typedef struct _zk_hashtable zk_hashtable; - -/** - * The function must return a non-zero value if the watcher object can be activated - * as a result of the server response. Normally, a watch can only be activated - * if the server returns a success code (ZOK). However in the case when zoo_exists() - * returns a ZNONODE code the watcher should be activated nevertheless. - */ -typedef zk_hashtable *(*result_checker_fn)(zhandle_t *, int rc); - -/** - * A watcher object gets temporarily stored with the completion entry until - * the server response comes back at which moment the watcher object is moved - * to the active watchers map. - */ -typedef struct _watcher_registration { - watcher_fn watcher; - void* context; - result_checker_fn checker; - const char* path; -} watcher_registration_t; - -zk_hashtable* create_zk_hashtable(); -void destroy_zk_hashtable(zk_hashtable* ht); - -char **collect_keys(zk_hashtable *ht, int *count); - -/** - * check if the completion has a watcher object associated - * with it. If it does, move the watcher object to the map of - * active watchers (only if the checker allows to do so) - */ - void activateWatcher(zhandle_t *zh, watcher_registration_t* reg, int rc); - watcher_object_list_t *collectWatchers(zhandle_t *zh,int type, char *path); - void deliverWatchers(zhandle_t *zh, int type, int state, char *path, struct watcher_object_list **list); - -#ifdef __cplusplus -} -#endif - -#endif /*ZK_HASHTABLE_H_*/ diff --git a/src/c/src/zk_log.c b/src/c/src/zk_log.c deleted file mode 100644 index 2b85163e12e..00000000000 --- a/src/c/src/zk_log.c +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DLL_EXPORT -# define USE_STATIC_LIB -#endif - -#include "zookeeper_log.h" -#include -#include -#include - -#define TIME_NOW_BUF_SIZE 1024 -#define FORMAT_LOG_BUF_SIZE 4096 - -#ifdef THREADED -#include - -static pthread_key_t time_now_buffer; -static pthread_key_t format_log_msg_buffer; - -void freeBuffer(void* p){ - if(p) free(p); -} - -__attribute__((constructor)) void prepareTSDKeys() { - pthread_key_create (&time_now_buffer, freeBuffer); - pthread_key_create (&format_log_msg_buffer, freeBuffer); -} - -char* getTSData(pthread_key_t key,int size){ - char* p=pthread_getspecific(key); - if(p==0){ - int res; - p=calloc(1,size); - res=pthread_setspecific(key,p); - if(res!=0){ - fprintf(stderr,"Failed to set TSD key: %d",res); - } - } - return p; -} - -char* get_time_buffer(){ - return getTSData(time_now_buffer,TIME_NOW_BUF_SIZE); -} - -char* get_format_log_buffer(){ - return getTSData(format_log_msg_buffer,FORMAT_LOG_BUF_SIZE); -} -#else -char* get_time_buffer(){ - static char buf[TIME_NOW_BUF_SIZE]; - return buf; -} - -char* get_format_log_buffer(){ - static char buf[FORMAT_LOG_BUF_SIZE]; - return buf; -} - -#endif - -ZooLogLevel logLevel=ZOO_LOG_LEVEL_INFO; - -static FILE* logStream=0; -FILE* getLogStream(){ - if(logStream==0) - logStream=stderr; - return logStream; -} - -void zoo_set_log_stream(FILE* stream){ - logStream=stream; -} - -static const char* time_now(){ - struct timeval tv; - struct tm lt; - time_t now = 0; - size_t len = 0; - char* now_str=get_time_buffer(); - - if(!now_str) - return "time_now(): Failed to allocate memory buffer"; - - gettimeofday(&tv,0); - - now = tv.tv_sec; - localtime_r(&now, <); - - // clone the format used by log4j ISO8601DateFormat - // specifically: "yyyy-MM-dd HH:mm:ss,SSS" - - len = strftime(now_str, TIME_NOW_BUF_SIZE, - "%F %H:%M:%S", - <); - - len += snprintf(now_str + len, - TIME_NOW_BUF_SIZE - len, - ",%03d", - (int)(tv.tv_usec/1000)); - - return now_str; -} - -void log_message(ZooLogLevel curLevel,int line,const char* funcName, - const char* message) -{ - static const char* dbgLevelStr[]={"ZOO_INVALID","ZOO_ERROR","ZOO_WARN", - "ZOO_INFO","ZOO_DEBUG"}; - static pid_t pid=0; - if(pid==0)pid=getpid(); -#ifndef THREADED - fprintf(LOGSTREAM, "%s:%d:%s@%s@%d: %s\n", time_now(),pid, - dbgLevelStr[curLevel],funcName,line,message); -#else - fprintf(LOGSTREAM, "%s:%d(0x%lx):%s@%s@%d: %s\n", time_now(),pid, - (unsigned long int)pthread_self(), - dbgLevelStr[curLevel],funcName,line,message); -#endif - fflush(LOGSTREAM); -} - -const char* format_log_message(const char* format,...) -{ - va_list va; - char* buf=get_format_log_buffer(); - if(!buf) - return "format_log_message: Unable to allocate memory buffer"; - - va_start(va,format); - vsnprintf(buf, FORMAT_LOG_BUF_SIZE-1,format,va); - va_end(va); - return buf; -} - -void zoo_set_debug_level(ZooLogLevel level) -{ - if(level==0){ - // disable logging (unit tests do this) - logLevel=(ZooLogLevel)0; - return; - } - if(levelZOO_LOG_LEVEL_DEBUG)level=ZOO_LOG_LEVEL_DEBUG; - logLevel=level; -} - diff --git a/src/c/src/zookeeper.c b/src/c/src/zookeeper.c deleted file mode 100644 index 658fe45a82a..00000000000 --- a/src/c/src/zookeeper.c +++ /dev/null @@ -1,3246 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DLL_EXPORT -# define USE_STATIC_LIB -#endif - -#if defined(__CYGWIN__) -#define USE_IPV6 -#endif - -#include -#include -#include -#include "zk_adaptor.h" -#include "zookeeper_log.h" -#include "zk_hashtable.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" - -#ifdef HAVE_SYS_UTSNAME_H -#include -#endif - -#ifdef HAVE_GETPWUID_R -#include -#endif - -#define IF_DEBUG(x) if(logLevel==ZOO_LOG_LEVEL_DEBUG) {x;} - -const int ZOOKEEPER_WRITE = 1 << 0; -const int ZOOKEEPER_READ = 1 << 1; - -const int ZOO_EPHEMERAL = 1 << 0; -const int ZOO_SEQUENCE = 1 << 1; - -const int ZOO_EXPIRED_SESSION_STATE = EXPIRED_SESSION_STATE_DEF; -const int ZOO_AUTH_FAILED_STATE = AUTH_FAILED_STATE_DEF; -const int ZOO_CONNECTING_STATE = CONNECTING_STATE_DEF; -const int ZOO_ASSOCIATING_STATE = ASSOCIATING_STATE_DEF; -const int ZOO_CONNECTED_STATE = CONNECTED_STATE_DEF; -static __attribute__ ((unused)) const char* state2String(int state){ - switch(state){ - case 0: - return "ZOO_CLOSED_STATE"; - case CONNECTING_STATE_DEF: - return "ZOO_CONNECTING_STATE"; - case ASSOCIATING_STATE_DEF: - return "ZOO_ASSOCIATING_STATE"; - case CONNECTED_STATE_DEF: - return "ZOO_CONNECTED_STATE"; - case EXPIRED_SESSION_STATE_DEF: - return "ZOO_EXPIRED_SESSION_STATE"; - case AUTH_FAILED_STATE_DEF: - return "ZOO_AUTH_FAILED_STATE"; - } - return "INVALID_STATE"; -} - -const int ZOO_CREATED_EVENT = CREATED_EVENT_DEF; -const int ZOO_DELETED_EVENT = DELETED_EVENT_DEF; -const int ZOO_CHANGED_EVENT = CHANGED_EVENT_DEF; -const int ZOO_CHILD_EVENT = CHILD_EVENT_DEF; -const int ZOO_SESSION_EVENT = SESSION_EVENT_DEF; -const int ZOO_NOTWATCHING_EVENT = NOTWATCHING_EVENT_DEF; -static __attribute__ ((unused)) const char* watcherEvent2String(int ev){ - switch(ev){ - case 0: - return "ZOO_ERROR_EVENT"; - case CREATED_EVENT_DEF: - return "ZOO_CREATED_EVENT"; - case DELETED_EVENT_DEF: - return "ZOO_DELETED_EVENT"; - case CHANGED_EVENT_DEF: - return "ZOO_CHANGED_EVENT"; - case CHILD_EVENT_DEF: - return "ZOO_CHILD_EVENT"; - case SESSION_EVENT_DEF: - return "ZOO_SESSION_EVENT"; - case NOTWATCHING_EVENT_DEF: - return "ZOO_NOTWATCHING_EVENT"; - } - return "INVALID_EVENT"; -} - -const int ZOO_PERM_READ = 1 << 0; -const int ZOO_PERM_WRITE = 1 << 1; -const int ZOO_PERM_CREATE = 1 << 2; -const int ZOO_PERM_DELETE = 1 << 3; -const int ZOO_PERM_ADMIN = 1 << 4; -const int ZOO_PERM_ALL = 0x1f; -struct Id ZOO_ANYONE_ID_UNSAFE = {"world", "anyone"}; -struct Id ZOO_AUTH_IDS = {"auth", ""}; -static struct ACL _OPEN_ACL_UNSAFE_ACL[] = {{0x1f, {"world", "anyone"}}}; -static struct ACL _READ_ACL_UNSAFE_ACL[] = {{0x01, {"world", "anyone"}}}; -static struct ACL _CREATOR_ALL_ACL_ACL[] = {{0x1f, {"auth", ""}}}; -struct ACL_vector ZOO_OPEN_ACL_UNSAFE = { 1, _OPEN_ACL_UNSAFE_ACL}; -struct ACL_vector ZOO_READ_ACL_UNSAFE = { 1, _READ_ACL_UNSAFE_ACL}; -struct ACL_vector ZOO_CREATOR_ALL_ACL = { 1, _CREATOR_ALL_ACL_ACL}; - -#define COMPLETION_WATCH -1 -#define COMPLETION_VOID 0 -#define COMPLETION_STAT 1 -#define COMPLETION_DATA 2 -#define COMPLETION_STRINGLIST 3 -#define COMPLETION_STRINGLIST_STAT 4 -#define COMPLETION_ACLLIST 5 -#define COMPLETION_STRING 6 - -typedef struct _auth_completion_list { - void_completion_t completion; - const char *auth_data; - struct _auth_completion_list *next; -} auth_completion_list_t; - -typedef struct _completion_list { - int xid; - int completion_type; /* one of the COMPLETION_* values */ - union { - void_completion_t void_result; - stat_completion_t stat_result; - data_completion_t data_result; - strings_completion_t strings_result; - strings_stat_completion_t strings_stat_result; - acl_completion_t acl_result; - string_completion_t string_result; - struct watcher_object_list *watcher_result; - } c; - const void *data; - buffer_list_t *buffer; - struct _completion_list *next; - watcher_registration_t* watcher; -} completion_list_t; - -const char*err2string(int err); -static int queue_session_event(zhandle_t *zh, int state); -static const char* format_endpoint_info(const struct sockaddr_storage* ep); -static const char* format_current_endpoint_info(zhandle_t* zh); - -/* completion routine forward declarations */ -static int add_completion(zhandle_t *zh, int xid, int completion_type, - const void *dc, const void *data, int add_to_front,watcher_registration_t* wo); -static completion_list_t* create_completion_entry(int xid, int completion_type, - const void *dc, const void *data,watcher_registration_t* wo); -static void destroy_completion_entry(completion_list_t* c); -static void queue_completion_nolock(completion_head_t *list, completion_list_t *c, - int add_to_front); -static void queue_completion(completion_head_t *list, completion_list_t *c, - int add_to_front); -static int handle_socket_error_msg(zhandle_t *zh, int line, int rc, - const char* format,...); -static void cleanup_bufs(zhandle_t *zh,int callCompletion,int rc); - -static int disable_conn_permute=0; // permute enabled by default - -static __attribute__((unused)) void print_completion_queue(zhandle_t *zh); - -static void *SYNCHRONOUS_MARKER = (void*)&SYNCHRONOUS_MARKER; -static int isValidPath(const char* path, const int flags); - -const void *zoo_get_context(zhandle_t *zh) -{ - return zh->context; -} - -void zoo_set_context(zhandle_t *zh, void *context) -{ - if (zh != NULL) { - zh->context = context; - } -} - -int zoo_recv_timeout(zhandle_t *zh) -{ - return zh->recv_timeout; -} - -/** these functions are thread unsafe, so make sure that - zoo_lock_auth is called before you access them **/ -static auth_info* get_last_auth(auth_list_head_t *auth_list) { - auth_info *element; - element = auth_list->auth; - if (element == NULL) { - return NULL; - } - while (element->next != NULL) { - element = element->next; - } - return element; -} - -static void free_auth_completion(auth_completion_list_t *a_list) { - auth_completion_list_t *tmp, *ftmp; - if (a_list == NULL) { - return; - } - tmp = a_list->next; - while (tmp != NULL) { - ftmp = tmp; - tmp = tmp->next; - ftmp->completion = NULL; - ftmp->auth_data = NULL; - free(ftmp); - } - a_list->completion = NULL; - a_list->auth_data = NULL; - a_list->next = NULL; - return; -} - -static void add_auth_completion(auth_completion_list_t* a_list, void_completion_t* completion, - const char *data) { - auth_completion_list_t *element; - auth_completion_list_t *n_element; - element = a_list; - if (a_list->completion == NULL) { - //this is the first element - a_list->completion = *completion; - a_list->next = NULL; - a_list->auth_data = data; - return; - } - while (element->next != NULL) { - element = element->next; - } - n_element = (auth_completion_list_t*) malloc(sizeof(auth_completion_list_t)); - n_element->next = NULL; - n_element->completion = *completion; - n_element->auth_data = data; - element->next = n_element; - return; -} - -static void get_auth_completions(auth_list_head_t *auth_list, auth_completion_list_t *a_list) { - auth_info *element; - element = auth_list->auth; - if (element == NULL) { - return; - } - while (element) { - if (element->completion) { - add_auth_completion(a_list, &element->completion, element->data); - } - element->completion = NULL; - element = element->next; - } - return; -} - -static void add_last_auth(auth_list_head_t *auth_list, auth_info *add_el) { - auth_info *element; - element = auth_list->auth; - if (element == NULL) { - //first element in the list - auth_list->auth = add_el; - return; - } - while (element->next != NULL) { - element = element->next; - } - element->next = add_el; - return; -} - -static void init_auth_info(auth_list_head_t *auth_list) -{ - auth_list->auth = NULL; -} - -static void mark_active_auth(zhandle_t *zh) { - auth_list_head_t auth_h = zh->auth_h; - auth_info *element; - if (auth_h.auth == NULL) { - return; - } - element = auth_h.auth; - while (element->next != NULL) { - element->state = 1; - element = element->next; - } -} - -static void free_auth_info(auth_list_head_t *auth_list) -{ - auth_info *auth = auth_list->auth; - while (auth != NULL) { - auth_info* old_auth = NULL; - if(auth->scheme!=NULL) - free(auth->scheme); - deallocate_Buffer(&auth->auth); - old_auth = auth; - auth = auth->next; - free(old_auth); - } - init_auth_info(auth_list); -} - -int is_unrecoverable(zhandle_t *zh) -{ - return (zh->state<0)? ZINVALIDSTATE: ZOK; -} - -zk_hashtable *exists_result_checker(zhandle_t *zh, int rc) -{ - if (rc == ZOK) { - return zh->active_node_watchers; - } else if (rc == ZNONODE) { - return zh->active_exist_watchers; - } - return 0; -} - -zk_hashtable *data_result_checker(zhandle_t *zh, int rc) -{ - return rc==ZOK ? zh->active_node_watchers : 0; -} - -zk_hashtable *child_result_checker(zhandle_t *zh, int rc) -{ - return rc==ZOK ? zh->active_child_watchers : 0; -} - -/** - * Frees and closes everything associated with a handle, - * including the handle itself. - */ -static void destroy(zhandle_t *zh) -{ - if (zh == NULL) { - return; - } - /* call any outstanding completions with a special error code */ - cleanup_bufs(zh,1,ZCLOSING); - if (zh->hostname != 0) { - free(zh->hostname); - zh->hostname = NULL; - } - if (zh->fd != -1) { - close(zh->fd); - zh->fd = -1; - zh->state = 0; - } - if (zh->addrs != 0) { - free(zh->addrs); - zh->addrs = NULL; - } - - if (zh->chroot != 0) { - free(zh->chroot); - zh->chroot = NULL; - } - - free_auth_info(&zh->auth_h); - destroy_zk_hashtable(zh->active_node_watchers); - destroy_zk_hashtable(zh->active_exist_watchers); - destroy_zk_hashtable(zh->active_child_watchers); -} - -static void setup_random() -{ - int seed; - int fd = open("/dev/urandom", O_RDONLY); - if (fd == -1) { - seed = getpid(); - } else { - int rc = read(fd, &seed, sizeof(seed)); - assert(rc == sizeof(seed)); - close(fd); - } - srandom(seed); -} - -#ifndef __CYGWIN__ -/** - * get the errno from the return code - * of get addrinfo. Errno is not set - * with the call to getaddrinfo, so thats - * why we have to do this. - */ -static int getaddrinfo_errno(int rc) { - switch(rc) { - case EAI_NONAME: - case EAI_NODATA: - return ENOENT; - case EAI_MEMORY: - return ENOMEM; - default: - return EINVAL; - } -} -#endif - -/** - * fill in the addrs array of the zookeeper servers in the zhandle. after filling - * them in, we will permute them for load balancing. - */ -int getaddrs(zhandle_t *zh) -{ - char *hosts = strdup(zh->hostname); - char *host; - char *strtok_last; - struct sockaddr_storage *addr; - int i; - int rc; - int alen = 0; /* the allocated length of the addrs array */ - - zh->addrs_count = 0; - if (zh->addrs) { - free(zh->addrs); - zh->addrs = 0; - } - if (!hosts) { - LOG_ERROR(("out of memory")); - errno=ENOMEM; - return ZSYSTEMERROR; - } - zh->addrs = 0; - host=strtok_r(hosts, ",", &strtok_last); - while(host) { - char *port_spec = strrchr(host, ':'); - char *end_port_spec; - int port; - if (!port_spec) { - LOG_ERROR(("no port in %s", host)); - errno=EINVAL; - rc=ZBADARGUMENTS; - goto fail; - } - *port_spec = '\0'; - port_spec++; - port = strtol(port_spec, &end_port_spec, 0); - if (!*port_spec || *end_port_spec) { - LOG_ERROR(("invalid port in %s", host)); - errno=EINVAL; - rc=ZBADARGUMENTS; - goto fail; - } -#if defined(__CYGWIN__) - // sadly CYGWIN doesn't have getaddrinfo - // but happily gethostbyname is threadsafe in windows - { - struct hostent *he; - char **ptr; - struct sockaddr_in *addr4; - - he = gethostbyname(host); - if (!he) { - LOG_ERROR(("could not resolve %s", host)); - errno=ENOENT; - rc=ZBADARGUMENTS; - goto fail; - } - - /* Setup the address array */ - for(ptr = he->h_addr_list;*ptr != 0; ptr++) { - if (zh->addrs_count == alen) { - alen += 16; - zh->addrs = realloc(zh->addrs, sizeof(*zh->addrs)*alen); - if (zh->addrs == 0) { - LOG_ERROR(("out of memory")); - errno=ENOMEM; - rc=ZSYSTEMERROR; - goto fail; - } - } - addr = &zh->addrs[zh->addrs_count]; - addr4 = (struct sockaddr_in*)addr; - addr->ss_family = he->h_addrtype; - if (addr->ss_family == AF_INET) { - addr4->sin_port = htons(port); - memset(&addr4->sin_zero, 0, sizeof(addr4->sin_zero)); - memcpy(&addr4->sin_addr, *ptr, he->h_length); - zh->addrs_count++; - } -#if defined(AF_INET6) - else if (addr->ss_family == AF_INET6) { - struct sockaddr_in6 *addr6; - - addr6 = (struct sockaddr_in6*)addr; - addr6->sin6_port = htons(port); - addr6->sin6_scope_id = 0; - addr6->sin6_flowinfo = 0; - memcpy(&addr6->sin6_addr, *ptr, he->h_length); - zh->addrs_count++; - } -#endif - else { - LOG_WARN(("skipping unknown address family %x for %s", - addr->ss_family, zh->hostname)); - } - } - host = strtok_r(0, ",", &strtok_last); - } -#else - { - struct addrinfo hints, *res, *res0; - - memset(&hints, 0, sizeof(hints)); -#ifdef AI_ADDRCONFIG - hints.ai_flags = AI_ADDRCONFIG; -#else - hints.ai_flags = 0; -#endif - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - while(isspace(*host) && host != strtok_last) - host++; - - if ((rc = getaddrinfo(host, port_spec, &hints, &res0)) != 0) { - //bug in getaddrinfo implementation when it returns - //EAI_BADFLAGS or EAI_ADDRFAMILY with AF_UNSPEC and - // ai_flags as AI_ADDRCONFIG -#ifdef AI_ADDRCONFIG - if ((hints.ai_flags == AI_ADDRCONFIG) && - ((rc ==EAI_BADFLAGS) || (rc == EAI_ADDRFAMILY))) { - //reset ai_flags to null - hints.ai_flags = 0; - //retry getaddrinfo - rc = getaddrinfo(host, port_spec, &hints, &res0); - } -#endif - if (rc != 0) { - errno = getaddrinfo_errno(rc); - LOG_ERROR(("getaddrinfo: %s\n", strerror(errno))); - rc=ZSYSTEMERROR; - goto fail; - } - } - - for (res = res0; res; res = res->ai_next) { - // Expand address list if needed - if (zh->addrs_count == alen) { - void *tmpaddr; - alen += 16; - tmpaddr = realloc(zh->addrs, sizeof(*zh->addrs)*alen); - if (tmpaddr == 0) { - LOG_ERROR(("out of memory")); - errno=ENOMEM; - rc=ZSYSTEMERROR; - goto fail; - } - zh->addrs=tmpaddr; - } - - // Copy addrinfo into address list - addr = &zh->addrs[zh->addrs_count]; - switch (res->ai_family) { - case AF_INET: -#if defined(AF_INET6) - case AF_INET6: -#endif - memcpy(addr, res->ai_addr, res->ai_addrlen); - ++zh->addrs_count; - break; - default: - LOG_WARN(("skipping unknown address family %x for %s", - res->ai_family, zh->hostname)); - break; - } - } - - freeaddrinfo(res0); - - host = strtok_r(0, ",", &strtok_last); - } -#endif - } - free(hosts); - - if(!disable_conn_permute){ - setup_random(); - /* Permute */ - for(i = 0; i < zh->addrs_count; i++) { - struct sockaddr_storage *s1 = zh->addrs + random()%zh->addrs_count; - struct sockaddr_storage *s2 = zh->addrs + random()%zh->addrs_count; - if (s1 != s2) { - struct sockaddr_storage t = *s1; - *s1 = *s2; - *s2 = t; - } - } - } - return ZOK; -fail: - if (zh->addrs) { - free(zh->addrs); - zh->addrs=0; - } - if (hosts) { - free(hosts); - } - return rc; -} - -const clientid_t *zoo_client_id(zhandle_t *zh) -{ - return &zh->client_id; -} - -static void null_watcher_fn(zhandle_t* p1, int p2, int p3,const char* p4,void*p5){} - -watcher_fn zoo_set_watcher(zhandle_t *zh,watcher_fn newFn) -{ - watcher_fn oldWatcher=zh->watcher; - if (newFn) { - zh->watcher = newFn; - } else { - zh->watcher = null_watcher_fn; - } - return oldWatcher; -} - -static void log_env() { - char buf[2048]; -#ifdef HAVE_SYS_UTSNAME_H - struct utsname utsname; -#endif - -#if defined(HAVE_GETUID) && defined(HAVE_GETPWUID_R) - struct passwd pw; - struct passwd *pwp = NULL; - uid_t uid = 0; -#endif - - LOG_INFO(("Client environment:zookeeper.version=%s", PACKAGE_STRING)); - -#ifdef HAVE_GETHOSTNAME - gethostname(buf, sizeof(buf)); - LOG_INFO(("Client environment:host.name=%s", buf)); -#else - LOG_INFO(("Client environment:host.name=")); -#endif - -#ifdef HAVE_SYS_UTSNAME_H - uname(&utsname); - LOG_INFO(("Client environment:os.name=%s", utsname.sysname)); - LOG_INFO(("Client environment:os.arch=%s", utsname.release)); - LOG_INFO(("Client environment:os.version=%s", utsname.version)); -#else - LOG_INFO(("Client environment:os.name=")); - LOG_INFO(("Client environment:os.arch=")); - LOG_INFO(("Client environment:os.version=")); -#endif - -#ifdef HAVE_GETLOGIN - LOG_INFO(("Client environment:user.name=%s", getlogin())); -#else - LOG_INFO(("Client environment:user.name=")); -#endif - -#if defined(HAVE_GETUID) && defined(HAVE_GETPWUID_R) - uid = getuid(); - if (!getpwuid_r(uid, &pw, buf, sizeof(buf), &pwp)) { - LOG_INFO(("Client environment:user.home=%s", pw.pw_dir)); - } else { - LOG_INFO(("Client environment:user.home=")); - } -#else - LOG_INFO(("Client environment:user.home=")); -#endif - -#ifdef HAVE_GETCWD - if (!getcwd(buf, sizeof(buf))) { - LOG_INFO(("Client environment:user.dir=")); - } else { - LOG_INFO(("Client environment:user.dir=%s", buf)); - } -#else - LOG_INFO(("Client environment:user.dir=")); -#endif -} - -/** - * Create a zookeeper handle associated with the given host and port. - */ -zhandle_t *zookeeper_init(const char *host, watcher_fn watcher, - int recv_timeout, const clientid_t *clientid, void *context, int flags) -{ - int errnosave = 0; - zhandle_t *zh = NULL; - char *index_chroot = NULL; - - log_env(); - - LOG_INFO(("Initiating client connection, host=%s sessionTimeout=%d watcher=%p" - " sessionId=%#llx sessionPasswd=%s context=%p flags=%d", - host, - recv_timeout, - watcher, - (clientid == 0 ? 0 : clientid->client_id), - ((clientid == 0) || (clientid->passwd == 0) ? - "" : ""), - context, - flags)); - - zh = calloc(1, sizeof(*zh)); - if (!zh) { - return 0; - } - zh->fd = -1; - zh->state = 0; - zh->context = context; - zh->recv_timeout = recv_timeout; - init_auth_info(&zh->auth_h); - if (watcher) { - zh->watcher = watcher; - } else { - zh->watcher = null_watcher_fn; - } - if (host == 0 || *host == 0) { // what we shouldn't dup - errno=EINVAL; - goto abort; - } - //parse the host to get the chroot if - //available - index_chroot = strchr(host, '/'); - if (index_chroot) { - zh->chroot = strdup(index_chroot); - // if chroot is just / set it to null - if (strlen(zh->chroot) == 1) { - zh->chroot = NULL; - } - // cannot use strndup so allocate and strcpy - zh->hostname = (char *) malloc(index_chroot - host + 1); - zh->hostname = strncpy(zh->hostname, host, (index_chroot - host)); - //strncpy does not null terminate - *(zh->hostname + (index_chroot - host)) = '\0'; - - } else { - zh->chroot = NULL; - zh->hostname = strdup(host); - } - if (zh->chroot && !isValidPath(zh->chroot, 0)) { - errno = EINVAL; - goto abort; - } - if (zh->hostname == 0) { - goto abort; - } - if(getaddrs(zh)!=0) { - goto abort; - } - zh->connect_index = 0; - if (clientid) { - memcpy(&zh->client_id, clientid, sizeof(zh->client_id)); - } else { - memset(&zh->client_id, 0, sizeof(zh->client_id)); - } - zh->primer_buffer.buffer = zh->primer_storage_buffer; - zh->primer_buffer.curr_offset = 0; - zh->primer_buffer.len = sizeof(zh->primer_storage_buffer); - zh->primer_buffer.next = 0; - zh->last_zxid = 0; - zh->next_deadline.tv_sec=zh->next_deadline.tv_usec=0; - zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0; - zh->active_node_watchers=create_zk_hashtable(); - zh->active_exist_watchers=create_zk_hashtable(); - zh->active_child_watchers=create_zk_hashtable(); - - if (adaptor_init(zh) == -1) { - goto abort; - } - - return zh; -abort: - errnosave=errno; - destroy(zh); - free(zh); - errno=errnosave; - return 0; -} - -/** - * deallocated the free_path only its beeen allocated - * and not equal to path - */ -void free_duplicate_path(const char *free_path, const char* path) { - if (free_path != path) { - free((void*)free_path); - } -} - -/** - prepend the chroot path if available else return the path -*/ -static char* prepend_string(zhandle_t *zh, const char* client_path) { - char *ret_str; - if (zh->chroot == NULL) - return (char *) client_path; - // handle the chroot itself, client_path = "/" - if (strlen(client_path) == 1) { - return strdup(zh->chroot); - } - ret_str = (char *) malloc(strlen(zh->chroot) + strlen(client_path) + 1); - strcpy(ret_str, zh->chroot); - return strcat(ret_str, client_path); -} - -/** - strip off the chroot string from the server path - if there is one else return the exact path - */ -char* sub_string(zhandle_t *zh, const char* server_path) { - char *ret_str; - if (zh->chroot == NULL) - return (char *) server_path; - if (strncmp(server_path, zh->chroot, strlen(zh->chroot) != 0)) { - LOG_ERROR(("server path %s does not include chroot path %s", - server_path, zh->chroot)); - return NULL; - } - if (strlen(server_path) == strlen(zh->chroot)) { - //return "/" - ret_str = strdup("/"); - return ret_str; - } - ret_str = strdup(server_path + strlen(zh->chroot)); - return ret_str; -} - -static buffer_list_t *allocate_buffer(char *buff, int len) -{ - buffer_list_t *buffer = calloc(1, sizeof(*buffer)); - if (buffer == 0) - return 0; - - buffer->len = len==0?sizeof(*buffer):len; - buffer->curr_offset = 0; - buffer->buffer = buff; - buffer->next = 0; - return buffer; -} - -static void free_buffer(buffer_list_t *b) -{ - if (!b) { - return; - } - if (b->buffer) { - free(b->buffer); - } - free(b); -} - -static buffer_list_t *dequeue_buffer(buffer_head_t *list) -{ - buffer_list_t *b; - lock_buffer_list(list); - b = list->head; - if (b) { - list->head = b->next; - if (!list->head) { - assert(b == list->last); - list->last = 0; - } - } - unlock_buffer_list(list); - return b; -} - -static int remove_buffer(buffer_head_t *list) -{ - buffer_list_t *b = dequeue_buffer(list); - if (!b) { - return 0; - } - free_buffer(b); - return 1; -} - -static void queue_buffer(buffer_head_t *list, buffer_list_t *b, int add_to_front) -{ - b->next = 0; - lock_buffer_list(list); - if (list->head) { - assert(list->last); - // The list is not empty - if (add_to_front) { - b->next = list->head; - list->head = b; - } else { - list->last->next = b; - list->last = b; - } - }else{ - // The list is empty - assert(!list->head); - list->head = b; - list->last = b; - } - unlock_buffer_list(list); -} - -static int queue_buffer_bytes(buffer_head_t *list, char *buff, int len) -{ - buffer_list_t *b = allocate_buffer(buff,len); - if (!b) - return ZSYSTEMERROR; - queue_buffer(list, b, 0); - return ZOK; -} - -static int queue_front_buffer_bytes(buffer_head_t *list, char *buff, int len) -{ - buffer_list_t *b = allocate_buffer(buff,len); - if (!b) - return ZSYSTEMERROR; - queue_buffer(list, b, 1); - return ZOK; -} - -static __attribute__ ((unused)) int get_queue_len(buffer_head_t *list) -{ - int i; - buffer_list_t *ptr; - lock_buffer_list(list); - ptr = list->head; - for (i=0; ptr!=0; ptr=ptr->next, i++) - ; - unlock_buffer_list(list); - return i; -} -/* returns: - * -1 if send failed, - * 0 if send would block while sending the buffer (or a send was incomplete), - * 1 if success - */ -static int send_buffer(int fd, buffer_list_t *buff) -{ - int len = buff->len; - int off = buff->curr_offset; - int rc = -1; - - if (off < 4) { - /* we need to send the length at the beginning */ - int nlen = htonl(len); - char *b = (char*)&nlen; - rc = send(fd, b + off, sizeof(nlen) - off, 0); - if (rc == -1) { - if (errno != EAGAIN) { - return -1; - } else { - return 0; - } - } else { - buff->curr_offset += rc; - } - off = buff->curr_offset; - } - if (off >= 4) { - /* want off to now represent the offset into the buffer */ - off -= sizeof(buff->len); - rc = send(fd, buff->buffer + off, len - off, 0); - if (rc == -1) { - if (errno != EAGAIN) { - return -1; - } - } else { - buff->curr_offset += rc; - } - } - return buff->curr_offset == len + sizeof(buff->len); -} - -/* returns: - * -1 if recv call failed, - * 0 if recv would block, - * 1 if success - */ -static int recv_buffer(int fd, buffer_list_t *buff) -{ - int off = buff->curr_offset; - int rc = 0; - //fprintf(LOGSTREAM, "rc = %d, off = %d, line %d\n", rc, off, __LINE__); - - /* if buffer is less than 4, we are reading in the length */ - if (off < 4) { - char *buffer = (char*)&(buff->len); - rc = recv(fd, buffer+off, sizeof(int)-off, 0); - //fprintf(LOGSTREAM, "rc = %d, off = %d, line %d\n", rc, off, __LINE__); - switch(rc) { - case 0: - errno = EHOSTDOWN; - case -1: - if (errno == EAGAIN) { - return 0; - } - return -1; - default: - buff->curr_offset += rc; - } - off = buff->curr_offset; - if (buff->curr_offset == sizeof(buff->len)) { - buff->len = ntohl(buff->len); - buff->buffer = calloc(1, buff->len); - } - } - if (buff->buffer) { - /* want off to now represent the offset into the buffer */ - off -= sizeof(buff->len); - - rc = recv(fd, buff->buffer+off, buff->len-off, 0); - switch(rc) { - case 0: - errno = EHOSTDOWN; - case -1: - if (errno == EAGAIN) { - break; - } - return -1; - default: - buff->curr_offset += rc; - } - } - return buff->curr_offset == buff->len + sizeof(buff->len); -} - -void free_buffers(buffer_head_t *list) -{ - while (remove_buffer(list)) - ; -} - -void free_completions(zhandle_t *zh,int callCompletion,int reason) -{ - completion_head_t tmp_list; - struct oarchive *oa; - struct ReplyHeader h; - void_completion_t auth_completion = NULL; - auth_completion_list_t a_list, *a_tmp; - - lock_completion_list(&zh->sent_requests); - tmp_list = zh->sent_requests; - zh->sent_requests.head = 0; - zh->sent_requests.last = 0; - unlock_completion_list(&zh->sent_requests); - while (tmp_list.head) { - completion_list_t *cptr = tmp_list.head; - - tmp_list.head = cptr->next; - if (cptr->c.data_result == SYNCHRONOUS_MARKER) { - struct sync_completion - *sc = (struct sync_completion*)cptr->data; - sc->rc = reason; - notify_sync_completion(sc); - zh->outstanding_sync--; - destroy_completion_entry(cptr); - } else if (callCompletion) { - if(cptr->xid == PING_XID){ - // Nothing to do with a ping response - destroy_completion_entry(cptr); - } else { - // Fake the response - buffer_list_t *bptr; - h.xid = cptr->xid; - h.zxid = -1; - h.err = reason; - oa = create_buffer_oarchive(); - serialize_ReplyHeader(oa, "header", &h); - bptr = calloc(sizeof(*bptr), 1); - assert(bptr); - bptr->len = get_buffer_len(oa); - bptr->buffer = get_buffer(oa); - close_buffer_oarchive(&oa, 0); - cptr->buffer = bptr; - queue_completion(&zh->completions_to_process, cptr, 0); - } - } - } - a_list.completion = NULL; - a_list.next = NULL; - zoo_lock_auth(zh); - get_auth_completions(&zh->auth_h, &a_list); - zoo_unlock_auth(zh); - a_tmp = &a_list; - // chain call user's completion function - while (a_tmp->completion != NULL) { - auth_completion = a_tmp->completion; - auth_completion(reason, a_tmp->auth_data); - a_tmp = a_tmp->next; - if (a_tmp == NULL) - break; - } - free_auth_completion(&a_list); -} - -static void cleanup_bufs(zhandle_t *zh,int callCompletion,int rc) -{ - enter_critical(zh); - free_buffers(&zh->to_send); - free_buffers(&zh->to_process); - free_completions(zh,callCompletion,rc); - leave_critical(zh); - if (zh->input_buffer && zh->input_buffer != &zh->primer_buffer) { - free_buffer(zh->input_buffer); - zh->input_buffer = 0; - } -} - -static void handle_error(zhandle_t *zh,int rc) -{ - close(zh->fd); - if (is_unrecoverable(zh)) { - LOG_DEBUG(("Calling a watcher for a ZOO_SESSION_EVENT and the state=%s", - state2String(zh->state))); - PROCESS_SESSION_EVENT(zh, zh->state); - } else if (zh->state == ZOO_CONNECTED_STATE) { - LOG_DEBUG(("Calling a watcher for a ZOO_SESSION_EVENT and the state=CONNECTING_STATE")); - PROCESS_SESSION_EVENT(zh, ZOO_CONNECTING_STATE); - } - cleanup_bufs(zh,1,rc); - zh->fd = -1; - zh->connect_index++; - if (!is_unrecoverable(zh)) { - zh->state = 0; - } - if (process_async(zh->outstanding_sync)) { - process_completions(zh); - } -} - -static int handle_socket_error_msg(zhandle_t *zh, int line, int rc, - const char* format, ...) -{ - if(logLevel>=ZOO_LOG_LEVEL_ERROR){ - va_list va; - char buf[1024]; - va_start(va,format); - vsnprintf(buf, sizeof(buf)-1,format,va); - log_message(ZOO_LOG_LEVEL_ERROR,line,__func__, - format_log_message("Socket [%s] zk retcode=%d, errno=%d(%s): %s", - format_current_endpoint_info(zh),rc,errno,strerror(errno),buf)); - va_end(va); - } - handle_error(zh,rc); - return rc; -} - -static void auth_completion_func(int rc, zhandle_t* zh) -{ - void_completion_t auth_completion = NULL; - auth_completion_list_t a_list; - auth_completion_list_t *a_tmp; - - if(zh==NULL) - return; - - zoo_lock_auth(zh); - - if(rc!=0){ - zh->state=ZOO_AUTH_FAILED_STATE; - }else{ - //change state for all auths - mark_active_auth(zh); - } - a_list.completion = NULL; - a_list.next = NULL; - get_auth_completions(&zh->auth_h, &a_list); - zoo_unlock_auth(zh); - if (rc) { - LOG_ERROR(("Authentication scheme %s failed. Connection closed.", - zh->auth_h.auth->scheme)); - } - else { - LOG_INFO(("Authentication scheme %s succeeded", zh->auth_h.auth->scheme)); - } - a_tmp = &a_list; - // chain call user's completion function - while (a_tmp->completion != NULL) { - auth_completion = a_tmp->completion; - auth_completion(rc, a_tmp->auth_data); - a_tmp = a_tmp->next; - if (a_tmp == NULL) - break; - } - free_auth_completion(&a_list); -} - -static int send_info_packet(zhandle_t *zh, auth_info* auth) { - struct oarchive *oa; - struct RequestHeader h = { .xid = AUTH_XID, .type = SETAUTH_OP}; - struct AuthPacket req; - int rc; - oa = create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - req.type=0; // ignored by the server - req.scheme = auth->scheme; - req.auth = auth->auth; - rc = rc < 0 ? rc : serialize_AuthPacket(oa, "req", &req); - /* add this buffer to the head of the send queue */ - rc = rc < 0 ? rc : queue_front_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - return rc; -} - -/** send all auths, not just the last one **/ -static int send_auth_info(zhandle_t *zh) { - int rc = 0; - auth_info *auth = NULL; - - zoo_lock_auth(zh); - auth = zh->auth_h.auth; - if (auth == NULL) { - zoo_unlock_auth(zh); - return ZOK; - } - while (auth->next != NULL) { - rc = send_info_packet(zh, auth); - auth = auth->next; - } - zoo_unlock_auth(zh); - LOG_DEBUG(("Sending all auth info request to %s", format_current_endpoint_info(zh))); - return (rc <0) ? ZMARSHALLINGERROR:ZOK; -} - -static int send_last_auth_info(zhandle_t *zh) -{ - int rc = 0; - auth_info *auth = NULL; - - zoo_lock_auth(zh); - auth = get_last_auth(&zh->auth_h); - if(auth==NULL) { - zoo_unlock_auth(zh); - return ZOK; // there is nothing to send - } - rc = send_info_packet(zh, auth); - zoo_unlock_auth(zh); - LOG_DEBUG(("Sending auth info request to %s",format_current_endpoint_info(zh))); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -static void free_key_list(char **list, int count) -{ - int i; - - for(i = 0; i < count; i++) { - free(list[i]); - } - free(list); -} - -static int send_set_watches(zhandle_t *zh) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = SET_WATCHES_XID, .type = SETWATCHES_OP}; - struct SetWatches req; - int rc; - - req.relativeZxid = zh->last_zxid; - req.dataWatches.data = collect_keys(zh->active_node_watchers, (int*)&req.dataWatches.count); - req.existWatches.data = collect_keys(zh->active_exist_watchers, (int*)&req.existWatches.count); - req.childWatches.data = collect_keys(zh->active_child_watchers, (int*)&req.childWatches.count); - - // return if there are no pending watches - if (!req.dataWatches.count && !req.existWatches.count && - !req.childWatches.count) { - free_key_list(req.dataWatches.data, req.dataWatches.count); - free_key_list(req.existWatches.data, req.existWatches.count); - free_key_list(req.childWatches.data, req.childWatches.count); - return ZOK; - } - - - oa = create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_SetWatches(oa, "req", &req); - /* add this buffer to the head of the send queue */ - rc = rc < 0 ? rc : queue_front_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - free_key_list(req.dataWatches.data, req.dataWatches.count); - free_key_list(req.existWatches.data, req.existWatches.count); - free_key_list(req.childWatches.data, req.childWatches.count); - LOG_DEBUG(("Sending set watches request to %s",format_current_endpoint_info(zh))); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -static int serialize_prime_connect(struct connect_req *req, char* buffer){ - //this should be the order of serialization - int offset = 0; - req->protocolVersion = htonl(req->protocolVersion); - memcpy(buffer + offset, &req->protocolVersion, sizeof(req->protocolVersion)); - offset = offset + sizeof(req->protocolVersion); - - req->lastZxidSeen = htonll(req->lastZxidSeen); - memcpy(buffer + offset, &req->lastZxidSeen, sizeof(req->lastZxidSeen)); - offset = offset + sizeof(req->lastZxidSeen); - - req->timeOut = htonl(req->timeOut); - memcpy(buffer + offset, &req->timeOut, sizeof(req->timeOut)); - offset = offset + sizeof(req->timeOut); - - req->sessionId = htonll(req->sessionId); - memcpy(buffer + offset, &req->sessionId, sizeof(req->sessionId)); - offset = offset + sizeof(req->sessionId); - - req->passwd_len = htonl(req->passwd_len); - memcpy(buffer + offset, &req->passwd_len, sizeof(req->passwd_len)); - offset = offset + sizeof(req->passwd_len); - - memcpy(buffer + offset, req->passwd, sizeof(req->passwd)); - - return 0; -} - - static int deserialize_prime_response(struct prime_struct *req, char* buffer){ - //this should be the order of deserialization - int offset = 0; - memcpy(&req->len, buffer + offset, sizeof(req->len)); - offset = offset + sizeof(req->len); - - req->len = ntohl(req->len); - memcpy(&req->protocolVersion, buffer + offset, sizeof(req->protocolVersion)); - offset = offset + sizeof(req->protocolVersion); - - req->protocolVersion = ntohl(req->protocolVersion); - memcpy(&req->timeOut, buffer + offset, sizeof(req->timeOut)); - offset = offset + sizeof(req->timeOut); - - req->timeOut = ntohl(req->timeOut); - memcpy(&req->sessionId, buffer + offset, sizeof(req->sessionId)); - offset = offset + sizeof(req->sessionId); - - req->sessionId = htonll(req->sessionId); - memcpy(&req->passwd_len, buffer + offset, sizeof(req->passwd_len)); - offset = offset + sizeof(req->passwd_len); - - req->passwd_len = ntohl(req->passwd_len); - memcpy(req->passwd, buffer + offset, sizeof(req->passwd)); - return 0; - } - -static int prime_connection(zhandle_t *zh) -{ - int rc; - /*this is the size of buffer to serialize req into*/ - char buffer_req[HANDSHAKE_REQ_SIZE]; - int len = sizeof(buffer_req); - int hlen = 0; - struct connect_req req; - req.protocolVersion = 0; - req.sessionId = zh->client_id.client_id; - req.passwd_len = sizeof(req.passwd); - memcpy(req.passwd, zh->client_id.passwd, sizeof(zh->client_id.passwd)); - req.timeOut = zh->recv_timeout; - req.lastZxidSeen = zh->last_zxid; - hlen = htonl(len); - /* We are running fast and loose here, but this string should fit in the initial buffer! */ - rc=send(zh->fd, &hlen, sizeof(len), 0); - serialize_prime_connect(&req, buffer_req); - rc=rc<0 ? rc : send(zh->fd, buffer_req, len, 0); - if (rc<0) { - return handle_socket_error_msg(zh, __LINE__, ZCONNECTIONLOSS, - "failed to send a handshake packet: %s", strerror(errno)); - } - zh->state = ZOO_ASSOCIATING_STATE; - - zh->input_buffer = &zh->primer_buffer; - /* This seems a bit weird to to set the offset to 4, but we already have a - * length, so we skip reading the length (and allocating the buffer) by - * saying that we are already at offset 4 */ - zh->input_buffer->curr_offset = 4; - - return ZOK; -} - -static inline int calculate_interval(const struct timeval *start, - const struct timeval *end) -{ - int interval; - struct timeval i = *end; - i.tv_sec -= start->tv_sec; - i.tv_usec -= start->tv_usec; - interval = i.tv_sec * 1000 + (i.tv_usec/1000); - return interval; -} - -static struct timeval get_timeval(int interval) -{ - struct timeval tv; - if (interval < 0) { - interval = 0; - } - tv.tv_sec = interval/1000; - tv.tv_usec = (interval%1000)*1000; - return tv; -} - - static int add_void_completion(zhandle_t *zh, int xid, void_completion_t dc, - const void *data); - static int add_string_completion(zhandle_t *zh, int xid, - string_completion_t dc, const void *data); - - int send_ping(zhandle_t* zh) - { - int rc; - struct oarchive *oa = create_buffer_oarchive(); - struct RequestHeader h = { .xid = PING_XID, .type = PING_OP }; - - rc = serialize_RequestHeader(oa, "header", &h); - enter_critical(zh); - gettimeofday(&zh->last_ping, 0); - rc = rc < 0 ? rc : add_void_completion(zh, h.xid, 0, 0); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - close_buffer_oarchive(&oa, 0); - return rc<0 ? rc : adaptor_send_queue(zh, 0); -} - - int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, - struct timeval *tv) -{ - struct timeval now; - if(zh==0 || fd==0 ||interest==0 || tv==0) - return ZBADARGUMENTS; - if (is_unrecoverable(zh)) - return ZINVALIDSTATE; - gettimeofday(&now, 0); - if(zh->next_deadline.tv_sec!=0 || zh->next_deadline.tv_usec!=0){ - int time_left = calculate_interval(&zh->next_deadline, &now); - if (time_left > 10) - LOG_WARN(("Exceeded deadline by %dms", time_left)); - } - api_prolog(zh); - *fd = zh->fd; - *interest = 0; - tv->tv_sec = 0; - tv->tv_usec = 0; - if (*fd == -1) { - if (zh->connect_index == zh->addrs_count) { - /* Wait a bit before trying again so that we don't spin */ - zh->connect_index = 0; - }else { - int rc; - int on = 1; - - zh->fd = socket(zh->addrs[zh->connect_index].ss_family, SOCK_STREAM, 0); - if (zh->fd < 0) { - return api_epilog(zh,handle_socket_error_msg(zh,__LINE__, - ZSYSTEMERROR, "socket() call failed")); - } - setsockopt(zh->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(int)); - fcntl(zh->fd, F_SETFL, O_NONBLOCK|fcntl(zh->fd, F_GETFL, 0)); -#if defined(AF_INET6) - if (zh->addrs[zh->connect_index].ss_family == AF_INET6) { - rc = connect(zh->fd, (struct sockaddr*) &zh->addrs[zh->connect_index], sizeof(struct sockaddr_in6)); - } else { -#else - { -#endif - rc = connect(zh->fd, (struct sockaddr*) &zh->addrs[zh->connect_index], sizeof(struct sockaddr_in)); - } - if (rc == -1) { - /* we are handling the non-blocking connect according to - * the description in section 16.3 "Non-blocking connect" - * in UNIX Network Programming vol 1, 3rd edition */ - if (errno == EWOULDBLOCK || errno == EINPROGRESS) - zh->state = ZOO_CONNECTING_STATE; - else - return api_epilog(zh,handle_socket_error_msg(zh,__LINE__, - ZCONNECTIONLOSS,"connect() call failed")); - } else { - if((rc=prime_connection(zh))!=0) - return api_epilog(zh,rc); - - LOG_INFO(("Initiated connection to server [%s]", - format_endpoint_info(&zh->addrs[zh->connect_index]))); - } - } - *fd = zh->fd; - *tv = get_timeval(zh->recv_timeout/3); - zh->last_recv = now; - zh->last_send = now; - zh->last_ping = now; - } - if (zh->fd != -1) { - int idle_recv = calculate_interval(&zh->last_recv, &now); - int idle_send = calculate_interval(&zh->last_send, &now); - int recv_to = zh->recv_timeout*2/3 - idle_recv; - int send_to = zh->recv_timeout/3; - // have we exceeded the receive timeout threshold? - if (recv_to <= 0) { - // We gotta cut our losses and connect to someone else - errno = ETIMEDOUT; - *fd=-1; - *interest=0; - *tv = get_timeval(0); - return api_epilog(zh,handle_socket_error_msg(zh, - __LINE__,ZOPERATIONTIMEOUT, - "connection timed out (exceeded timeout by %dms)",-recv_to)); - } - // We only allow 1/3 of our timeout time to expire before sending - // a PING - if (zh->state==ZOO_CONNECTED_STATE) { - send_to = zh->recv_timeout/3 - idle_send; - if (send_to <= 0 && zh->sent_requests.head==0) { -// LOG_DEBUG(("Sending PING to %s (exceeded idle by %dms)", -// format_current_endpoint_info(zh),-send_to)); - int rc=send_ping(zh); - if (rc < 0){ - LOG_ERROR(("failed to send PING request (zk retcode=%d)",rc)); - return api_epilog(zh,rc); - } - send_to = zh->recv_timeout/3; - } - } - // choose the lesser value as the timeout - *tv = get_timeval(recv_to < send_to? recv_to:send_to); - zh->next_deadline.tv_sec = now.tv_sec + tv->tv_sec; - zh->next_deadline.tv_usec = now.tv_usec + tv->tv_usec; - if (zh->next_deadline.tv_usec > 1000000) { - zh->next_deadline.tv_sec += zh->next_deadline.tv_usec / 1000000; - zh->next_deadline.tv_usec = zh->next_deadline.tv_usec % 1000000; - } - *interest = ZOOKEEPER_READ; - /* we are interested in a write if we are connected and have something - * to send, or we are waiting for a connect to finish. */ - if ((zh->to_send.head && (zh->state == ZOO_CONNECTED_STATE)) - || zh->state == ZOO_CONNECTING_STATE) { - *interest |= ZOOKEEPER_WRITE; - } - } - return api_epilog(zh,ZOK); -} - -static int check_events(zhandle_t *zh, int events) -{ - if (zh->fd == -1) - return ZINVALIDSTATE; - if ((events&ZOOKEEPER_WRITE)&&(zh->state == ZOO_CONNECTING_STATE)) { - int rc, error; - socklen_t len = sizeof(error); - rc = getsockopt(zh->fd, SOL_SOCKET, SO_ERROR, &error, &len); - /* the description in section 16.4 "Non-blocking connect" - * in UNIX Network Programming vol 1, 3rd edition, points out - * that sometimes the error is in errno and sometimes in error */ - if (rc < 0 || error) { - if (rc == 0) - errno = error; - return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS, - "server refused to accept the client"); - } - if((rc=prime_connection(zh))!=0) - return rc; - LOG_INFO(("initiated connection to server [%s]", - format_endpoint_info(&zh->addrs[zh->connect_index]))); - return ZOK; - } - if (zh->to_send.head && (events&ZOOKEEPER_WRITE)) { - /* make the flush call non-blocking by specifying a 0 timeout */ - int rc=flush_send_queue(zh,0); - if (rc < 0) - return handle_socket_error_msg(zh,__LINE__,ZCONNECTIONLOSS, - "failed while flushing send queue"); - } - if (events&ZOOKEEPER_READ) { - int rc; - if (zh->input_buffer == 0) { - zh->input_buffer = allocate_buffer(0,0); - } - - rc = recv_buffer(zh->fd, zh->input_buffer); - if (rc < 0) { - return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS, - "failed while receiving a server response"); - } - if (rc > 0) { - gettimeofday(&zh->last_recv, 0); - if (zh->input_buffer != &zh->primer_buffer) { - queue_buffer(&zh->to_process, zh->input_buffer, 0); - } else { - int64_t oldid,newid; - //deserialize - deserialize_prime_response(&zh->primer_storage, zh->primer_buffer.buffer); - /* We are processing the primer_buffer, so we need to finish - * the connection handshake */ - oldid = zh->client_id.client_id; - newid = zh->primer_storage.sessionId; - if (oldid != 0 && oldid != newid) { - zh->state = ZOO_EXPIRED_SESSION_STATE; - errno = ESTALE; - return handle_socket_error_msg(zh,__LINE__,ZSESSIONEXPIRED, - "sessionId=%#llx has expired.",oldid); - } else { - zh->recv_timeout = zh->primer_storage.timeOut; - zh->client_id.client_id = newid; - - memcpy(zh->client_id.passwd, &zh->primer_storage.passwd, - sizeof(zh->client_id.passwd)); - zh->state = ZOO_CONNECTED_STATE; - LOG_INFO(("session establishment complete on server [%s], sessionId=%#llx, negotiated timeout=%d", - format_endpoint_info(&zh->addrs[zh->connect_index]), - newid, zh->recv_timeout)); - /* we want the auth to be sent for, but since both call push to front - we need to call send_watch_set first */ - send_set_watches(zh); - /* send the authentication packet now */ - send_auth_info(zh); - LOG_DEBUG(("Calling a watcher for a ZOO_SESSION_EVENT and the state=ZOO_CONNECTED_STATE")); - zh->input_buffer = 0; // just in case the watcher calls zookeeper_process() again - PROCESS_SESSION_EVENT(zh, ZOO_CONNECTED_STATE); - } - } - zh->input_buffer = 0; - } else { - // zookeeper_process was called but there was nothing to read - // from the socket - return ZNOTHING; - } - } - return ZOK; -} - -void api_prolog(zhandle_t* zh) -{ - inc_ref_counter(zh,1); -} - -int api_epilog(zhandle_t *zh,int rc) -{ - if(inc_ref_counter(zh,-1)==0 && zh->close_requested!=0) - zookeeper_close(zh); - return rc; -} - -static __attribute__((unused)) void print_completion_queue(zhandle_t *zh) -{ - completion_list_t* cptr; - - if(logLevelsent_requests.head==0) { - fprintf(LOGSTREAM,"empty\n"); - return; - } - - cptr=zh->sent_requests.head; - while(cptr){ - fprintf(LOGSTREAM,"%d,",cptr->xid); - cptr=cptr->next; - } - fprintf(LOGSTREAM,"end\n"); -} - -//#ifdef THREADED -// IO thread queues session events to be processed by the completion thread -static int queue_session_event(zhandle_t *zh, int state) -{ - int rc; - struct WatcherEvent evt = { ZOO_SESSION_EVENT, state, "" }; - struct ReplyHeader hdr = { WATCHER_EVENT_XID, 0, 0 }; - struct oarchive *oa; - completion_list_t *cptr; - - if ((oa=create_buffer_oarchive())==NULL) { - LOG_ERROR(("out of memory")); - goto error; - } - rc = serialize_ReplyHeader(oa, "hdr", &hdr); - rc = rc<0?rc: serialize_WatcherEvent(oa, "event", &evt); - if(rc<0){ - close_buffer_oarchive(&oa, 1); - goto error; - } - cptr = create_completion_entry(WATCHER_EVENT_XID,-1,0,0,0); - cptr->buffer = allocate_buffer(get_buffer(oa), get_buffer_len(oa)); - cptr->buffer->curr_offset = get_buffer_len(oa); - if (!cptr->buffer) { - free(cptr); - close_buffer_oarchive(&oa, 1); - goto error; - } - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - cptr->c.watcher_result = collectWatchers(zh, ZOO_SESSION_EVENT, ""); - queue_completion(&zh->completions_to_process, cptr, 0); - if (process_async(zh->outstanding_sync)) { - process_completions(zh); - } - return ZOK; -error: - errno=ENOMEM; - return ZSYSTEMERROR; -} -//#endif - -completion_list_t *dequeue_completion(completion_head_t *list) -{ - completion_list_t *cptr; - lock_completion_list(list); - cptr = list->head; - if (cptr) { - list->head = cptr->next; - if (!list->head) { - assert(list->last == cptr); - list->last = 0; - } - } - unlock_completion_list(list); - return cptr; -} - - -/* handles async completion (both single- and multithreaded) */ -void process_completions(zhandle_t *zh) -{ - completion_list_t *cptr; - while ((cptr = dequeue_completion(&zh->completions_to_process)) != 0) { - struct ReplyHeader hdr; - buffer_list_t *bptr = cptr->buffer; - struct iarchive *ia = create_buffer_iarchive(bptr->buffer, - bptr->len); - deserialize_ReplyHeader(ia, "hdr", &hdr); - - if (hdr.xid == WATCHER_EVENT_XID) { - int type, state; - struct WatcherEvent evt; - deserialize_WatcherEvent(ia, "event", &evt); - /* We are doing a notification, so there is no pending request */ - type = evt.type; - state = evt.state; - /* This is a notification so there aren't any pending requests */ - LOG_DEBUG(("Calling a watcher for node [%s], type = %d event=%s", - (evt.path==NULL?"NULL":evt.path), cptr->completion_type, - watcherEvent2String(type))); - deliverWatchers(zh,type,state,evt.path, &cptr->c.watcher_result); - deallocate_WatcherEvent(&evt); - } else { - int rc = hdr.err; - switch (cptr->completion_type) { - case COMPLETION_DATA: - LOG_DEBUG(("Calling COMPLETION_DATA for xid=%#x rc=%d",cptr->xid,rc)); - if (rc) { - cptr->c.data_result(rc, 0, 0, 0, cptr->data); - } else { - struct GetDataResponse res; - deserialize_GetDataResponse(ia, "reply", &res); - cptr->c.data_result(rc, res.data.buff, res.data.len, - &res.stat, cptr->data); - deallocate_GetDataResponse(&res); - } - break; - case COMPLETION_STAT: - LOG_DEBUG(("Calling COMPLETION_STAT for xid=%#x rc=%d",cptr->xid,rc)); - if (rc) { - cptr->c.stat_result(rc, 0, cptr->data); - } else { - struct SetDataResponse res; - deserialize_SetDataResponse(ia, "reply", &res); - cptr->c.stat_result(rc, &res.stat, cptr->data); - deallocate_SetDataResponse(&res); - } - break; - case COMPLETION_STRINGLIST: - LOG_DEBUG(("Calling COMPLETION_STRINGLIST for xid=%#x rc=%d",cptr->xid,rc)); - if (rc) { - cptr->c.strings_result(rc, 0, cptr->data); - } else { - struct GetChildrenResponse res; - deserialize_GetChildrenResponse(ia, "reply", &res); - cptr->c.strings_result(rc, &res.children, cptr->data); - deallocate_GetChildrenResponse(&res); - } - break; - case COMPLETION_STRINGLIST_STAT: - LOG_DEBUG(("Calling COMPLETION_STRINGLIST_STAT for xid=%#x rc=%d",cptr->xid,rc)); - if (rc) { - cptr->c.strings_stat_result(rc, 0, 0, cptr->data); - } else { - struct GetChildren2Response res; - deserialize_GetChildren2Response(ia, "reply", &res); - cptr->c.strings_stat_result(rc, &res.children, &res.stat, cptr->data); - deallocate_GetChildren2Response(&res); - } - break; - case COMPLETION_STRING: - LOG_DEBUG(("Calling COMPLETION_STRING for xid=%#x rc=%d",cptr->xid,rc)); - if (rc) { - cptr->c.string_result(rc, 0, cptr->data); - } else { - struct CreateResponse res; - deserialize_CreateResponse(ia, "reply", &res); - cptr->c.string_result(rc, res.path, cptr->data); - deallocate_CreateResponse(&res); - } - break; - case COMPLETION_ACLLIST: - LOG_DEBUG(("Calling COMPLETION_ACLLIST for xid=%#x rc=%d",cptr->xid,rc)); - if (rc) { - cptr->c.acl_result(rc, 0, 0, cptr->data); - } else { - struct GetACLResponse res; - deserialize_GetACLResponse(ia, "reply", &res); - cptr->c.acl_result(rc, &res.acl, &res.stat, cptr->data); - deallocate_GetACLResponse(&res); - } - break; - case COMPLETION_VOID: - LOG_DEBUG(("Calling COMPLETION_VOID for xid=%#x rc=%d",cptr->xid,rc)); - if (hdr.xid == PING_XID) { - // We want to skip the ping - } else { - cptr->c.void_result(rc, cptr->data); - } - break; - } - } - destroy_completion_entry(cptr); - close_buffer_iarchive(&ia); - } -} - -static void isSocketReadable(zhandle_t* zh) -{ - struct pollfd fds; - fds.fd = zh->fd; - fds.events = POLLIN; - if (poll(&fds,1,0)<=0) { - // socket not readable -- no more responses to process - zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0; - }else{ - gettimeofday(&zh->socket_readable,0); - } -} - -static void checkResponseLatency(zhandle_t* zh) -{ - int delay; - struct timeval now; - - if(zh->socket_readable.tv_sec==0) - return; - - gettimeofday(&now,0); - delay=calculate_interval(&zh->socket_readable, &now); - if(delay>20) - LOG_DEBUG(("The following server response has spent at least %dms sitting in the client socket recv buffer",delay)); - - zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0; -} - -int zookeeper_process(zhandle_t *zh, int events) -{ - buffer_list_t *bptr; - int rc; - - if (zh==NULL) - return ZBADARGUMENTS; - if (is_unrecoverable(zh)) - return ZINVALIDSTATE; - api_prolog(zh); - IF_DEBUG(checkResponseLatency(zh)); - rc = check_events(zh, events); - if (rc!=ZOK) - return api_epilog(zh, rc); - - IF_DEBUG(isSocketReadable(zh)); - - while (rc >= 0 && (bptr=dequeue_buffer(&zh->to_process))) { - struct ReplyHeader hdr; - struct iarchive *ia = create_buffer_iarchive( - bptr->buffer, bptr->curr_offset); - deserialize_ReplyHeader(ia, "hdr", &hdr); - if (hdr.zxid > 0) { - zh->last_zxid = hdr.zxid; - } else { - // fprintf(stderr, "Got %#x for %#x\n", hdr.zxid, hdr.xid); - } - - if (hdr.xid == WATCHER_EVENT_XID) { - struct WatcherEvent evt; - int type = 0; - char *path = NULL; - completion_list_t *c = NULL; - - LOG_DEBUG(("Processing WATCHER_EVENT")); - - deserialize_WatcherEvent(ia, "event", &evt); - type = evt.type; - path = evt.path; - /* We are doing a notification, so there is no pending request */ - c = create_completion_entry(WATCHER_EVENT_XID,-1,0,0,0); - c->buffer = bptr; - c->c.watcher_result = collectWatchers(zh, type, path); - - // We cannot free until now, otherwise path will become invalid - deallocate_WatcherEvent(&evt); - queue_completion(&zh->completions_to_process, c, 0); - } else if (hdr.xid == SET_WATCHES_XID) { - LOG_DEBUG(("Processing SET_WATCHES")); - free_buffer(bptr); - } else if (hdr.xid == AUTH_XID){ - LOG_DEBUG(("Processing AUTH_XID")); - - /* special handling for the AUTH response as it may come back - * out-of-band */ - auth_completion_func(hdr.err,zh); - free_buffer(bptr); - /* authentication completion may change the connection state to - * unrecoverable */ - if(is_unrecoverable(zh)){ - handle_error(zh, ZAUTHFAILED); - close_buffer_iarchive(&ia); - return api_epilog(zh, ZAUTHFAILED); - } - } else { - int rc = hdr.err; - /* Find the request corresponding to the response */ - completion_list_t *cptr = dequeue_completion(&zh->sent_requests); - - /* [ZOOKEEPER-804] Don't assert if zookeeper_close has been called. */ - if (zh->close_requested == 1) { - if (cptr) { - destroy_completion_entry(cptr); - cptr = NULL; - } - return ZINVALIDSTATE; - } - assert(cptr); - /* The requests are going to come back in order */ - if (cptr->xid != hdr.xid) { - LOG_DEBUG(("Processing unexpected or out-of-order response!")); - - // received unexpected (or out-of-order) response - close_buffer_iarchive(&ia); - free_buffer(bptr); - // put the completion back on the queue (so it gets properly - // signaled and deallocated) and disconnect from the server - queue_completion(&zh->sent_requests,cptr,1); - return handle_socket_error_msg(zh, __LINE__,ZRUNTIMEINCONSISTENCY, - "unexpected server response: expected %#x, but received %#x", - hdr.xid,cptr->xid); - } - - activateWatcher(zh, cptr->watcher, rc); - - if (cptr->c.void_result != SYNCHRONOUS_MARKER) { - if(hdr.xid == PING_XID){ - int elapsed = 0; - struct timeval now; - gettimeofday(&now, 0); - elapsed = calculate_interval(&zh->last_ping, &now); - LOG_DEBUG(("Got ping response in %d ms", elapsed)); - - // Nothing to do with a ping response - free_buffer(bptr); - destroy_completion_entry(cptr); - } else { - LOG_DEBUG(("Queueing asynchronous response")); - - cptr->buffer = bptr; - queue_completion(&zh->completions_to_process, cptr, 0); - } - } else { - struct sync_completion - *sc = (struct sync_completion*)cptr->data; - sc->rc = rc; - switch(cptr->completion_type) { - case COMPLETION_DATA: - LOG_DEBUG(("Calling COMPLETION_DATA for xid=%#x rc=%d", - cptr->xid, rc)); - if (rc==0) { - struct GetDataResponse res; - int len; - deserialize_GetDataResponse(ia, "reply", &res); - if (res.data.len <= sc->u.data.buff_len) { - len = res.data.len; - } else { - len = sc->u.data.buff_len; - } - sc->u.data.buff_len = len; - // check if len is negative - // just of NULL which is -1 int - if (len == -1) { - sc->u.data.buffer = NULL; - } else { - memcpy(sc->u.data.buffer, res.data.buff, len); - } - sc->u.data.stat = res.stat; - deallocate_GetDataResponse(&res); - } - break; - case COMPLETION_STAT: - LOG_DEBUG(("Calling COMPLETION_STAT for xid=%#x rc=%d", - cptr->xid, rc)); - if (rc == 0) { - struct SetDataResponse res; - deserialize_SetDataResponse(ia, "reply", &res); - sc->u.stat = res.stat; - deallocate_SetDataResponse(&res); - } - break; - case COMPLETION_STRINGLIST: - LOG_DEBUG(("Calling COMPLETION_STRINGLIST for xid=%#x rc=%d", - cptr->xid, rc)); - if (rc == 0) { - struct GetChildrenResponse res; - deserialize_GetChildrenResponse(ia, "reply", &res); - sc->u.strs2 = res.children; - /* We don't deallocate since we are passing it back */ - // deallocate_GetChildrenResponse(&res); - } - break; - case COMPLETION_STRINGLIST_STAT: - LOG_DEBUG(("Calling COMPLETION_STRINGLIST_STAT for xid=%#x rc=%d", - cptr->xid, rc)); - if (rc == 0) { - struct GetChildren2Response res; - deserialize_GetChildren2Response(ia, "reply", &res); - sc->u.strs_stat.strs2 = res.children; - sc->u.strs_stat.stat2 = res.stat; - /* We don't deallocate since we are passing it back */ - // deallocate_GetChildren2Response(&res); - } - break; - case COMPLETION_STRING: - LOG_DEBUG(("Calling COMPLETION_STRING for xid=%#x rc=%d", - cptr->xid, rc)); - if (rc == 0) { - struct CreateResponse res; - int len; - deserialize_CreateResponse(ia, "reply", &res); - len = strlen(res.path) + 1; - if (len > sc->u.str.str_len) { - len = sc->u.str.str_len; - } - if (len > 0) { - memcpy(sc->u.str.str, res.path, len - 1); - sc->u.str.str[len - 1] = '\0'; - } - deallocate_CreateResponse(&res); - } - break; - case COMPLETION_ACLLIST: - LOG_DEBUG(("Calling COMPLETION_ACLLIST for xid=%#x rc=%d", - cptr->xid, rc)); - if (rc == 0) { - struct GetACLResponse res; - deserialize_GetACLResponse(ia, "reply", &res); - sc->u.acl.acl = res.acl; - sc->u.acl.stat = res.stat; - /* We don't deallocate since we are passing it back */ - //deallocate_GetACLResponse(&res); - } - break; - case COMPLETION_VOID: - LOG_DEBUG(("Calling COMPLETION_VOID for xid=%#x rc=%d", - cptr->xid, rc)); - break; - default: - LOG_DEBUG(("UNKNOWN response type xid=%#x rc=%d", - cptr->xid, rc)); - break; - } - notify_sync_completion(sc); - free_buffer(bptr); - zh->outstanding_sync--; - destroy_completion_entry(cptr); - } - } - - close_buffer_iarchive(&ia); - - } - if (process_async(zh->outstanding_sync)) { - process_completions(zh); - } - return api_epilog(zh,ZOK);} - -int zoo_state(zhandle_t *zh) -{ - if(zh!=0) - return zh->state; - return 0; -} - -static watcher_registration_t* create_watcher_registration(const char* path, - result_checker_fn checker,watcher_fn watcher,void* ctx){ - watcher_registration_t* wo; - if(watcher==0) - return 0; - wo=calloc(1,sizeof(watcher_registration_t)); - wo->path=strdup(path); - wo->watcher=watcher; - wo->context=ctx; - wo->checker=checker; - return wo; -} - -static void destroy_watcher_registration(watcher_registration_t* wo){ - if(wo!=0){ - free((void*)wo->path); - free(wo); - } -} - -static completion_list_t* create_completion_entry(int xid, int completion_type, - const void *dc, const void *data,watcher_registration_t* wo) -{ - completion_list_t *c = calloc(1,sizeof(completion_list_t)); - if (!c) { - LOG_ERROR(("out of memory")); - return 0; - } - c->completion_type = completion_type; - c->data = data; - switch(c->completion_type) { - case COMPLETION_VOID: - c->c.void_result = (void_completion_t)dc; - break; - case COMPLETION_STRING: - c->c.string_result = (string_completion_t)dc; - break; - case COMPLETION_DATA: - c->c.data_result = (data_completion_t)dc; - break; - case COMPLETION_STAT: - c->c.stat_result = (stat_completion_t)dc; - break; - case COMPLETION_STRINGLIST: - c->c.strings_result = (strings_completion_t)dc; - break; - case COMPLETION_STRINGLIST_STAT: - c->c.strings_stat_result = (strings_stat_completion_t)dc; - break; - case COMPLETION_ACLLIST: - c->c.acl_result = (acl_completion_t)dc; - break; - } - c->xid = xid; - c->watcher = wo; - - return c; -} - -static void destroy_completion_entry(completion_list_t* c){ - if(c!=0){ - destroy_watcher_registration(c->watcher); - if(c->buffer!=0) - free_buffer(c->buffer); - free(c); - } -} - -static void queue_completion_nolock(completion_head_t *list, - completion_list_t *c, - int add_to_front) -{ - c->next = 0; - /* appending a new entry to the back of the list */ - if (list->last) { - assert(list->head); - // List is not empty - if (!add_to_front) { - list->last->next = c; - list->last = c; - } else { - c->next = list->head; - list->head = c; - } - } else { - // List is empty - assert(!list->head); - list->head = c; - list->last = c; - } -} - -static void queue_completion(completion_head_t *list, completion_list_t *c, - int add_to_front) -{ - - lock_completion_list(list); - queue_completion_nolock(list, c, add_to_front); - unlock_completion_list(list); -} - -static int add_completion(zhandle_t *zh, int xid, int completion_type, - const void *dc, const void *data, int add_to_front, - watcher_registration_t* wo) -{ - completion_list_t *c =create_completion_entry(xid, completion_type, dc, - data,wo); - int rc = 0; - if (!c) - return ZSYSTEMERROR; - lock_completion_list(&zh->sent_requests); - if (zh->close_requested != 1) { - queue_completion_nolock(&zh->sent_requests, c, add_to_front); - rc = ZOK; - } else { - rc = ZINVALIDSTATE; - } - unlock_completion_list(&zh->sent_requests); - if (dc == SYNCHRONOUS_MARKER) { - zh->outstanding_sync++; - } - return rc; -} - -static int add_data_completion(zhandle_t *zh, int xid, data_completion_t dc, - const void *data,watcher_registration_t* wo) -{ - return add_completion(zh, xid, COMPLETION_DATA, dc, data, 0,wo); -} - -static int add_stat_completion(zhandle_t *zh, int xid, stat_completion_t dc, - const void *data,watcher_registration_t* wo) -{ - return add_completion(zh, xid, COMPLETION_STAT, dc, data, 0,wo); -} - -static int add_strings_completion(zhandle_t *zh, int xid, - strings_completion_t dc, const void *data,watcher_registration_t* wo) -{ - return add_completion(zh, xid, COMPLETION_STRINGLIST, dc, data, 0,wo); -} - -static int add_strings_stat_completion(zhandle_t *zh, int xid, - strings_stat_completion_t dc, const void *data,watcher_registration_t* wo) -{ - return add_completion(zh, xid, COMPLETION_STRINGLIST_STAT, dc, data, 0,wo); -} - -static int add_acl_completion(zhandle_t *zh, int xid, acl_completion_t dc, - const void *data) -{ - return add_completion(zh, xid, COMPLETION_ACLLIST, dc, data, 0,0); -} - -static int add_void_completion(zhandle_t *zh, int xid, void_completion_t dc, - const void *data) -{ - return add_completion(zh, xid, COMPLETION_VOID, dc, data, 0,0); -} - -static int add_string_completion(zhandle_t *zh, int xid, - string_completion_t dc, const void *data) -{ - return add_completion(zh, xid, COMPLETION_STRING, dc, data, 0,0); -} - -int zookeeper_close(zhandle_t *zh) -{ - int rc=ZOK; - if (zh==0) - return ZBADARGUMENTS; - - zh->close_requested=1; - if (inc_ref_counter(zh,0)!=0) { - cleanup_bufs(zh, 1, ZCLOSING); - adaptor_finish(zh); - return ZOK; - } - if(zh->state==ZOO_CONNECTED_STATE){ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = CLOSE_OP}; - LOG_INFO(("Closing zookeeper sessionId=%#llx to [%s]\n", - zh->client_id.client_id,format_current_endpoint_info(zh))); - oa = create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - if (rc < 0) { - rc = ZMARSHALLINGERROR; - goto finish; - } - - /* make sure the close request is sent; we set timeout to an arbitrary - * (but reasonable) number of milliseconds since we want the call to block*/ - rc=adaptor_send_queue(zh, 3000); - }else{ - LOG_INFO(("Freeing zookeeper resources for sessionId=%#llx\n", - zh->client_id.client_id)); - rc = ZOK; - } - -finish: - destroy(zh); - adaptor_destroy(zh); - free(zh); - return rc; -} - -static int isValidPath(const char* path, const int flags) { - int len = 0; - char lastc = '/'; - char c; - int i = 0; - - if (path == 0) - return 0; - len = strlen(path); - if (len == 0) - return 0; - if (path[0] != '/') - return 0; - if (len == 1) // done checking - it's the root - return 1; - if (path[len - 1] == '/' && !(flags & ZOO_SEQUENCE)) - return 0; - - i = 1; - for (; i < len; lastc = path[i], i++) { - c = path[i]; - - if (c == 0) { - return 0; - } else if (c == '/' && lastc == '/') { - return 0; - } else if (c == '.' && lastc == '.') { - if (path[i-2] == '/' && (((i + 1 == len) && !(flags & ZOO_SEQUENCE)) - || path[i+1] == '/')) { - return 0; - } - } else if (c == '.') { - if ((path[i-1] == '/') && (((i + 1 == len) && !(flags & ZOO_SEQUENCE)) - || path[i+1] == '/')) { - return 0; - } - } else if (c > 0x00 && c < 0x1f) { - return 0; - } - } - - return 1; -} - -int zoo_aget(zhandle_t *zh, const char *path, int watch, data_completion_t dc, - const void *data) -{ - return zoo_awget(zh,path,watch?zh->watcher:0,zh->context,dc,data); -} - -int zoo_awget(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - data_completion_t dc, const void *data) -{ - struct oarchive *oa; - char *server_path = prepend_string(zh, path); - struct RequestHeader h = { .xid = get_xid(), .type = GETDATA_OP}; - struct GetDataRequest req = { (char*)server_path, watcher!=0 }; - int rc; - - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa=create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_GetDataRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_data_completion(zh, h.xid, dc, data, - create_watcher_registration(server_path,data_result_checker,watcher,watcherCtx)); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_aset(zhandle_t *zh, const char *path, const char *buffer, int buflen, - int version, stat_completion_t dc, const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = SETDATA_OP}; - struct SetDataRequest req; - int rc; - char *server_path; - server_path = prepend_string(zh, path); - - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - req.path = (char*)server_path; - req.data.buff = (char*)buffer; - req.data.len = buflen; - req.version = version; - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_SetDataRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_stat_completion(zh, h.xid, dc, data,0); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_acreate(zhandle_t *zh, const char *path, const char *value, - int valuelen, const struct ACL_vector *acl_entries, int flags, - string_completion_t completion, const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = CREATE_OP }; - struct CreateRequest req; - int rc; - char *server_path; - - server_path = prepend_string(zh, path); - if (zh==0 || !isValidPath(server_path, flags)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - req.path = (char*)server_path; - req.flags = flags; - req.data.buff = (char*)value; - req.data.len = valuelen; - if (acl_entries == 0) { - req.acl.count = 0; - req.acl.data = 0; - } else { - req.acl = *acl_entries; - } - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_string_completion(zh, h.xid, completion, data); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_adelete(zhandle_t *zh, const char *path, int version, - void_completion_t completion, const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = DELETE_OP}; - struct DeleteRequest req; - int rc; - char *server_path; - - server_path = prepend_string(zh, path); - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - req.path = (char*)server_path; - req.version = version; - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_DeleteRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_void_completion(zh, h.xid, completion, data); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_aexists(zhandle_t *zh, const char *path, int watch, - stat_completion_t sc, const void *data) -{ - return zoo_awexists(zh,path,watch?zh->watcher:0,zh->context,sc,data); -} - -int zoo_awexists(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - stat_completion_t completion, const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = EXISTS_OP }; - char *server_path = prepend_string(zh, path); - struct ExistsRequest req = {(char*)server_path, watcher!=0 }; - int rc; - - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_ExistsRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_stat_completion(zh, h.xid, completion, data, - create_watcher_registration(server_path,exists_result_checker, - watcher,watcherCtx)); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -static int zoo_awget_children_(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - strings_completion_t sc, - const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = GETCHILDREN_OP}; - char * server_path = prepend_string(zh, path); - struct GetChildrenRequest req = {(char*)server_path, watcher!=0 }; - int rc; - - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_GetChildrenRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_strings_completion(zh, h.xid, sc, data, - create_watcher_registration(server_path,child_result_checker,watcher,watcherCtx)); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_aget_children(zhandle_t *zh, const char *path, int watch, - strings_completion_t dc, const void *data) -{ - return zoo_awget_children_(zh,path,watch?zh->watcher:0,zh->context,dc,data); -} - -int zoo_awget_children(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - strings_completion_t dc, - const void *data) -{ - return zoo_awget_children_(zh,path,watcher,watcherCtx,dc,data); -} - -static int zoo_awget_children2_(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - strings_stat_completion_t ssc, - const void *data) -{ - /* invariant: (sc == NULL) != (sc == NULL) */ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = GETCHILDREN2_OP}; - char * server_path = prepend_string(zh, path); - struct GetChildren2Request req2 = {(char*)server_path, watcher!=0 }; - int rc; - - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_GetChildren2Request(oa, "req", &req2); - enter_critical(zh); - rc = rc < 0 ? rc : add_strings_stat_completion(zh, h.xid, ssc, data, - create_watcher_registration(server_path,child_result_checker,watcher,watcherCtx)); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_aget_children2(zhandle_t *zh, const char *path, int watch, - strings_stat_completion_t dc, const void *data) -{ - return zoo_awget_children2_(zh,path,watch?zh->watcher:0,zh->context,dc,data); -} - -int zoo_awget_children2(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - strings_stat_completion_t dc, - const void *data) -{ - return zoo_awget_children2_(zh,path,watcher,watcherCtx,dc,data); -} - -int zoo_async(zhandle_t *zh, const char *path, - string_completion_t completion, const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = SYNC_OP}; - struct SyncRequest req; - int rc; - char *server_path; - - server_path = prepend_string(zh, path); - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - req.path = (char*)server_path; - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_SyncRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_string_completion(zh, h.xid, completion, data); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - - -int zoo_aget_acl(zhandle_t *zh, const char *path, acl_completion_t completion, - const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = GETACL_OP}; - struct GetACLRequest req; - int rc; - char *server_path; - - server_path = prepend_string(zh, path); - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - req.path = (char*)server_path; - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_GetACLRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_acl_completion(zh, h.xid, completion, data); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -int zoo_aset_acl(zhandle_t *zh, const char *path, int version, - struct ACL_vector *acl, void_completion_t completion, const void *data) -{ - struct oarchive *oa; - struct RequestHeader h = { .xid = get_xid(), .type = SETACL_OP}; - struct SetACLRequest req; - int rc; - char *server_path; - - server_path = prepend_string(zh, path); - if (zh==0 || !isValidPath(server_path, 0)) { - free_duplicate_path(server_path, path); - return ZBADARGUMENTS; - } - if (is_unrecoverable(zh)) { - free_duplicate_path(server_path, path); - return ZINVALIDSTATE; - } - oa = create_buffer_oarchive(); - req.path = (char*)server_path; - req.acl = *acl; - req.version = version; - rc = serialize_RequestHeader(oa, "header", &h); - rc = rc < 0 ? rc : serialize_SetACLRequest(oa, "req", &req); - enter_critical(zh); - rc = rc < 0 ? rc : add_void_completion(zh, h.xid, completion, data); - rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), - get_buffer_len(oa)); - leave_critical(zh); - free_duplicate_path(server_path, path); - /* We queued the buffer, so don't free it */ - close_buffer_oarchive(&oa, 0); - - LOG_DEBUG(("Sending request xid=%#x for path [%s] to %s",h.xid,path, - format_current_endpoint_info(zh))); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; -} - -/* specify timeout of 0 to make the function non-blocking */ -/* timeout is in milliseconds */ -int flush_send_queue(zhandle_t*zh, int timeout) -{ - int rc= ZOK; - struct timeval started; - gettimeofday(&started,0); - // we can't use dequeue_buffer() here because if (non-blocking) send_buffer() - // returns EWOULDBLOCK we'd have to put the buffer back on the queue. - // we use a recursive lock instead and only dequeue the buffer if a send was - // successful - lock_buffer_list(&zh->to_send); - while (zh->to_send.head != 0&& zh->state == ZOO_CONNECTED_STATE) { - if(timeout!=0){ - int elapsed; - struct pollfd fds; - struct timeval now; - gettimeofday(&now,0); - elapsed=calculate_interval(&started,&now); - if (elapsed>timeout) { - rc = ZOPERATIONTIMEOUT; - break; - } - fds.fd = zh->fd; - fds.events = POLLOUT; - fds.revents = 0; - rc = poll(&fds, 1, timeout-elapsed); - if (rc<=0) { - /* timed out or an error or POLLERR */ - rc = rc==0 ? ZOPERATIONTIMEOUT : ZSYSTEMERROR; - break; - } - } - - rc = send_buffer(zh->fd, zh->to_send.head); - if(rc==0 && timeout==0){ - /* send_buffer would block while sending this buffer */ - rc = ZOK; - break; - } - if (rc < 0) { - rc = ZCONNECTIONLOSS; - break; - } - // if the buffer has been sent successfully, remove it from the queue - if (rc > 0) - remove_buffer(&zh->to_send); - gettimeofday(&zh->last_send, 0); - rc = ZOK; - } - unlock_buffer_list(&zh->to_send); - return rc; -} - -const char* zerror(int c) -{ - switch (c){ - case ZOK: - return "ok"; - case ZSYSTEMERROR: - return "system error"; - case ZRUNTIMEINCONSISTENCY: - return "run time inconsistency"; - case ZDATAINCONSISTENCY: - return "data inconsistency"; - case ZCONNECTIONLOSS: - return "connection loss"; - case ZMARSHALLINGERROR: - return "marshalling error"; - case ZUNIMPLEMENTED: - return "unimplemented"; - case ZOPERATIONTIMEOUT: - return "operation timeout"; - case ZBADARGUMENTS: - return "bad arguments"; - case ZINVALIDSTATE: - return "invalid zhandle state"; - case ZAPIERROR: - return "api error"; - case ZNONODE: - return "no node"; - case ZNOAUTH: - return "not authenticated"; - case ZBADVERSION: - return "bad version"; - case ZNOCHILDRENFOREPHEMERALS: - return "no children for ephemerals"; - case ZNODEEXISTS: - return "node exists"; - case ZNOTEMPTY: - return "not empty"; - case ZSESSIONEXPIRED: - return "session expired"; - case ZINVALIDCALLBACK: - return "invalid callback"; - case ZINVALIDACL: - return "invalid acl"; - case ZAUTHFAILED: - return "authentication failed"; - case ZCLOSING: - return "zookeeper is closing"; - case ZNOTHING: - return "(not error) no server responses to process"; - case ZSESSIONMOVED: - return "session moved to another server, so operation is ignored"; - } - if (c > 0) { - return strerror(c); - } - return "unknown error"; -} - -int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert, - int certLen,void_completion_t completion, const void *data) -{ - struct buffer auth; - auth_info *authinfo; - if(scheme==NULL || zh==NULL) - return ZBADARGUMENTS; - - if (is_unrecoverable(zh)) - return ZINVALIDSTATE; - - if(cert!=NULL && certLen!=0){ - auth.buff=calloc(1,certLen); - if(auth.buff==0) { - return ZSYSTEMERROR; - } - memcpy(auth.buff,cert,certLen); - auth.len=certLen; - } else { - auth.buff = 0; - auth.len = 0; - } - - zoo_lock_auth(zh); - authinfo = (auth_info*) malloc(sizeof(auth_info)); - authinfo->scheme=strdup(scheme); - authinfo->auth=auth; - authinfo->completion=completion; - authinfo->data=data; - authinfo->next = NULL; - add_last_auth(&zh->auth_h, authinfo); - zoo_unlock_auth(zh); - - if(zh->state == ZOO_CONNECTED_STATE || zh->state == ZOO_ASSOCIATING_STATE) - return send_last_auth_info(zh); - - return ZOK; -} - -static const char* format_endpoint_info(const struct sockaddr_storage* ep) -{ - static char buf[128]; - char addrstr[128]; - void *inaddr; - int port; - if(ep==0) - return "null"; - -#if defined(AF_INET6) - if(ep->ss_family==AF_INET6){ - inaddr=&((struct sockaddr_in6*)ep)->sin6_addr; - port=((struct sockaddr_in6*)ep)->sin6_port; - } else { -#endif - inaddr=&((struct sockaddr_in*)ep)->sin_addr; - port=((struct sockaddr_in*)ep)->sin_port; -#if defined(AF_INET6) - } -#endif - inet_ntop(ep->ss_family,inaddr,addrstr,sizeof(addrstr)-1); - sprintf(buf,"%s:%d",addrstr,ntohs(port)); - return buf; -} - -static const char* format_current_endpoint_info(zhandle_t* zh) -{ - return format_endpoint_info(&zh->addrs[zh->connect_index]); -} - -void zoo_deterministic_conn_order(int yesOrNo) -{ - disable_conn_permute=yesOrNo; -} - -/* **************************************************************************** - * sync API - */ -int zoo_create(zhandle_t *zh, const char *path, const char *value, - int valuelen, const struct ACL_vector *acl, int flags, - char *path_buffer, int path_buffer_len) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - sc->u.str.str = path_buffer; - sc->u.str.str_len = path_buffer_len; - rc=zoo_acreate(zh, path, value, valuelen, acl, flags, SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - } - free_sync_completion(sc); - return rc; -} - -int zoo_delete(zhandle_t *zh, const char *path, int version) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc=zoo_adelete(zh, path, version, SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - } - free_sync_completion(sc); - return rc; -} - -int zoo_exists(zhandle_t *zh, const char *path, int watch, struct Stat *stat) -{ - return zoo_wexists(zh,path,watch?zh->watcher:0,zh->context,stat); -} - -int zoo_wexists(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, struct Stat *stat) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc=zoo_awexists(zh,path,watcher,watcherCtx,SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - if (rc == 0&& stat) { - *stat = sc->u.stat; - } - } - free_sync_completion(sc); - return rc; -} - -int zoo_get(zhandle_t *zh, const char *path, int watch, char *buffer, - int* buffer_len, struct Stat *stat) -{ - return zoo_wget(zh,path,watch?zh->watcher:0,zh->context, - buffer,buffer_len,stat); -} - -int zoo_wget(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - char *buffer, int* buffer_len, struct Stat *stat) -{ - struct sync_completion *sc; - int rc=0; - - if(buffer_len==NULL) - return ZBADARGUMENTS; - if((sc=alloc_sync_completion())==NULL) - return ZSYSTEMERROR; - - sc->u.data.buffer = buffer; - sc->u.data.buff_len = *buffer_len; - rc=zoo_awget(zh, path, watcher, watcherCtx, SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - if (rc == 0) { - if(stat) - *stat = sc->u.data.stat; - *buffer_len = sc->u.data.buff_len; - } - } - free_sync_completion(sc); - return rc; -} - -int zoo_set(zhandle_t *zh, const char *path, const char *buffer, int buflen, - int version) -{ - return zoo_set2(zh, path, buffer, buflen, version, 0); -} - -int zoo_set2(zhandle_t *zh, const char *path, const char *buffer, int buflen, - int version, struct Stat *stat) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc=zoo_aset(zh, path, buffer, buflen, version, SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - if (rc == 0 && stat) { - *stat = sc->u.stat; - } - } - free_sync_completion(sc); - return rc; -} - -static int zoo_wget_children_(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - struct String_vector *strings) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc= zoo_awget_children (zh, path, watcher, watcherCtx, SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - if (rc == 0) { - if (strings) { - *strings = sc->u.strs2; - } else { - deallocate_String_vector(&sc->u.strs2); - } - } - } - free_sync_completion(sc); - return rc; -} - -static int zoo_wget_children2_(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - struct String_vector *strings, struct Stat *stat) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc= zoo_awget_children2(zh, path, watcher, watcherCtx, SYNCHRONOUS_MARKER, sc); - - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - if (rc == 0) { - *stat = sc->u.strs_stat.stat2; - if (strings) { - *strings = sc->u.strs_stat.strs2; - } else { - deallocate_String_vector(&sc->u.strs_stat.strs2); - } - } - } - free_sync_completion(sc); - return rc; -} - -int zoo_get_children(zhandle_t *zh, const char *path, int watch, - struct String_vector *strings) -{ - return zoo_wget_children_(zh,path,watch?zh->watcher:0,zh->context,strings); -} - -int zoo_wget_children(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - struct String_vector *strings) -{ - return zoo_wget_children_(zh,path,watcher,watcherCtx,strings); -} - -int zoo_get_children2(zhandle_t *zh, const char *path, int watch, - struct String_vector *strings, struct Stat *stat) -{ - return zoo_wget_children2_(zh,path,watch?zh->watcher:0,zh->context,strings,stat); -} - -int zoo_wget_children2(zhandle_t *zh, const char *path, - watcher_fn watcher, void* watcherCtx, - struct String_vector *strings, struct Stat *stat) -{ - return zoo_wget_children2_(zh,path,watcher,watcherCtx,strings,stat); -} - -int zoo_get_acl(zhandle_t *zh, const char *path, struct ACL_vector *acl, - struct Stat *stat) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc=zoo_aget_acl(zh, path, SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - if (rc == 0&& stat) { - *stat = sc->u.acl.stat; - } - if (rc == 0) { - if (acl) { - *acl = sc->u.acl.acl; - } else { - deallocate_ACL_vector(&sc->u.acl.acl); - } - } - } - free_sync_completion(sc); - return rc; -} - -int zoo_set_acl(zhandle_t *zh, const char *path, int version, - const struct ACL_vector *acl) -{ - struct sync_completion *sc = alloc_sync_completion(); - int rc; - if (!sc) { - return ZSYSTEMERROR; - } - rc=zoo_aset_acl(zh, path, version, (struct ACL_vector*)acl, - SYNCHRONOUS_MARKER, sc); - if(rc==ZOK){ - wait_sync_completion(sc); - rc = sc->rc; - } - free_sync_completion(sc); - return rc; -} diff --git a/src/c/tests/CollectionUtil.h b/src/c/tests/CollectionUtil.h deleted file mode 100644 index dd3481175b0..00000000000 --- a/src/c/tests/CollectionUtil.h +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef _COLLECTION_UTIL_H_ -#define _COLLECTION_UTIL_H_ - -/** - * \file - * CollectionBuilder and DictionaryBuilder classes and collection utility functions - */ - -namespace Util -{ - -// ********************************************************* -/** A shortcut to use for building collections. - * This class is a wrapper around standard STL collection containers such as vector. - * It allows one to conveniently build collections at the variable initialization time: - * \code - * #include "CollectionUtil.h" - * #include "Vector.h" // for ostream << operator overload for STL vector - * using Util; - * - * int main() - * { - * typedef vector MyVector; - * MyVector myVector=CollectionBuilder()("str1")("str2")("str3"); - * cout< -class CollectionBuilder -{ -public: - /// Type of the collection container. - typedef CONT CollectionType; - /// Container's value type. - typedef typename CollectionType::value_type value_type; - /// Container's constant iterator type. - typedef typename CollectionType::const_iterator const_iterator; - /// Container's size type. - typedef typename CollectionType::size_type size_type; - - /** Operator function call overload to allow call chaining. - * \param value the value to be inserted into the container - */ - CollectionBuilder& operator()(const value_type& value){ - return push_back(value); - } - /** Same as regular STL push_back() but allows call chaining. - * \param value the value to be inserted into the container - */ - CollectionBuilder& push_back(const value_type& value){ - collection_.push_back(value); - return *this; - } - /// \name Standard STL container interface - /// @{ - const_iterator begin() const{return collection_.begin();} - const_iterator end() const{return collection_.end();} - size_type size() const{return collection_.size();} - void clear() {collection_.clear();} - ///@} - /// Explicit typecast operator. - operator const CollectionType&() const {return collection_;} -private: - /// \cond PRIVATE - CollectionType collection_; - /// \endcond -}; - - -// ********************************************************* -/** A shortcut to use for building dictionaries. - * This class is a wrapper around standard STL associative containers such as map. - * It allows one to conveniently build dictionaries at the variable initialization time: - * \code - * #include "CollectionUtil.h" - * #include "Map.h" // for ostream << operator overload for STL map - * using Util; - * - * int main() - * { - * typedef map MyMap; - * MyMap myMap=DictionaryBuilder()("str1",1)("str2",2)("str3",3); - * cout< -class DictionaryBuilder -{ -public: - /// The type of the associative container - typedef CONT DictionaryType; - /// Container's element type (usually a pair) - typedef typename DictionaryType::value_type value_type; - /// Container's key type - typedef typename DictionaryType::key_type key_type; - /// Container's value type - typedef typename DictionaryType::mapped_type mapped_type; - /// Container's constant iterator type - typedef typename DictionaryType::const_iterator const_iterator; - /// Container's writable iterator type - typedef typename DictionaryType::iterator iterator; - /// Container's size type - typedef typename DictionaryType::size_type size_type; - - /** Operator function call overload to allow call chaining. - * \param key the value key to be inserted - * \param value the value to be inserted into the container - * \return a non-const reference to self - */ - DictionaryBuilder& operator()(const key_type& key,const mapped_type& value){ - dict_.insert(value_type(key,value)); - return *this; - } - /** Lookup value by key. - * \param key the key associated with the value. - * \return a non-const iterator pointing to the element whose key matched the \a key parameter - */ - iterator find(const key_type& key){ - return dict_.find(key); - } - /** Lookup value by key. - * \param key the key associated with the value. - * \return a const iterator pointing to the element whose key matched the \a key parameter - */ - const_iterator find(const key_type& key) const{ - return dict_.find(key); - } - - /// \name Standard STL container interface - /// @{ - const_iterator begin() const{return dict_.begin();} - const_iterator end() const{return dict_.end();} - size_type size() const{return dict_.size();} - void clear() {dict_.clear();} - ///@} - /// Explicit typecast operator. - operator const DictionaryType&() const {return dict_;} -private: - DictionaryType dict_; -}; - - -// *********************************************************** -/** Deletes all dynamically allocated elements of a collection. - * C::value_type is expected to be a pointer to a dynamically allocated object, or it won't compile. - * The function will iterate over all container elements and call delete for each of them. - * \param c a collection (vector,set) whose elements are being deleted. - */ -template -void clearCollection(C& c){ - for(typename C::const_iterator it=c.begin();it!=c.end();++it) - delete *it; - c.clear(); -} - -/** Deletes all dynamically allocated values of the assotiative container. - * The function expects the M::value_type to be a pair<..., ptr_to_type>, or it won't compile. - * It first deletes the objects pointed to by ptr_to_type - * and then clears (calls m.clear()) the container. - * \param m an associative container (map,hash_map) whose elements are being deleted. - */ -template -void clearMap(M& m){ - for(typename M::const_iterator it=m.begin();it!=m.end();++it) - delete it->second; - m.clear(); -} - -} // namespace Util - - -#endif // _COLLECTION_UTIL_H_ diff --git a/src/c/tests/CppAssertHelper.h b/src/c/tests/CppAssertHelper.h deleted file mode 100644 index 3926f51efcb..00000000000 --- a/src/c/tests/CppAssertHelper.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CPPASSERTHELPER_H_ -#define CPPASSERTHELPER_H_ - -#include - -// make it possible to specify location of the ASSERT call -#define CPPUNIT_ASSERT_EQUAL_LOC(expected,actual,file,line) \ - ( CPPUNIT_NS::assertEquals( (expected), \ - (actual), \ - CPPUNIT_NS::SourceLine(file,line), \ - "" ) ) - -#define CPPUNIT_ASSERT_EQUAL_MESSAGE_LOC(message,expected,actual,file,line) \ - ( CPPUNIT_NS::assertEquals( (expected), \ - (actual), \ - CPPUNIT_NS::SourceLine(file,line), \ - (message) ) ) - -#endif /*CPPASSERTHELPER_H_*/ diff --git a/src/c/tests/LibCMocks.cc b/src/c/tests/LibCMocks.cc deleted file mode 100644 index 57de1e36dc2..00000000000 --- a/src/c/tests/LibCMocks.cc +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "Util.h" -#include "LibCMocks.h" - -#undef USING_DUMA - -using namespace std; - -// ***************************************************************************** -// gethostbyname - -struct hostent* gethostbyname(const char *name) { - if(!Mock_gethostbyname::mock_) - return LIBC_SYMBOLS.gethostbyname(name); - return Mock_gethostbyname::mock_->call(name); -} - -Mock_gethostbyname* Mock_gethostbyname::mock_=0; - -Mock_gethostbyname::~Mock_gethostbyname(){ - mock_=0; - for(unsigned int i=0;icall(p1,p2); -} -#endif - -void* Mock_calloc::call(size_t p1, size_t p2){ -#ifndef USING_DUMA - if(counter++ ==callsBeforeFailure){ - counter=0; - errno=errnoOnFailure; - return 0; - } - return CALL_REAL(calloc,(p1,p2)); -#else - return 0; -#endif -} - -Mock_calloc* Mock_calloc::mock_=0; - -// ***************************************************************************** -// realloc - -#ifndef USING_DUMA -void* realloc(void* p, size_t s){ - if(!Mock_realloc::mock_) - return LIBC_SYMBOLS.realloc(p,s); - return Mock_realloc::mock_->call(p,s); -} -#endif - -Mock_realloc* Mock_realloc::mock_=0; - -void* Mock_realloc::call(void* p, size_t s){ - if(counter++ ==callsBeforeFailure){ - counter=0; - errno=errnoOnFailure; - return 0; - } - return LIBC_SYMBOLS.realloc(p,s); -} - -// ***************************************************************************** -// random -RANDOM_RET_TYPE random(){ - if(!Mock_random::mock_) - return LIBC_SYMBOLS.random(); - return Mock_random::mock_->call(); -} - -void srandom(unsigned long seed){ - if (!Mock_random::mock_) - LIBC_SYMBOLS.srandom(seed); - else - Mock_random::mock_->setSeed(seed); -} - -Mock_random* Mock_random::mock_=0; - -int Mock_random::call(){ - assert("Must specify one or more random integers"&&(randomReturns.size()!=0)); - return randomReturns[currentIdx++ % randomReturns.size()]; -} - -// ***************************************************************************** -// free -#ifndef USING_DUMA -DECLARE_WRAPPER(void,free,(void* p)){ - if(Mock_free_noop::mock_ && !Mock_free_noop::mock_->nested) - Mock_free_noop::mock_->call(p); - else - CALL_REAL(free,(p)); -} -#endif - -void Mock_free_noop::call(void* p){ - // on cygwin libc++ is linked statically - // push_back() may call free(), hence the nesting guards - synchronized(mx); - nested++; - callCounter++; - requested.push_back(p); - nested--; -} -void Mock_free_noop::freeRequested(){ -#ifndef USING_DUMA - synchronized(mx); - for(unsigned i=0; icallSocket(domain,type,protocol); -} - -int close(int fd){ - if (!Mock_socket::mock_) - return LIBC_SYMBOLS.close(fd); - return Mock_socket::mock_->callClose(fd); -} - -int getsockopt(int s,int level,int optname,void *optval,socklen_t *optlen){ - if (!Mock_socket::mock_) - return LIBC_SYMBOLS.getsockopt(s,level,optname,optval,optlen); - return Mock_socket::mock_->callGet(s,level,optname,optval,optlen); -} - -int setsockopt(int s,int level,int optname,const void *optval,socklen_t optlen){ - if (!Mock_socket::mock_) - return LIBC_SYMBOLS.setsockopt(s,level,optname,optval,optlen); - return Mock_socket::mock_->callSet(s,level,optname,optval,optlen); -} -int connect(int s,const struct sockaddr *addr,socklen_t len){ - if (!Mock_socket::mock_) - return LIBC_SYMBOLS.connect(s,addr,len); - return Mock_socket::mock_->callConnect(s,addr,len); -} -ssize_t send(int s,const void *buf,size_t len,int flags){ - if (!Mock_socket::mock_) - return LIBC_SYMBOLS.send(s,buf,len,flags); - return Mock_socket::mock_->callSend(s,buf,len,flags); -} - -ssize_t recv(int s,void *buf,size_t len,int flags){ - if (!Mock_socket::mock_) - return LIBC_SYMBOLS.recv(s,buf,len,flags); - return Mock_socket::mock_->callRecv(s,buf,len,flags); -} - -Mock_socket* Mock_socket::mock_=0; - -// ***************************************************************************** -// fcntl -extern "C" int fcntl(int fd,int cmd,...){ - va_list va; - va_start(va,cmd); - void* arg = va_arg(va, void *); - va_end (va); - if (!Mock_fcntl::mock_) - return LIBC_SYMBOLS.fcntl(fd,cmd,arg); - return Mock_fcntl::mock_->call(fd,cmd,arg); -} - -Mock_fcntl* Mock_fcntl::mock_=0; - -// ***************************************************************************** -// select -int select(int nfds,fd_set *rfds,fd_set *wfds,fd_set *efds,struct timeval *timeout){ - if (!Mock_select::mock_) - return LIBC_SYMBOLS.select(nfds,rfds,wfds,efds,timeout); - return Mock_select::mock_->call(nfds,rfds,wfds,efds,timeout); -} - -Mock_select* Mock_select::mock_=0; - -// ***************************************************************************** -// poll -Mock_poll* Mock_poll::mock_=0; -int poll(struct pollfd *fds, POLL_NFDS_TYPE nfds, int timeout){ - if (!Mock_poll::mock_) - return LIBC_SYMBOLS.poll(fds,nfds,timeout); - return Mock_poll::mock_->call(fds,nfds,timeout); - -} - -// ***************************************************************************** -// gettimeofday -int gettimeofday(struct timeval *tp, GETTIMEOFDAY_ARG2_TYPE tzp){ - if (!Mock_gettimeofday::mock_) - return LIBC_SYMBOLS.gettimeofday(tp,tzp); - return Mock_gettimeofday::mock_->call(tp,tzp); -} - -Mock_gettimeofday* Mock_gettimeofday::mock_=0; - diff --git a/src/c/tests/LibCMocks.h b/src/c/tests/LibCMocks.h deleted file mode 100644 index 5b07cdae92d..00000000000 --- a/src/c/tests/LibCMocks.h +++ /dev/null @@ -1,408 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LIBCMOCKS_H_ -#define LIBCMOCKS_H_ - -#include -#include -#include - -#include -#include - -#include "MocksBase.h" -#include "LibCSymTable.h" -#include "ThreadingUtil.h" - -// ***************************************************************************** -// gethostbyname - -class Mock_gethostbyname: public Mock -{ -public: - struct HostEntry: public hostent { - HostEntry(const char* hostName,short addrtype); - ~HostEntry(); - HostEntry& addAlias(const char* alias); - HostEntry& addAddress(const char* addr4); - }; - - Mock_gethostbyname():current(0){mock_=this;} - virtual ~Mock_gethostbyname(); - HostEntry& addHostEntry(const char* hostName,short addrtype=AF_INET); - virtual hostent* call(const char* name); - - typedef std::vector HostEntryCollection; - HostEntryCollection gethostbynameReturns; - int current; - static Mock_gethostbyname* mock_; -}; - -class MockFailed_gethostbyname: public Mock_gethostbyname -{ -public: - MockFailed_gethostbyname():h_errnoReturn(HOST_NOT_FOUND) {} - - int h_errnoReturn; - virtual hostent* call(const char* name) { - h_errno=h_errnoReturn; - return 0; - } -}; - -// ***************************************************************************** -// calloc - -class Mock_calloc: public Mock -{ -public: - Mock_calloc():errnoOnFailure(ENOMEM),callsBeforeFailure(-1),counter(0) { - mock_=this; - } - virtual ~Mock_calloc() {mock_=0;} - - int errnoOnFailure; - int callsBeforeFailure; - int counter; - virtual void* call(size_t p1, size_t p2); - - static Mock_calloc* mock_; -}; - -// ***************************************************************************** -// realloc - -class Mock_realloc: public Mock -{ -public: - Mock_realloc():errnoOnFailure(ENOMEM),callsBeforeFailure(-1),counter(0) { - mock_=this; - } - virtual ~Mock_realloc() {mock_=0;} - - int errnoOnFailure; - int callsBeforeFailure; - int counter; - virtual void* call(void* p, size_t s); - - static Mock_realloc* mock_; -}; - -// ***************************************************************************** -// random - -class Mock_random: public Mock -{ -public: - Mock_random():currentIdx(0) {mock_=this;} - virtual ~Mock_random() {mock_=0;} - - int currentIdx; - std::vector randomReturns; - virtual int call(); - void setSeed(unsigned long){currentIdx=0;} - - static Mock_random* mock_; -}; - -// ***************************************************************************** -// no-op free; keeps track of all deallocation requests -class Mock_free_noop: public Mock -{ - Mutex mx; - std::vector requested; -public: - Mock_free_noop():nested(0),callCounter(0){mock_=this;} - virtual ~Mock_free_noop(){ - mock_=0; - freeRequested(); - } - - int nested; - int callCounter; - virtual void call(void* p); - void freeRequested(); - void disable(){mock_=0;} - // returns number of times the pointer was freed - int getFreeCount(void*); - bool isFreed(void*); - - static Mock_free_noop* mock_; -}; - -// ***************************************************************************** -// socket and related system calls - -class Mock_socket: public Mock -{ -public: - static const int FD=63; - Mock_socket():socketReturns(FD),closeReturns(0),getsocketoptReturns(0), - optvalSO_ERROR(0), - setsockoptReturns(0),connectReturns(0),connectErrno(0), - sendErrno(0),recvErrno(0) - { - mock_=this; - } - virtual ~Mock_socket(){mock_=0;} - - int socketReturns; - virtual int callSocket(int domain, int type, int protocol){ - return socketReturns; - } - int closeReturns; - virtual int callClose(int fd){ - return closeReturns; - } - int getsocketoptReturns; - int optvalSO_ERROR; - virtual int callGet(int s,int level,int optname,void *optval,socklen_t *len){ - if(level==SOL_SOCKET && optname==SO_ERROR){ - setSO_ERROR(optval,*len); - } - return getsocketoptReturns; - } - virtual void setSO_ERROR(void *optval,socklen_t len){ - memcpy(optval,&optvalSO_ERROR,len); - } - - int setsockoptReturns; - virtual int callSet(int s,int level,int optname,const void *optval,socklen_t len){ - return setsockoptReturns; - } - int connectReturns; - int connectErrno; - virtual int callConnect(int s,const struct sockaddr *addr,socklen_t len){ - errno=connectErrno; - return connectReturns; - } - - virtual void notifyBufferSent(const std::string& buffer){} - - int sendErrno; - std::string sendBuffer; - virtual ssize_t callSend(int s,const void *buf,size_t len,int flags){ - if(sendErrno!=0){ - errno=sendErrno; - return -1; - } - // first call to send() is always the length of the buffer to follow - bool sendingLength=sendBuffer.size()==0; - // overwrite the length bytes - sendBuffer.assign((const char*)buf,len); - if(!sendingLength){ - notifyBufferSent(sendBuffer); - sendBuffer.erase(); - } - return len; - } - - int recvErrno; - std::string recvReturnBuffer; - virtual ssize_t callRecv(int s,void *buf,size_t len,int flags){ - if(recvErrno!=0){ - errno=recvErrno; - return -1; - } - int k=std::min(len,recvReturnBuffer.length()); - if(k==0) - return 0; - memcpy(buf,recvReturnBuffer.data(),k); - recvReturnBuffer.erase(0,k); - return k; - } - virtual bool hasMoreRecv() const{ - return recvReturnBuffer.size()!=0; - } - static Mock_socket* mock_; -}; - -// ***************************************************************************** -// fcntl -class Mock_fcntl: public Mock -{ -public: - Mock_fcntl():callReturns(0),trapFD(-1){mock_=this;} - ~Mock_fcntl(){mock_=0;} - - int callReturns; - int trapFD; - virtual int call(int fd, int cmd, void* arg){ - if(trapFD==-1) - return LIBC_SYMBOLS.fcntl(fd,cmd,arg); - return callReturns; - } - - static Mock_fcntl* mock_; -}; - -// ***************************************************************************** -// select -class Mock_select: public Mock -{ -public: - Mock_select(Mock_socket* s,int fd):sock(s), - callReturns(0),myFD(fd),timeout(50) - { - mock_=this; - } - ~Mock_select(){mock_=0;} - - Mock_socket* sock; - int callReturns; - int myFD; - int timeout; //in millis - virtual int call(int nfds,fd_set *rfds,fd_set *wfds,fd_set *efds,struct timeval *tv){ - bool isWritableRequested=(wfds && FD_ISSET(myFD,wfds)); - if(rfds) FD_CLR(myFD,rfds); - if(wfds) FD_CLR(myFD,wfds); - // this timeout is only to prevent a tight loop - timeval myTimeout={0,0}; - if(!isWritableRequested && !isFDReadable()){ - myTimeout.tv_sec=timeout/1000; - myTimeout.tv_usec=(timeout%1000)*1000; - } - LIBC_SYMBOLS.select(nfds,rfds,wfds,efds,&myTimeout); - // myFD is always writable - if(isWritableRequested) FD_SET(myFD,wfds); - // myFD is only readable if the socket has anything to read - if(isFDReadable() && rfds) FD_SET(myFD,rfds); - return callReturns; - } - - virtual bool isFDReadable() const { - return sock->hasMoreRecv(); - } - - static Mock_select* mock_; -}; - -// ***************************************************************************** -// poll -// the last element of the pollfd array is expected to be test FD -class Mock_poll: public Mock -{ -public: - Mock_poll(Mock_socket* s,int fd):sock(s), - callReturns(1),myFD(fd),timeout(50) - { - mock_=this; - } - ~Mock_poll(){mock_=0;} - - Mock_socket* sock; - int callReturns; - int myFD; - int timeout; //in millis - virtual int call(struct pollfd *fds, POLL_NFDS_TYPE nfds, int to) { - pollfd* myPoll=0; - if(fds[nfds-1].fd==myFD) - myPoll=&fds[nfds-1]; - bool isWritableRequested=false; - if(myPoll!=0){ - isWritableRequested=myPoll->events&POLLOUT; - nfds--; - } - LIBC_SYMBOLS.poll(fds,nfds,(!isWritableRequested&&!isFDReadable())?timeout:0); - if(myPoll!=0){ - // myFD is always writable if requested - myPoll->revents=isWritableRequested?POLLOUT:0; - // myFD is only readable if the socket has anything to read - myPoll->revents|=isFDReadable()?POLLIN:0; - } - return callReturns; - } - - virtual bool isFDReadable() const { - return sock->hasMoreRecv(); - } - - static Mock_poll* mock_; -}; - -// ***************************************************************************** -// gettimeofday -class Mock_gettimeofday: public Mock -{ -public: - Mock_gettimeofday(){ - LIBC_SYMBOLS.gettimeofday(&tv,0); - mock_=this; - } - Mock_gettimeofday(const Mock_gettimeofday& other):tv(other.tv){} - Mock_gettimeofday(int32_t sec,int32_t usec){ - tv.tv_sec=sec; - tv.tv_usec=usec; - } - ~Mock_gettimeofday(){mock_=0;} - - timeval tv; - virtual int call(struct timeval *tp, GETTIMEOFDAY_ARG2_TYPE tzp){ - *tp=tv; - return 0; - } - operator timeval() const{ - return tv; - } - // advance secs - virtual void tick(int howmuch=1){tv.tv_sec+=howmuch;} - // advance milliseconds - // can move the clock forward as well as backward by providing a negative - // number - virtual void millitick(int howmuch=1){ - int ms=tv.tv_usec/1000+howmuch; - tv.tv_sec+=ms/1000; - // going backward? - if(ms<0){ - ms=1000-(-ms%1000); //wrap millis around - } - tv.tv_usec=(ms%1000)*1000; - } - virtual void tick(const timeval& howmuch){ - // add milliseconds (discarding microsecond portion) - long ms=tv.tv_usec/1000+howmuch.tv_usec/1000; - tv.tv_sec+=howmuch.tv_sec+ms/1000; - tv.tv_usec=(ms%1000)*1000; - } - static Mock_gettimeofday* mock_; -}; - -// discard microseconds! -inline bool operator==(const timeval& lhs, const timeval& rhs){ - return rhs.tv_sec==lhs.tv_sec && rhs.tv_usec/1000==lhs.tv_usec/1000; -} - -// simplistic implementation: no normalization, assume lhs >= rhs, -// discarding microseconds -inline timeval operator-(const timeval& lhs, const timeval& rhs){ - timeval res; - res.tv_sec=lhs.tv_sec-rhs.tv_sec; - res.tv_usec=(lhs.tv_usec/1000-rhs.tv_usec/1000)*1000; - if(res.tv_usec<0){ - res.tv_sec--; - res.tv_usec=1000000+res.tv_usec%1000000; // wrap the millis around - } - return res; -} - -inline int32_t toMilliseconds(const timeval& tv){ - return tv.tv_sec*1000+tv.tv_usec/1000; -} - -#endif /*LIBCMOCKS_H_*/ diff --git a/src/c/tests/LibCSymTable.cc b/src/c/tests/LibCSymTable.cc deleted file mode 100644 index 53785796a93..00000000000 --- a/src/c/tests/LibCSymTable.cc +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LibCSymTable.h" - -#define LOAD_SYM(sym) \ - sym=(sym##_sig)dlsym(handle,#sym); \ - assert("Unable to load "#sym" from libc"&&sym) - - -LibCSymTable& LibCSymTable::instance(){ - static LibCSymTable tbl; - return tbl; -} - -//****************************************************************************** -// preload original libc symbols -LibCSymTable::LibCSymTable() -{ - void* handle=getHandle(); - LOAD_SYM(gethostbyname); - LOAD_SYM(calloc); - LOAD_SYM(realloc); - LOAD_SYM(free); - LOAD_SYM(random); - LOAD_SYM(srandom); - LOAD_SYM(printf); - LOAD_SYM(socket); - LOAD_SYM(close); - LOAD_SYM(getsockopt); - LOAD_SYM(setsockopt); - LOAD_SYM(fcntl); - LOAD_SYM(connect); - LOAD_SYM(send); - LOAD_SYM(recv); - LOAD_SYM(select); - LOAD_SYM(poll); - LOAD_SYM(gettimeofday); -#ifdef THREADED - LOAD_SYM(pthread_create); - LOAD_SYM(pthread_detach); - LOAD_SYM(pthread_cond_broadcast); - LOAD_SYM(pthread_cond_destroy); - LOAD_SYM(pthread_cond_init); - LOAD_SYM(pthread_cond_signal); - LOAD_SYM(pthread_cond_timedwait); - LOAD_SYM(pthread_cond_wait); - LOAD_SYM(pthread_join); - LOAD_SYM(pthread_mutex_destroy); - LOAD_SYM(pthread_mutex_init); - LOAD_SYM(pthread_mutex_lock); - LOAD_SYM(pthread_mutex_trylock); - LOAD_SYM(pthread_mutex_unlock); -#endif -} - -void* LibCSymTable::getHandle(){ - static void* handle=0; - if(!handle){ -#ifdef __CYGWIN__ - handle=dlopen("cygwin1.dll",RTLD_LAZY); - assert("Unable to dlopen global sym table"&&handle); -#else - handle=RTLD_NEXT; -#endif - } - return handle; -} diff --git a/src/c/tests/LibCSymTable.h b/src/c/tests/LibCSymTable.h deleted file mode 100644 index 2f7e0c291f0..00000000000 --- a/src/c/tests/LibCSymTable.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LIBCSYMTABLE_H_ -#define LIBCSYMTABLE_H_ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef THREADED -#include -#endif - -#include "config.h" - -// TODO: move all these macros to config.h (generated by autoconf) -#ifdef __CYGWIN__ -#if (CYGWIN_VERSION_DLL_MAJOR < 1007) -#define RANDOM_RET_TYPE int -#else -#define RANDOM_RET_TYPE long int -#endif -#define GETTIMEOFDAY_ARG2_TYPE void* -#else -#define RANDOM_RET_TYPE long int -#define GETTIMEOFDAY_ARG2_TYPE struct timezone* -#endif - -#define DECLARE_SYM(ret,sym,sig) \ - typedef ret (*sym##_sig)sig; \ - static sym##_sig preload_##sym () { \ - static sym##_sig ptr=0;\ - if(!ptr){ void* h=getHandle(); ptr=(sym##_sig)dlsym(h,#sym); } \ - assert("Unable to load "#sym" from libc"&&ptr); \ - return ptr; \ - } \ - sym##_sig sym - -#define LIBC_SYMBOLS LibCSymTable::instance() - -//****************************************************************************** -// preload original libc symbols -struct LibCSymTable -{ - DECLARE_SYM(hostent*,gethostbyname,(const char*)); - DECLARE_SYM(void*,calloc,(size_t, size_t)); - DECLARE_SYM(void*,realloc,(void*, size_t)); - DECLARE_SYM(void,free,(void*)); - DECLARE_SYM(RANDOM_RET_TYPE,random,(void)); - DECLARE_SYM(void,srandom,(unsigned long)); - DECLARE_SYM(int,printf,(const char*, ...)); - DECLARE_SYM(int,socket,(int,int,int)); - DECLARE_SYM(int,close,(int)); - DECLARE_SYM(int,getsockopt,(int,int,int,void*,socklen_t*)); - DECLARE_SYM(int,setsockopt,(int,int,int,const void*,socklen_t)); - DECLARE_SYM(int,fcntl,(int,int,...)); - DECLARE_SYM(int,connect,(int,const struct sockaddr*,socklen_t)); - DECLARE_SYM(ssize_t,send,(int,const void*,size_t,int)); - DECLARE_SYM(ssize_t,recv,(int,const void*,size_t,int)); - DECLARE_SYM(int,select,(int,fd_set*,fd_set*,fd_set*,struct timeval*)); - DECLARE_SYM(int,poll,(struct pollfd*,POLL_NFDS_TYPE,int)); - DECLARE_SYM(int,gettimeofday,(struct timeval*,GETTIMEOFDAY_ARG2_TYPE)); -#ifdef THREADED - DECLARE_SYM(int,pthread_create,(pthread_t *, const pthread_attr_t *, - void *(*)(void *), void *)); - DECLARE_SYM(int,pthread_detach,(pthread_t)); - DECLARE_SYM(int,pthread_cond_broadcast,(pthread_cond_t *)); - DECLARE_SYM(int,pthread_cond_destroy,(pthread_cond_t *)); - DECLARE_SYM(int,pthread_cond_init,(pthread_cond_t *, const pthread_condattr_t *)); - DECLARE_SYM(int,pthread_cond_signal,(pthread_cond_t *)); - DECLARE_SYM(int,pthread_cond_timedwait,(pthread_cond_t *, - pthread_mutex_t *, const struct timespec *)); - DECLARE_SYM(int,pthread_cond_wait,(pthread_cond_t *, pthread_mutex_t *)); - DECLARE_SYM(int,pthread_join,(pthread_t, void **)); - DECLARE_SYM(int,pthread_mutex_destroy,(pthread_mutex_t *)); - DECLARE_SYM(int,pthread_mutex_init,(pthread_mutex_t *, const pthread_mutexattr_t *)); - DECLARE_SYM(int,pthread_mutex_lock,(pthread_mutex_t *)); - DECLARE_SYM(int,pthread_mutex_trylock,(pthread_mutex_t *)); - DECLARE_SYM(int,pthread_mutex_unlock,(pthread_mutex_t *)); -#endif - LibCSymTable(); - - static void* getHandle(); - static LibCSymTable& instance(); -}; - -#endif /*LIBCSYMTABLE_H_*/ diff --git a/src/c/tests/MocksBase.cc b/src/c/tests/MocksBase.cc deleted file mode 100644 index 60b2c75f25b..00000000000 --- a/src/c/tests/MocksBase.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "MocksBase.h" -#include "LibCSymTable.h" - -// ***************************************************************************** -// Mock base -void* Mock::operator new(std::size_t s){ - void* p=malloc(s); - if(!p) - throw std::bad_alloc(); - return p; -} - -void Mock::operator delete(void* p){ - LIBC_SYMBOLS.free(p); -} diff --git a/src/c/tests/MocksBase.h b/src/c/tests/MocksBase.h deleted file mode 100644 index 5b54251eca0..00000000000 --- a/src/c/tests/MocksBase.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MOCKSBASE_H_ -#define MOCKSBASE_H_ - -#include - -// ***************************************************************************** -// Mock base - -class Mock -{ -public: - virtual ~Mock(){} - - static void* operator new(std::size_t s); - static void operator delete(void* p); -}; - -#endif /*MOCKSBASE_H_*/ diff --git a/src/c/tests/PthreadMocks.cc b/src/c/tests/PthreadMocks.cc deleted file mode 100644 index 490cebf3361..00000000000 --- a/src/c/tests/PthreadMocks.cc +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PthreadMocks.h" - -MockPthreadsBase* MockPthreadsBase::mock_=0; - -#undef USING_DUMA - -#ifndef USING_DUMA -int pthread_cond_broadcast (pthread_cond_t *c){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_cond_broadcast(c); - return MockPthreadsBase::mock_->pthread_cond_broadcast(c); -} -int pthread_cond_destroy (pthread_cond_t *c){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_cond_destroy(c); - return MockPthreadsBase::mock_->pthread_cond_destroy(c); -} -int pthread_cond_init (pthread_cond_t *c, const pthread_condattr_t *a){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_cond_init(c,a); - return MockPthreadsBase::mock_->pthread_cond_init(c,a); -} -int pthread_cond_signal (pthread_cond_t *c){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_cond_signal(c); - return MockPthreadsBase::mock_->pthread_cond_signal(c); -} -int pthread_cond_timedwait (pthread_cond_t *c, - pthread_mutex_t *m, const struct timespec *t){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_cond_timedwait(c,m,t); - return MockPthreadsBase::mock_->pthread_cond_timedwait(c,m,t); -} -int pthread_cond_wait (pthread_cond_t *c, pthread_mutex_t *m){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_cond_wait(c,m); - return MockPthreadsBase::mock_->pthread_cond_wait(c,m); -} -int pthread_create (pthread_t *t, const pthread_attr_t *a, - void *(*f)(void *), void *d){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_create(t,a,f,d); - return MockPthreadsBase::mock_->pthread_create(t,a,f,d); -} -int pthread_detach(pthread_t t){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_detach(t); - return MockPthreadsBase::mock_->pthread_detach(t); -} -int pthread_join (pthread_t t, void **r){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_join(t,r); - return MockPthreadsBase::mock_->pthread_join(t,r); -} -int pthread_mutex_destroy (pthread_mutex_t *m){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_mutex_destroy(m); - return MockPthreadsBase::mock_->pthread_mutex_destroy(m); -} -int pthread_mutex_init (pthread_mutex_t *m, const pthread_mutexattr_t *a){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_mutex_init(m,a); - return MockPthreadsBase::mock_->pthread_mutex_init(m,a); -} - -DECLARE_WRAPPER(int,pthread_mutex_lock,(pthread_mutex_t *m)){ - if(!MockPthreadsBase::mock_) - return CALL_REAL(pthread_mutex_lock,(m)); - return MockPthreadsBase::mock_->pthread_mutex_lock(m); -} - -int pthread_mutex_trylock (pthread_mutex_t *m){ - if(!MockPthreadsBase::mock_) - return LIBC_SYMBOLS.pthread_mutex_trylock(m); - return MockPthreadsBase::mock_->pthread_mutex_trylock(m); -} - -DECLARE_WRAPPER(int,pthread_mutex_unlock,(pthread_mutex_t *m)){ - if(!MockPthreadsBase::mock_) - return CALL_REAL(pthread_mutex_unlock,(m)); - return MockPthreadsBase::mock_->pthread_mutex_unlock(m); -} -#endif - -CheckedPthread::ThreadMap CheckedPthread::tmap_; -CheckedPthread::MutexMap CheckedPthread::mmap_; -CheckedPthread::CVMap CheckedPthread::cvmap_; -Mutex CheckedPthread::mx; diff --git a/src/c/tests/PthreadMocks.h b/src/c/tests/PthreadMocks.h deleted file mode 100644 index 8db881501c6..00000000000 --- a/src/c/tests/PthreadMocks.h +++ /dev/null @@ -1,449 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PTHREADMOCKS_H_ -#define PTHREADMOCKS_H_ - -#include -#include -#include - -#include "src/zk_adaptor.h" - -#include "Util.h" -#include "MocksBase.h" -#include "LibCSymTable.h" -#include "ThreadingUtil.h" - -// an ABC for pthreads -class MockPthreadsBase: public Mock -{ -public: - MockPthreadsBase(){mock_=this;} - virtual ~MockPthreadsBase(){mock_=0;} - - virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, - void *(*f)(void *), void *d) =0; - virtual int pthread_join(pthread_t t, void ** r) =0; - virtual int pthread_detach(pthread_t t) =0; - virtual int pthread_cond_broadcast(pthread_cond_t *c) =0; - virtual int pthread_cond_destroy(pthread_cond_t *c) =0; - virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a) =0; - virtual int pthread_cond_signal(pthread_cond_t *c) =0; - virtual int pthread_cond_timedwait(pthread_cond_t *c, - pthread_mutex_t *m, const struct timespec *t) =0; - virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) =0; - virtual int pthread_mutex_destroy(pthread_mutex_t *m) =0; - virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) =0; - virtual int pthread_mutex_lock(pthread_mutex_t *m) =0; - virtual int pthread_mutex_trylock(pthread_mutex_t *m) =0; - virtual int pthread_mutex_unlock(pthread_mutex_t *m) =0; - - static MockPthreadsBase* mock_; -}; - -// all pthread functions simply return an error code -// and increment their invocation counter. No actual threads are spawned. -class MockPthreadsNull: public MockPthreadsBase -{ -public: - MockPthreadsNull(): - pthread_createReturns(0),pthread_createCounter(0), - pthread_joinReturns(0),pthread_joinCounter(0),pthread_joinResultReturn(0), - pthread_detachReturns(0),pthread_detachCounter(0), - pthread_cond_broadcastReturns(0),pthread_cond_broadcastCounter(0), - pthread_cond_destroyReturns(0),pthread_cond_destroyCounter(0), - pthread_cond_initReturns(0),pthread_cond_initCounter(0), - pthread_cond_signalReturns(0),pthread_cond_signalCounter(0), - pthread_cond_timedwaitReturns(0),pthread_cond_timedwaitCounter(0), - pthread_cond_waitReturns(0),pthread_cond_waitCounter(0), - pthread_mutex_destroyReturns(0),pthread_mutex_destroyCounter(0), - pthread_mutex_initReturns(0),pthread_mutex_initCounter(0), - pthread_mutex_lockReturns(0),pthread_mutex_lockCounter(0), - pthread_mutex_trylockReturns(0),pthread_mutex_trylockCounter(0), - pthread_mutex_unlockReturns(0),pthread_mutex_unlockCounter(0) - { - memset(threads,0,sizeof(threads)); - } - - short threads[512]; - - int pthread_createReturns; - int pthread_createCounter; - virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, - void *(*f)(void *), void *d){ - char* p=(char*)&threads[pthread_createCounter++]; - p[0]='i'; // mark as created - *t=(pthread_t)p; - return pthread_createReturns; - } - int pthread_joinReturns; - int pthread_joinCounter; - void* pthread_joinResultReturn; - virtual int pthread_join(pthread_t t, void ** r){ - pthread_joinCounter++; - if(r!=0) - *r=pthread_joinResultReturn; - char* p=(char*)t; - p[0]='x';p[1]+=1; - return pthread_joinReturns; - } - int pthread_detachReturns; - int pthread_detachCounter; - virtual int pthread_detach(pthread_t t){ - pthread_detachCounter++; - char* p=(char*)t; - p[0]='x';p[1]+=1; - return pthread_detachReturns; - } - - template - static bool isInitialized(const T& t){ - return ((char*)t)[0]=='i'; - } - template - static bool isDestroyed(const T& t){ - return ((char*)t)[0]=='x'; - } - template - static int getDestroyCounter(const T& t){ - return ((char*)t)[1]; - } - template - static int getInvalidAccessCounter(const T& t){ - return ((char*)t)[2]; - } - int pthread_cond_broadcastReturns; - int pthread_cond_broadcastCounter; - virtual int pthread_cond_broadcast(pthread_cond_t *c){ - pthread_cond_broadcastCounter++; - if(isDestroyed(c))((char*)c)[2]++; - return pthread_cond_broadcastReturns; - } - int pthread_cond_destroyReturns; - int pthread_cond_destroyCounter; - virtual int pthread_cond_destroy(pthread_cond_t *c){ - pthread_cond_destroyCounter++; - char* p=(char*)c; - p[0]='x';p[1]+=1; - return pthread_cond_destroyReturns; - } - int pthread_cond_initReturns; - int pthread_cond_initCounter; - virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a){ - pthread_cond_initCounter++; - char* p=(char*)c; - p[0]='i'; // mark as created - p[1]=0; // destruction counter - p[2]=0; // access after destruction counter - return pthread_cond_initReturns; - } - int pthread_cond_signalReturns; - int pthread_cond_signalCounter; - virtual int pthread_cond_signal(pthread_cond_t *c){ - pthread_cond_signalCounter++; - if(isDestroyed(c))((char*)c)[2]++; - return pthread_cond_signalReturns; - } - int pthread_cond_timedwaitReturns; - int pthread_cond_timedwaitCounter; - virtual int pthread_cond_timedwait(pthread_cond_t *c, - pthread_mutex_t *m, const struct timespec *t){ - pthread_cond_timedwaitCounter++; - if(isDestroyed(c))((char*)c)[2]++; - return pthread_cond_timedwaitReturns; - } - int pthread_cond_waitReturns; - int pthread_cond_waitCounter; - virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m){ - pthread_cond_waitCounter++; - if(isDestroyed(c))((char*)c)[2]++; - return pthread_cond_waitReturns; - } - int pthread_mutex_destroyReturns; - int pthread_mutex_destroyCounter; - virtual int pthread_mutex_destroy(pthread_mutex_t *m){ - pthread_mutex_destroyCounter++; - char* p=(char*)m; - p[0]='x';p[1]+=1; - return pthread_mutex_destroyReturns; - } - int pthread_mutex_initReturns; - int pthread_mutex_initCounter; - virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a){ - pthread_mutex_initCounter++; - char* p=(char*)m; - p[0]='i'; // mark as created - p[1]=0; // destruction counter - p[2]=0; // access after destruction counter - return pthread_mutex_initReturns; - } - int pthread_mutex_lockReturns; - int pthread_mutex_lockCounter; - virtual int pthread_mutex_lock(pthread_mutex_t *m){ - pthread_mutex_lockCounter++; - if(isDestroyed(m))((char*)m)[2]++; - return pthread_mutex_lockReturns; - } - int pthread_mutex_trylockReturns; - int pthread_mutex_trylockCounter; - virtual int pthread_mutex_trylock(pthread_mutex_t *m){ - pthread_mutex_trylockCounter++; - if(isDestroyed(m))((char*)m)[2]++; - return pthread_mutex_trylockReturns; - } - int pthread_mutex_unlockReturns; - int pthread_mutex_unlockCounter; - virtual int pthread_mutex_unlock(pthread_mutex_t *m){ - pthread_mutex_unlockCounter++; - if(isDestroyed(m))((char*)m)[2]++; - return pthread_mutex_unlockReturns; - } -}; - -// simulates the way zookeeper threads make use of api_prolog/epilog and -// -class MockPthreadZKNull: public MockPthreadsNull -{ - typedef std::map Map; - Map map_; -public: - virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, - void *(*f)(void *), void *d){ - int ret=MockPthreadsNull::pthread_create(t,a,f,d); - zhandle_t* zh=(zhandle_t*)d; - adaptor_threads* ad=(adaptor_threads*)zh->adaptor_priv; - api_prolog(zh); - ad->threadsToWait--; - putValue(map_,*t,zh); - return ret; - } - virtual int pthread_join(pthread_t t, void ** r){ - zhandle_t* zh=0; - if(getValue(map_,t,zh)) - api_epilog(zh,0); - return MockPthreadsNull::pthread_join(t,r); - } -}; - -struct ThreadInfo{ - typedef enum {RUNNING,TERMINATED} ThreadState; - - ThreadInfo(): - destructionCounter_(0),invalidAccessCounter_(0),state_(RUNNING) - { - } - - ThreadInfo& incDestroyed() { - destructionCounter_++; - return *this; - } - ThreadInfo& incInvalidAccess(){ - invalidAccessCounter_++; - return *this; - } - ThreadInfo& setTerminated(){ - state_=TERMINATED; - return *this; - } - int destructionCounter_; - int invalidAccessCounter_; - ThreadState state_; -}; - -class CheckedPthread: public MockPthreadsBase -{ - // first => destruction counter - // second => invalid access counter - //typedef std::pair Entry; - typedef ThreadInfo Entry; - typedef std::map ThreadMap; - static ThreadMap tmap_; - static ThreadMap& getMap(const TypeOp::BareT&){return tmap_;} - typedef std::map MutexMap; - static MutexMap mmap_; - static MutexMap& getMap(const TypeOp::BareT&){return mmap_;} - typedef std::map CVMap; - static CVMap cvmap_; - static CVMap& getMap(const TypeOp::BareT&){return cvmap_;} - - static Mutex mx; - - template - static void markDestroyed(T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - if(getValue(getMap(Type()),t,e)){ - putValue(getMap(Type()),t,Entry(e).incDestroyed()); - }else{ - putValue(getMap(Type()),t,Entry().incDestroyed()); - } - } - template - static void markCreated(T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - if(!getValue(getMap(Type()),t,e)) - putValue(getMap(Type()),t,Entry()); - } - template - static void checkAccessed(T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - if(getValue(getMap(Type()),t,e) && e.destructionCounter_>0) - putValue(getMap(Type()),t,Entry(e).incInvalidAccess()); - } - static void setTerminated(pthread_t t){ - Entry e; - synchronized(mx); - if(getValue(tmap_,t,e)) - putValue(tmap_,t,Entry(e).setTerminated()); - } -public: - bool verbose; - CheckedPthread():verbose(false){ - tmap_.clear(); - mmap_.clear(); - cvmap_.clear(); - mx.release(); - } - template - static bool isInitialized(const T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - return getValue(getMap(Type()),t,e) && e.destructionCounter_==0; - } - template - static bool isDestroyed(const T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - return getValue(getMap(Type()),t,e) && e.destructionCounter_>0; - } - static bool isTerminated(pthread_t t){ - Entry e; - synchronized(mx); - return getValue(tmap_,t,e) && e.state_==ThreadInfo::TERMINATED; - } - template - static int getDestroyCounter(const T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - return getValue(getMap(Type()),t,e)?e.destructionCounter_:-1; - } - template - static int getInvalidAccessCounter(const T& t){ - typedef typename TypeOp::BareT Type; - Entry e; - synchronized(mx); - return getValue(getMap(Type()),t,e)?e.invalidAccessCounter_:-1; - } - - struct ThreadContext{ - typedef void *(*ThreadFunc)(void *); - - ThreadContext(ThreadFunc func,void* param):func_(func),param_(param){} - ThreadFunc func_; - void* param_; - }; - static void* threadFuncWrapper(void* v){ - ThreadContext* ctx=(ThreadContext*)v; - pthread_t t=pthread_self(); - markCreated(t); - void* res=ctx->func_(ctx->param_); - setTerminated(pthread_self()); - delete ctx; - return res; - } - virtual int pthread_create(pthread_t * t, const pthread_attr_t *a, - void *(*f)(void *), void *d) - { - int ret=LIBC_SYMBOLS.pthread_create(t,a,threadFuncWrapper, - new ThreadContext(f,d)); - if(verbose) - TEST_TRACE(("thread created %p",*t)); - return ret; - } - virtual int pthread_join(pthread_t t, void ** r){ - if(verbose) TEST_TRACE(("thread joined %p",t)); - int ret=LIBC_SYMBOLS.pthread_join(t,r); - if(ret==0) - markDestroyed(t); - return ret; - } - virtual int pthread_detach(pthread_t t){ - if(verbose) TEST_TRACE(("thread detached %p",t)); - int ret=LIBC_SYMBOLS.pthread_detach(t); - if(ret==0) - markDestroyed(t); - return ret; - } - virtual int pthread_cond_broadcast(pthread_cond_t *c){ - checkAccessed(c); - return LIBC_SYMBOLS.pthread_cond_broadcast(c); - } - virtual int pthread_cond_destroy(pthread_cond_t *c){ - markDestroyed(c); - return LIBC_SYMBOLS.pthread_cond_destroy(c); - } - virtual int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a){ - markCreated(c); - return LIBC_SYMBOLS.pthread_cond_init(c,a); - } - virtual int pthread_cond_signal(pthread_cond_t *c){ - checkAccessed(c); - return LIBC_SYMBOLS.pthread_cond_signal(c); - } - virtual int pthread_cond_timedwait(pthread_cond_t *c, - pthread_mutex_t *m, const struct timespec *t){ - checkAccessed(c); - return LIBC_SYMBOLS.pthread_cond_timedwait(c,m,t); - } - virtual int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m){ - checkAccessed(c); - return LIBC_SYMBOLS.pthread_cond_wait(c,m); - } - virtual int pthread_mutex_destroy(pthread_mutex_t *m){ - markDestroyed(m); - return LIBC_SYMBOLS.pthread_mutex_destroy(m); - } - virtual int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a){ - markCreated(m); - return LIBC_SYMBOLS.pthread_mutex_init(m,a); - } - virtual int pthread_mutex_lock(pthread_mutex_t *m){ - checkAccessed(m); - return LIBC_SYMBOLS.pthread_mutex_lock(m); - } - virtual int pthread_mutex_trylock(pthread_mutex_t *m){ - checkAccessed(m); - return LIBC_SYMBOLS.pthread_mutex_trylock(m); - } - virtual int pthread_mutex_unlock(pthread_mutex_t *m){ - checkAccessed(m); - return LIBC_SYMBOLS.pthread_mutex_unlock(m); - } -}; - -#endif /*PTHREADMOCKS_H_*/ - diff --git a/src/c/tests/TestClient.cc b/src/c/tests/TestClient.cc deleted file mode 100644 index cc994c6418b..00000000000 --- a/src/c/tests/TestClient.cc +++ /dev/null @@ -1,1082 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CppAssertHelper.h" - -#include -#include -#include -#include - -#include "CollectionUtil.h" -#include "ThreadingUtil.h" - -using namespace Util; - -#include "Vector.h" -using namespace std; - -#include -#include - -#include -#include -#include -#include "Util.h" - -struct buff_struct_2 { - int32_t len; - int32_t off; - char *buffer; -}; - -static int Stat_eq(struct Stat* a, struct Stat* b) -{ - if (a->czxid != b->czxid) return 0; - if (a->mzxid != b->mzxid) return 0; - if (a->ctime != b->ctime) return 0; - if (a->mtime != b->mtime) return 0; - if (a->version != b->version) return 0; - if (a->cversion != b->cversion) return 0; - if (a->aversion != b->aversion) return 0; - if (a->ephemeralOwner != b->ephemeralOwner) return 0; - if (a->dataLength != b->dataLength) return 0; - if (a->numChildren != b->numChildren) return 0; - if (a->pzxid != b->pzxid) return 0; - return 1; -} -#ifdef THREADED - static void yield(zhandle_t *zh, int i) - { - sleep(i); - } -#else - static void yield(zhandle_t *zh, int seconds) - { - int fd; - int interest; - int events; - struct timeval tv; - int rc; - time_t expires = time(0) + seconds; - time_t timeLeft = seconds; - fd_set rfds, wfds, efds; - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - - while(timeLeft >= 0) { - zookeeper_interest(zh, &fd, &interest, &tv); - if (fd != -1) { - if (interest&ZOOKEEPER_READ) { - FD_SET(fd, &rfds); - } else { - FD_CLR(fd, &rfds); - } - if (interest&ZOOKEEPER_WRITE) { - FD_SET(fd, &wfds); - } else { - FD_CLR(fd, &wfds); - } - } else { - fd = 0; - } - FD_SET(0, &rfds); - if (tv.tv_sec > timeLeft) { - tv.tv_sec = timeLeft; - } - rc = select(fd+1, &rfds, &wfds, &efds, &tv); - timeLeft = expires - time(0); - events = 0; - if (FD_ISSET(fd, &rfds)) { - events |= ZOOKEEPER_READ; - } - if (FD_ISSET(fd, &wfds)) { - events |= ZOOKEEPER_WRITE; - } - zookeeper_process(zh, events); - } - } -#endif - -typedef struct evt { - string path; - int type; -} evt_t; - -typedef struct watchCtx { -private: - list events; - watchCtx(const watchCtx&); - watchCtx& operator=(const watchCtx&); -public: - bool connected; - zhandle_t *zh; - Mutex mutex; - - watchCtx() { - connected = false; - zh = 0; - } - ~watchCtx() { - if (zh) { - zookeeper_close(zh); - zh = 0; - } - } - - evt_t getEvent() { - evt_t evt; - mutex.acquire(); - CPPUNIT_ASSERT( events.size() > 0); - evt = events.front(); - events.pop_front(); - mutex.release(); - return evt; - } - - int countEvents() { - int count; - mutex.acquire(); - count = events.size(); - mutex.release(); - return count; - } - - void putEvent(evt_t evt) { - mutex.acquire(); - events.push_back(evt); - mutex.release(); - } - - bool waitForConnected(zhandle_t *zh) { - time_t expires = time(0) + 10; - while(!connected && time(0) < expires) { - yield(zh, 1); - } - return connected; - } - bool waitForDisconnected(zhandle_t *zh) { - time_t expires = time(0) + 15; - while(connected && time(0) < expires) { - yield(zh, 1); - } - return !connected; - } -} watchctx_t; - -class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture -{ - CPPUNIT_TEST_SUITE(Zookeeper_simpleSystem); - CPPUNIT_TEST(testAsyncWatcherAutoReset); - CPPUNIT_TEST(testDeserializeString); -#ifdef THREADED - CPPUNIT_TEST(testNullData); -#ifdef ZOO_IPV6_ENABLED - CPPUNIT_TEST(testIPV6); -#endif - CPPUNIT_TEST(testPath); - CPPUNIT_TEST(testPathValidation); - CPPUNIT_TEST(testPing); - CPPUNIT_TEST(testAcl); - CPPUNIT_TEST(testChroot); - CPPUNIT_TEST(testAuth); - CPPUNIT_TEST(testHangingClient); - CPPUNIT_TEST(testWatcherAutoResetWithGlobal); - CPPUNIT_TEST(testWatcherAutoResetWithLocal); - CPPUNIT_TEST(testGetChildren2); -#endif - CPPUNIT_TEST_SUITE_END(); - - static void watcher(zhandle_t *, int type, int state, const char *path,void*v){ - watchctx_t *ctx = (watchctx_t*)v; - - if (state == ZOO_CONNECTED_STATE) { - ctx->connected = true; - } else { - ctx->connected = false; - } - if (type != ZOO_SESSION_EVENT) { - evt_t evt; - evt.path = path; - evt.type = type; - ctx->putEvent(evt); - } - } - - static const char hostPorts[]; - - const char *getHostPorts() { - return hostPorts; - } - - zhandle_t *createClient(watchctx_t *ctx) { - return createClient(hostPorts, ctx); - } - - zhandle_t *createClient(const char *hp, watchctx_t *ctx) { - zhandle_t *zk = zookeeper_init(hp, watcher, 10000, 0, ctx, 0); - ctx->zh = zk; - sleep(1); - return zk; - } - - zhandle_t *createchClient(watchctx_t *ctx, const char* chroot) { - zhandle_t *zk = zookeeper_init(chroot, watcher, 10000, 0, ctx, 0); - ctx->zh = zk; - sleep(1); - return zk; - } - - FILE *logfile; -public: - - Zookeeper_simpleSystem() { - logfile = openlogfile("Zookeeper_simpleSystem"); - } - - ~Zookeeper_simpleSystem() { - if (logfile) { - fflush(logfile); - fclose(logfile); - logfile = 0; - } - } - - void setUp() - { - zoo_set_log_stream(logfile); - } - - - void startServer() { - char cmd[1024]; - sprintf(cmd, "%s start %s", ZKSERVER_CMD, getHostPorts()); - CPPUNIT_ASSERT(system(cmd) == 0); - } - - void stopServer() { - char cmd[1024]; - sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts()); - CPPUNIT_ASSERT(system(cmd) == 0); - } - - void tearDown() - { - } - - /** have a callback in the default watcher **/ - static void default_zoo_watcher(zhandle_t *zzh, int type, int state, const char *path, void *context){ - int zrc = 0; - struct String_vector str_vec = {0, NULL}; - zrc = zoo_wget_children(zzh, "/mytest", default_zoo_watcher, NULL, &str_vec); - } - - /** this checks for a deadlock in calling zookeeper_close and calls from a default watcher that might get triggered just when zookeeper_close() is in progress **/ - void testHangingClient() { - int zrc = 0; - char buff[10] = "testall"; - char path[512]; - watchctx_t *ctx; - struct String_vector str_vec = {0, NULL}; - zhandle_t *zh = zookeeper_init(hostPorts, NULL, 10000, 0, ctx, 0); - sleep(1); - zrc = zoo_create(zh, "/mytest", buff, 10, &ZOO_OPEN_ACL_UNSAFE, 0, path, 512); - zrc = zoo_wget_children(zh, "/mytest", default_zoo_watcher, NULL, &str_vec); - zrc = zoo_create(zh, "/mytest/test1", buff, 10, &ZOO_OPEN_ACL_UNSAFE, 0, path, 512); - zrc = zoo_wget_children(zh, "/mytest", default_zoo_watcher, NULL, &str_vec); - zrc = zoo_delete(zh, "/mytest/test1", -1); - zookeeper_close(zh); - } - - - void testPing() - { - watchctx_t ctxIdle; - watchctx_t ctxWC; - zhandle_t *zkIdle = createClient(&ctxIdle); - zhandle_t *zkWatchCreator = createClient(&ctxWC); - - CPPUNIT_ASSERT(zkIdle); - CPPUNIT_ASSERT(zkWatchCreator); - - char path[80]; - sprintf(path, "/testping"); - int rc = zoo_create(zkWatchCreator, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - for(int i = 0; i < 30; i++) { - sprintf(path, "/testping/%i", i); - rc = zoo_create(zkWatchCreator, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - - for(int i = 0; i < 30; i++) { - sprintf(path, "/testping/%i", i); - struct Stat stat; - rc = zoo_exists(zkIdle, path, 1, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - - for(int i = 0; i < 30; i++) { - sprintf(path, "/testping/%i", i); - usleep(500000); - rc = zoo_delete(zkWatchCreator, path, -1); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - struct Stat stat; - CPPUNIT_ASSERT_EQUAL((int)ZNONODE, zoo_exists(zkIdle, "/testping/0", 0, &stat)); - } - - bool waitForEvent(zhandle_t *zh, watchctx_t *ctx, int seconds) { - time_t expires = time(0) + seconds; - while(ctx->countEvents() == 0 && time(0) < expires) { - yield(zh, 1); - } - return ctx->countEvents() > 0; - } - -#define COUNT 100 - - static zhandle_t *async_zk; - static volatile int count; - static const char* hp_chroot; - - static void statCompletion(int rc, const struct Stat *stat, const void *data) { - int tmp = (int) (long) data; - CPPUNIT_ASSERT_EQUAL(tmp, rc); - } - - static void stringCompletion(int rc, const char *value, const void *data) { - char *path = (char*)data; - - if (rc == ZCONNECTIONLOSS && path) { - // Try again - rc = zoo_acreate(async_zk, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, stringCompletion, 0); - } else if (rc != ZOK) { - // fprintf(stderr, "rc = %d with path = %s\n", rc, (path ? path : "null")); - } - if (path) { - free(path); - } - } - - static void create_completion_fn(int rc, const char* value, const void *data) { - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - count++; - } - - static void waitForCreateCompletion(int seconds) { - time_t expires = time(0) + seconds; - while(count == 0 && time(0) < expires) { - sleep(1); - } - count--; - } - - static void watcher_chroot_fn(zhandle_t *zh, int type, - int state, const char *path,void *watcherCtx) { - // check for path - char *client_path = (char *) watcherCtx; - CPPUNIT_ASSERT(strcmp(client_path, path) == 0); - count ++; - } - - static void waitForChrootWatch(int seconds) { - time_t expires = time(0) + seconds; - while (count == 0 && time(0) < expires) { - sleep(1); - } - count--; - } - - static void waitForVoidCompletion(int seconds) { - time_t expires = time(0) + seconds; - while(count == 0 && time(0) < expires) { - sleep(1); - } - count--; - } - - static void voidCompletion(int rc, const void *data) { - int tmp = (int) (long) data; - CPPUNIT_ASSERT_EQUAL(tmp, rc); - count++; - } - - static void verifyCreateFails(const char *path, zhandle_t *zk) { - CPPUNIT_ASSERT_EQUAL((int)ZBADARGUMENTS, zoo_create(zk, - path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0)); - } - - static void verifyCreateOk(const char *path, zhandle_t *zk) { - CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_create(zk, - path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0)); - } - - static void verifyCreateFailsSeq(const char *path, zhandle_t *zk) { - CPPUNIT_ASSERT_EQUAL((int)ZBADARGUMENTS, zoo_create(zk, - path, "", 0, &ZOO_OPEN_ACL_UNSAFE, ZOO_SEQUENCE, 0, 0)); - } - - static void verifyCreateOkSeq(const char *path, zhandle_t *zk) { - CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_create(zk, - path, "", 0, &ZOO_OPEN_ACL_UNSAFE, ZOO_SEQUENCE, 0, 0)); - } - - - /** - returns false if the vectors dont match - **/ - bool compareAcl(struct ACL_vector acl1, struct ACL_vector acl2) { - if (acl1.count != acl2.count) { - return false; - } - struct ACL *aclval1 = acl1.data; - struct ACL *aclval2 = acl2.data; - if (aclval1->perms != aclval2->perms) { - return false; - } - struct Id id1 = aclval1->id; - struct Id id2 = aclval2->id; - if (strcmp(id1.scheme, id2.scheme) != 0) { - return false; - } - if (strcmp(id1.id, id2.id) != 0) { - return false; - } - return true; - } - - void testDeserializeString() { - char *val_str; - int rc = 0; - int val = -1; - struct iarchive *ia; - struct buff_struct_2 *b; - struct oarchive *oa = create_buffer_oarchive(); - oa->serialize_Int(oa, "int", &val); - b = (struct buff_struct_2 *) oa->priv; - ia = create_buffer_iarchive(b->buffer, b->len); - rc = ia->deserialize_String(ia, "string", &val_str); - CPPUNIT_ASSERT_EQUAL(-EINVAL, rc); - } - - void testAcl() { - int rc; - struct ACL_vector aclvec; - struct Stat stat; - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - rc = zoo_create(zk, "/acl", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_get_acl(zk, "/acl", &aclvec, &stat ); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - bool cmp = compareAcl(ZOO_OPEN_ACL_UNSAFE, aclvec); - CPPUNIT_ASSERT_EQUAL(true, cmp); - rc = zoo_set_acl(zk, "/acl", -1, &ZOO_READ_ACL_UNSAFE); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_get_acl(zk, "/acl", &aclvec, &stat); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - cmp = compareAcl(ZOO_READ_ACL_UNSAFE, aclvec); - CPPUNIT_ASSERT_EQUAL(true, cmp); - } - - - void testAuth() { - int rc; - count = 0; - watchctx_t ctx1, ctx2, ctx3, ctx4; - zhandle_t *zk = createClient(&ctx1); - struct ACL_vector nodeAcl; - struct ACL acl_val; - rc = zoo_add_auth(0, "", 0, 0, voidCompletion, (void*)-1); - CPPUNIT_ASSERT_EQUAL((int) ZBADARGUMENTS, rc); - - rc = zoo_add_auth(zk, 0, 0, 0, voidCompletion, (void*)-1); - CPPUNIT_ASSERT_EQUAL((int) ZBADARGUMENTS, rc); - - // auth as pat, create /tauth1, close session - rc = zoo_add_auth(zk, "digest", "pat:passwd", 10, voidCompletion, - (void*)ZOK); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForVoidCompletion(3); - - CPPUNIT_ASSERT(count == 0); - - rc = zoo_create(zk, "/tauth1", "", 0, &ZOO_CREATOR_ALL_ACL, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - { - //create a new client - zk = createClient(&ctx4); - rc = zoo_add_auth(zk, "digest", "", 0, voidCompletion, (void*)ZOK); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForVoidCompletion(3); - CPPUNIT_ASSERT(count == 0); - - rc = zoo_add_auth(zk, "digest", "", 0, voidCompletion, (void*)ZOK); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForVoidCompletion(3); - CPPUNIT_ASSERT(count == 0); - } - - //create a new client - zk = createClient(&ctx2); - - rc = zoo_add_auth(zk, "digest", "pat:passwd2", 11, voidCompletion, - (void*)ZOK); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForVoidCompletion(3); - CPPUNIT_ASSERT(count == 0); - - char buf[1024]; - int blen = sizeof(buf); - struct Stat stat; - rc = zoo_get(zk, "/tauth1", 0, buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, rc); - // add auth pat w/correct pass verify success - rc = zoo_add_auth(zk, "digest", "pat:passwd", 10, voidCompletion, - (void*)ZOK); - - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_get(zk, "/tauth1", 0, buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - waitForVoidCompletion(3); - CPPUNIT_ASSERT(count == 0); - //create a new client - zk = createClient(&ctx3); - rc = zoo_add_auth(zk, "digest", "pat:passwd", 10, voidCompletion, (void*) ZOK); - waitForVoidCompletion(3); - CPPUNIT_ASSERT(count == 0); - rc = zoo_add_auth(zk, "ip", "none", 4, voidCompletion, (void*)ZOK); - //make the server forget the auths - waitForVoidCompletion(3); - CPPUNIT_ASSERT(count == 0); - - stopServer(); - CPPUNIT_ASSERT(ctx3.waitForDisconnected(zk)); - startServer(); - CPPUNIT_ASSERT(ctx3.waitForConnected(zk)); - // now try getting the data - rc = zoo_get(zk, "/tauth1", 0, buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - // also check for get - rc = zoo_get_acl(zk, "/", &nodeAcl, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - // check if the acl has all the perms - CPPUNIT_ASSERT_EQUAL((int)1, (int)nodeAcl.count); - acl_val = *(nodeAcl.data); - CPPUNIT_ASSERT_EQUAL((int) acl_val.perms, ZOO_PERM_ALL); - // verify on root node - rc = zoo_set_acl(zk, "/", -1, &ZOO_CREATOR_ALL_ACL); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - - rc = zoo_set_acl(zk, "/", -1, &ZOO_OPEN_ACL_UNSAFE); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - } - - void testGetChildren2() { - int rc; - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - - rc = zoo_create(zk, "/parent", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - rc = zoo_create(zk, "/parent/child_a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - rc = zoo_create(zk, "/parent/child_b", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - rc = zoo_create(zk, "/parent/child_c", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - rc = zoo_create(zk, "/parent/child_d", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - struct String_vector strings; - struct Stat stat_a, stat_b; - - rc = zoo_get_children2(zk, "/parent", 0, &strings, &stat_a); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - rc = zoo_exists(zk, "/parent", 0, &stat_b); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - CPPUNIT_ASSERT(Stat_eq(&stat_a, &stat_b)); - CPPUNIT_ASSERT(stat_a.numChildren == 4); - } - - void testIPV6() { - watchctx_t ctx; - zhandle_t *zk = createClient("::1:22181", &ctx); - CPPUNIT_ASSERT(zk); - int rc = 0; - rc = zoo_create(zk, "/ipv6", NULL, -1, - &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - } - - void testNullData() { - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - CPPUNIT_ASSERT(zk); - int rc = 0; - rc = zoo_create(zk, "/mahadev", NULL, -1, - &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - char buffer[512]; - struct Stat stat; - int len = 512; - rc = zoo_wget(zk, "/mahadev", NULL, NULL, buffer, &len, &stat); - CPPUNIT_ASSERT_EQUAL( -1, len); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_set(zk, "/mahadev", NULL, -1, -1); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_wget(zk, "/mahadev", NULL, NULL, buffer, &len, &stat); - CPPUNIT_ASSERT_EQUAL( -1, len); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - } - - void testPath() { - watchctx_t ctx; - char pathbuf[20]; - zhandle_t *zk = createClient(&ctx); - CPPUNIT_ASSERT(zk); - int rc = 0; - - memset(pathbuf, 'X', 20); - rc = zoo_create(zk, "/testpathpath0", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - CPPUNIT_ASSERT_EQUAL('X', pathbuf[0]); - - rc = zoo_create(zk, "/testpathpath1", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, 1); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - CPPUNIT_ASSERT(strlen(pathbuf) == 0); - - rc = zoo_create(zk, "/testpathpath2", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, 2); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - CPPUNIT_ASSERT(strcmp(pathbuf, "/") == 0); - - rc = zoo_create(zk, "/testpathpath3", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, 3); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - CPPUNIT_ASSERT(strcmp(pathbuf, "/t") == 0); - - rc = zoo_create(zk, "/testpathpath7", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, 15); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - CPPUNIT_ASSERT(strcmp(pathbuf, "/testpathpath7") == 0); - - rc = zoo_create(zk, "/testpathpath8", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, 16); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - CPPUNIT_ASSERT(strcmp(pathbuf, "/testpathpath8") == 0); - } - - void testPathValidation() { - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - CPPUNIT_ASSERT(zk); - - verifyCreateFails(0, zk); - verifyCreateFails("", zk); - verifyCreateFails("//", zk); - verifyCreateFails("///", zk); - verifyCreateFails("////", zk); - verifyCreateFails("/.", zk); - verifyCreateFails("/..", zk); - verifyCreateFails("/./", zk); - verifyCreateFails("/../", zk); - verifyCreateFails("/foo/./", zk); - verifyCreateFails("/foo/../", zk); - verifyCreateFails("/foo/.", zk); - verifyCreateFails("/foo/..", zk); - verifyCreateFails("/./.", zk); - verifyCreateFails("/../..", zk); - verifyCreateFails("/foo/bar/", zk); - verifyCreateFails("/foo//bar", zk); - verifyCreateFails("/foo/bar//", zk); - - verifyCreateFails("foo", zk); - verifyCreateFails("a", zk); - - // verify that trailing fails, except for seq which adds suffix - verifyCreateOk("/createseq", zk); - verifyCreateFails("/createseq/", zk); - verifyCreateOkSeq("/createseq/", zk); - verifyCreateOkSeq("/createseq/.", zk); - verifyCreateOkSeq("/createseq/..", zk); - verifyCreateFailsSeq("/createseq//", zk); - verifyCreateFailsSeq("/createseq/./", zk); - verifyCreateFailsSeq("/createseq/../", zk); - - verifyCreateOk("/.foo", zk); - verifyCreateOk("/.f.", zk); - verifyCreateOk("/..f", zk); - verifyCreateOk("/..f..", zk); - verifyCreateOk("/f.c", zk); - verifyCreateOk("/f", zk); - verifyCreateOk("/f/.f", zk); - verifyCreateOk("/f/f.", zk); - verifyCreateOk("/f/..f", zk); - verifyCreateOk("/f/f..", zk); - verifyCreateOk("/f/.f/f", zk); - verifyCreateOk("/f/f./f", zk); - } - - void testChroot() { - // the c client async callbacks do - // not callback with the path, so - // we dont need to test taht for now - // we should fix that though soon! - watchctx_t ctx, ctx_ch; - zhandle_t *zk, *zk_ch; - char buf[60]; - int rc, len; - struct Stat stat; - const char* data = "garbage"; - const char* retStr = "/chroot"; - const char* root= "/"; - zk_ch = createchClient(&ctx_ch, "127.0.0.1:22181/testch1/mahadev"); - CPPUNIT_ASSERT(zk_ch != NULL); - zk = createClient(&ctx); - rc = zoo_create(zk, "/testch1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_create(zk, "/testch1/mahadev", data, 7, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - // try an exists with / - len = 60; - rc = zoo_get(zk_ch, "/", 0, buf, &len, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - //check if the data is the same - CPPUNIT_ASSERT(strncmp(buf, data, 7) == 0); - //check for watches - rc = zoo_wexists(zk_ch, "/chroot", watcher_chroot_fn, (void *) retStr, &stat); - //now check if we can do create/delete/get/sets/acls/getChildren and others - //check create - rc = zoo_create(zk_ch, "/chroot", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0,0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForChrootWatch(3); - CPPUNIT_ASSERT(count == 0); - rc = zoo_create(zk_ch, "/chroot/child", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_exists(zk, "/testch1/mahadev/chroot/child", 0, &stat); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - - rc = zoo_delete(zk_ch, "/chroot/child", -1); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_exists(zk, "/testch1/mahadev/chroot/child", 0, &stat); - CPPUNIT_ASSERT_EQUAL((int) ZNONODE, rc); - rc = zoo_wget(zk_ch, "/chroot", watcher_chroot_fn, (char*) retStr, - buf, &len, &stat); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_set(zk_ch, "/chroot",buf, 3, -1); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForChrootWatch(3); - CPPUNIT_ASSERT(count == 0); - // check for getchildren - struct String_vector children; - rc = zoo_get_children(zk_ch, "/", 0, &children); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - CPPUNIT_ASSERT_EQUAL((int)1, (int)children.count); - //check if te child if chroot - CPPUNIT_ASSERT(strcmp((retStr+1), children.data[0]) == 0); - // check for get/set acl - struct ACL_vector acl; - rc = zoo_get_acl(zk_ch, "/", &acl, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - CPPUNIT_ASSERT_EQUAL((int)1, (int)acl.count); - CPPUNIT_ASSERT_EQUAL((int)ZOO_PERM_ALL, (int)acl.data->perms); - // set acl - rc = zoo_set_acl(zk_ch, "/chroot", -1, &ZOO_READ_ACL_UNSAFE); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - // see if you add children - rc = zoo_create(zk_ch, "/chroot/child1", "",0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, rc); - //add wget children test - rc = zoo_wget_children(zk_ch, "/", watcher_chroot_fn, (char*) root, &children); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - //now create a node - rc = zoo_create(zk_ch, "/child2", "",0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - waitForChrootWatch(3); - CPPUNIT_ASSERT(count == 0); - //check for one async call just to make sure - rc = zoo_acreate(zk_ch, "/child3", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, - create_completion_fn, 0); - waitForCreateCompletion(3); - CPPUNIT_ASSERT(count == 0); - } - - void testAsyncWatcherAutoReset() - { - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - watchctx_t lctx[COUNT]; - int i; - char path[80]; - int rc; - evt_t evt; - - async_zk = zk; - for(i = 0; i < COUNT; i++) { - sprintf(path, "/awar%d", i); - rc = zoo_awexists(zk, path, watcher, &lctx[i], statCompletion, (void*)ZNONODE); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - - yield(zk, 0); - - for(i = 0; i < COUNT/2; i++) { - sprintf(path, "/awar%d", i); - rc = zoo_acreate(zk, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, stringCompletion, strdup(path)); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - - yield(zk, 3); - for(i = 0; i < COUNT/2; i++) { - sprintf(path, "/awar%d", i); - CPPUNIT_ASSERT_MESSAGE(path, waitForEvent(zk, &lctx[i], 5)); - evt = lctx[i].getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path.c_str(), ZOO_CREATED_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string(path), evt.path); - } - - for(i = COUNT/2 + 1; i < COUNT*10; i++) { - sprintf(path, "/awar%d", i); - rc = zoo_acreate(zk, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, stringCompletion, strdup(path)); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - - yield(zk, 1); - stopServer(); - CPPUNIT_ASSERT(ctx.waitForDisconnected(zk)); - startServer(); - CPPUNIT_ASSERT(ctx.waitForConnected(zk)); - yield(zk, 3); - for(i = COUNT/2+1; i < COUNT; i++) { - sprintf(path, "/awar%d", i); - CPPUNIT_ASSERT_MESSAGE(path, waitForEvent(zk, &lctx[i], 5)); - evt = lctx[i].getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_CREATED_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string(path), evt.path); - } - } - - void testWatcherAutoReset(zhandle_t *zk, watchctx_t *ctxGlobal, - watchctx_t *ctxLocal) - { - bool isGlobal = (ctxGlobal == ctxLocal); - int rc; - struct Stat stat; - char buf[1024]; - int blen; - struct String_vector strings; - const char *testName; - - rc = zoo_create(zk, "/watchtest", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_create(zk, "/watchtest/child", "", 0, - &ZOO_OPEN_ACL_UNSAFE, ZOO_EPHEMERAL, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - if (isGlobal) { - testName = "GlobalTest"; - rc = zoo_get_children(zk, "/watchtest", 1, &strings); - deallocate_String_vector(&strings); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - blen = sizeof(buf); - rc = zoo_get(zk, "/watchtest/child", 1, buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_exists(zk, "/watchtest/child2", 1, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); - } else { - testName = "LocalTest"; - rc = zoo_wget_children(zk, "/watchtest", watcher, ctxLocal, - &strings); - deallocate_String_vector(&strings); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - blen = sizeof(buf); - rc = zoo_wget(zk, "/watchtest/child", watcher, ctxLocal, - buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_wexists(zk, "/watchtest/child2", watcher, ctxLocal, - &stat); - CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); - } - - CPPUNIT_ASSERT(ctxLocal->countEvents() == 0); - - stopServer(); - CPPUNIT_ASSERT_MESSAGE(testName, ctxGlobal->waitForDisconnected(zk)); - startServer(); - CPPUNIT_ASSERT_MESSAGE(testName, ctxLocal->waitForConnected(zk)); - - CPPUNIT_ASSERT(ctxLocal->countEvents() == 0); - - rc = zoo_set(zk, "/watchtest/child", "1", 1, -1); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - struct Stat stat1, stat2; - rc = zoo_set2(zk, "/watchtest/child", "1", 1, -1, &stat1); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - CPPUNIT_ASSERT(stat1.version >= 0); - rc = zoo_set2(zk, "/watchtest/child", "1", 1, stat1.version, &stat2); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_set(zk, "/watchtest/child", "1", 1, stat2.version); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_create(zk, "/watchtest/child2", "", 0, - &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - - CPPUNIT_ASSERT_MESSAGE(testName, waitForEvent(zk, ctxLocal, 5)); - - evt_t evt = ctxLocal->getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_CHANGED_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string("/watchtest/child"), evt.path); - - CPPUNIT_ASSERT_MESSAGE(testName, waitForEvent(zk, ctxLocal, 5)); - // The create will trigget the get children and the - // exists watches - evt = ctxLocal->getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_CREATED_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string("/watchtest/child2"), evt.path); - CPPUNIT_ASSERT_MESSAGE(testName, waitForEvent(zk, ctxLocal, 5)); - evt = ctxLocal->getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_CHILD_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string("/watchtest"), evt.path); - - // Make sure Pings are giving us problems - sleep(5); - - CPPUNIT_ASSERT(ctxLocal->countEvents() == 0); - - stopServer(); - CPPUNIT_ASSERT_MESSAGE(testName, ctxGlobal->waitForDisconnected(zk)); - startServer(); - CPPUNIT_ASSERT_MESSAGE(testName, ctxGlobal->waitForConnected(zk)); - - if (isGlobal) { - testName = "GlobalTest"; - rc = zoo_get_children(zk, "/watchtest", 1, &strings); - deallocate_String_vector(&strings); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - blen = sizeof(buf); - rc = zoo_get(zk, "/watchtest/child", 1, buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_exists(zk, "/watchtest/child2", 1, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } else { - testName = "LocalTest"; - rc = zoo_wget_children(zk, "/watchtest", watcher, ctxLocal, - &strings); - deallocate_String_vector(&strings); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - blen = sizeof(buf); - rc = zoo_wget(zk, "/watchtest/child", watcher, ctxLocal, - buf, &blen, &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - rc = zoo_wexists(zk, "/watchtest/child2", watcher, ctxLocal, - &stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); - } - - zoo_delete(zk, "/watchtest/child2", -1); - - CPPUNIT_ASSERT_MESSAGE(testName, waitForEvent(zk, ctxLocal, 5)); - - evt = ctxLocal->getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_DELETED_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string("/watchtest/child2"), evt.path); - - CPPUNIT_ASSERT_MESSAGE(testName, waitForEvent(zk, ctxLocal, 5)); - evt = ctxLocal->getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_CHILD_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string("/watchtest"), evt.path); - - stopServer(); - CPPUNIT_ASSERT_MESSAGE(testName, ctxGlobal->waitForDisconnected(zk)); - startServer(); - CPPUNIT_ASSERT_MESSAGE(testName, ctxLocal->waitForConnected(zk)); - - zoo_delete(zk, "/watchtest/child", -1); - zoo_delete(zk, "/watchtest", -1); - - CPPUNIT_ASSERT_MESSAGE(testName, waitForEvent(zk, ctxLocal, 5)); - - evt = ctxLocal->getEvent(); - CPPUNIT_ASSERT_EQUAL_MESSAGE(evt.path, ZOO_DELETED_EVENT, evt.type); - CPPUNIT_ASSERT_EQUAL(string("/watchtest/child"), evt.path); - - // Make sure nothing is straggling - sleep(1); - CPPUNIT_ASSERT(ctxLocal->countEvents() == 0); - } - - void testWatcherAutoResetWithGlobal() - { - { - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - int rc = zoo_create(zk, "/testarwg", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_create(zk, "/testarwg/arwg", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - } - - { - watchctx_t ctx; - zhandle_t *zk = createchClient(&ctx, "127.0.0.1:22181/testarwg/arwg"); - - testWatcherAutoReset(zk, &ctx, &ctx); - } - } - - void testWatcherAutoResetWithLocal() - { - { - watchctx_t ctx; - zhandle_t *zk = createClient(&ctx); - int rc = zoo_create(zk, "/testarwl", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - rc = zoo_create(zk, "/testarwl/arwl", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); - } - - { - watchctx_t ctx; - watchctx_t lctx; - zhandle_t *zk = createchClient(&ctx, "127.0.0.1:22181/testarwl/arwl"); - testWatcherAutoReset(zk, &ctx, &lctx); - } - } -}; - -volatile int Zookeeper_simpleSystem::count; -zhandle_t *Zookeeper_simpleSystem::async_zk; -const char Zookeeper_simpleSystem::hostPorts[] = "127.0.0.1:22181"; -CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_simpleSystem); diff --git a/src/c/tests/TestClientRetry.cc b/src/c/tests/TestClientRetry.cc deleted file mode 100644 index 41d51798648..00000000000 --- a/src/c/tests/TestClientRetry.cc +++ /dev/null @@ -1,273 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CppAssertHelper.h" - -#include -#include -#include -#include - -#include "CollectionUtil.h" -#include "ThreadingUtil.h" - -using namespace Util; - -#include "Vector.h" -using namespace std; - -#include -#include - -#include - -#include "Util.h" - -#ifdef THREADED - static void yield(zhandle_t *zh, int i) - { - sleep(i); - } -#else - static void yield(zhandle_t *zh, int seconds) - { - int fd; - int interest; - int events; - struct timeval tv; - int rc; - time_t expires = time(0) + seconds; - time_t timeLeft = seconds; - fd_set rfds, wfds, efds; - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - - while(timeLeft >= 0) { - zookeeper_interest(zh, &fd, &interest, &tv); - if (fd != -1) { - if (interest&ZOOKEEPER_READ) { - FD_SET(fd, &rfds); - } else { - FD_CLR(fd, &rfds); - } - if (interest&ZOOKEEPER_WRITE) { - FD_SET(fd, &wfds); - } else { - FD_CLR(fd, &wfds); - } - } else { - fd = 0; - } - FD_SET(0, &rfds); - if (tv.tv_sec > timeLeft) { - tv.tv_sec = timeLeft; - } - rc = select(fd+1, &rfds, &wfds, &efds, &tv); - timeLeft = expires - time(0); - events = 0; - if (FD_ISSET(fd, &rfds)) { - events |= ZOOKEEPER_READ; - } - if (FD_ISSET(fd, &wfds)) { - events |= ZOOKEEPER_WRITE; - } - zookeeper_process(zh, events); - } - } -#endif - -typedef struct evt { - string path; - int type; -} evt_t; - -typedef struct watchCtx { -private: - list events; -public: - bool connected; - zhandle_t *zh; - Mutex mutex; - - watchCtx() { - connected = false; - zh = 0; - } - ~watchCtx() { - if (zh) { - zookeeper_close(zh); - zh = 0; - } - } - - evt_t getEvent() { - evt_t evt; - mutex.acquire(); - CPPUNIT_ASSERT( events.size() > 0); - evt = events.front(); - events.pop_front(); - mutex.release(); - return evt; - } - - int countEvents() { - int count; - mutex.acquire(); - count = events.size(); - mutex.release(); - return count; - } - - void putEvent(evt_t evt) { - mutex.acquire(); - events.push_back(evt); - mutex.release(); - } - - bool waitForConnected(zhandle_t *zh) { - time_t expires = time(0) + 10; - while(!connected && time(0) < expires) { - yield(zh, 1); - } - return connected; - } - bool waitForDisconnected(zhandle_t *zh) { - time_t expires = time(0) + 15; - while(connected && time(0) < expires) { - yield(zh, 1); - } - return !connected; - } -} watchctx_t; - -class Zookeeper_clientretry : public CPPUNIT_NS::TestFixture -{ - CPPUNIT_TEST_SUITE(Zookeeper_clientretry); -#ifdef THREADED - CPPUNIT_TEST(testRetry); -#endif - CPPUNIT_TEST_SUITE_END(); - - static void watcher(zhandle_t *, int type, int state, const char *path,void*v){ - watchctx_t *ctx = (watchctx_t*)v; - - if (state == ZOO_CONNECTED_STATE) { - ctx->connected = true; - } else { - ctx->connected = false; - } - if (type != ZOO_SESSION_EVENT) { - evt_t evt; - evt.path = path; - evt.type = type; - ctx->putEvent(evt); - } - } - - static const char hostPorts[]; - - const char *getHostPorts() { - return hostPorts; - } - - zhandle_t *createClient(watchctx_t *ctx) { - zhandle_t *zk = zookeeper_init(hostPorts, watcher, 10000, 0, - ctx, 0); - ctx->zh = zk; - sleep(1); - return zk; - } - - FILE *logfile; -public: - - Zookeeper_clientretry() { - logfile = openlogfile("Zookeeper_clientretry"); - } - - ~Zookeeper_clientretry() { - if (logfile) { - fflush(logfile); - fclose(logfile); - logfile = 0; - } - } - - void setUp() - { - zoo_set_log_stream(logfile); - - char cmd[1024]; - sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts()); - CPPUNIT_ASSERT(system(cmd) == 0); - - /* we are testing that if max cnxns is exceeded the server does the right thing */ - sprintf(cmd, "export ZKMAXCNXNS=1;%s startClean %s", ZKSERVER_CMD, getHostPorts()); - CPPUNIT_ASSERT(system(cmd) == 0); - - struct sigaction act; - act.sa_handler = SIG_IGN; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - CPPUNIT_ASSERT(sigaction(SIGPIPE, &act, NULL) == 0); - } - - void tearDown() - { - char cmd[1024]; - sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts()); - CPPUNIT_ASSERT(system(cmd) == 0); - - /* restart the server in "normal" mode */ - sprintf(cmd, "%s startClean %s", ZKSERVER_CMD, getHostPorts()); - CPPUNIT_ASSERT(system(cmd) == 0); - - struct sigaction act; - act.sa_handler = SIG_IGN; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - CPPUNIT_ASSERT(sigaction(SIGPIPE, &act, NULL) == 0); - } - - bool waitForEvent(zhandle_t *zh, watchctx_t *ctx, int seconds) { - time_t expires = time(0) + seconds; - while(ctx->countEvents() == 0 && time(0) < expires) { - yield(zh, 1); - } - return ctx->countEvents() > 0; - } - - static zhandle_t *async_zk; - - void testRetry() - { - watchctx_t ctx1, ctx2; - zhandle_t *zk1 = createClient(&ctx1); - CPPUNIT_ASSERT_EQUAL(true, ctx1.waitForConnected(zk1)); - zhandle_t *zk2 = createClient(&ctx2); - zookeeper_close(zk1); - CPPUNIT_ASSERT_EQUAL(true, ctx2.waitForConnected(zk2)); - ctx1.zh = 0; - } -}; - -zhandle_t *Zookeeper_clientretry::async_zk; -const char Zookeeper_clientretry::hostPorts[] = "127.0.0.1:22181"; -CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_clientretry); diff --git a/src/c/tests/TestDriver.cc b/src/c/tests/TestDriver.cc deleted file mode 100644 index 3f32ba483c4..00000000000 --- a/src/c/tests/TestDriver.cc +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Util.h" -#include "zookeeper_log.h" - -using namespace std; - -CPPUNIT_NS_BEGIN - -class EclipseOutputter: public CompilerOutputter -{ -public: - EclipseOutputter(TestResultCollector *result,ostream &stream): - CompilerOutputter(result,stream,"%p:%l: "),stream_(stream) - { - } - virtual void printFailedTestName( TestFailure *failure ){} - virtual void printFailureMessage( TestFailure *failure ) - { - stream_<<": "; - Message msg = failure->thrownException()->message(); - stream_<< msg.shortDescription(); - - string text; - for(int i=0; i the output must be in the compiler error format. - //bool selfTest = (argc > 1) && (std::string("-ide") == argv[1]); - globalTestConfig.addConfigFromCmdLine(argc,argv); - - ZKServer zkserver; - - // Create the event manager and test controller - CPPUNIT_NS::TestResult controller; - // Add a listener that colllects test result - CPPUNIT_NS::TestResultCollector result; - controller.addListener( &result ); - - // A listener that print dots as tests run. - // CPPUNIT_NS::TextTestProgressListener progress; - // CPPUNIT_NS::BriefTestProgressListener progress; - - // brief + elapsed time - TimingListener progress; - controller.addListener( &progress ); - - CPPUNIT_NS::TestRunner runner; - runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() ); - - try { - CPPUNIT_NS::stdCOut() << "Running " << endl; - - zoo_set_debug_level(ZOO_LOG_LEVEL_INFO); - //zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); - - runner.run( controller, globalTestConfig.getTestName()); - - // Print test in a compiler compatible format. - CPPUNIT_NS::EclipseOutputter outputter( &result,cout); - outputter.write(); - - // Uncomment this for XML output -#ifdef ENABLE_XML_OUTPUT - std::ofstream file( "tests.xml" ); - CPPUNIT_NS::XmlOutputter xml( &result, file ); - xml.setStyleSheet( "report.xsl" ); - xml.write(); - file.close(); -#endif - } catch ( std::invalid_argument &e ) { - // Test path not resolved - cout<<"\nERROR: "< -#include "CppAssertHelper.h" - -#include "ZKMocks.h" -#include - -using namespace std; - -class Zookeeper_operations : public CPPUNIT_NS::TestFixture -{ - CPPUNIT_TEST_SUITE(Zookeeper_operations); -#ifndef THREADED - CPPUNIT_TEST(testPing); - CPPUNIT_TEST(testTimeoutCausedByWatches1); - CPPUNIT_TEST(testTimeoutCausedByWatches2); -#else - CPPUNIT_TEST(testAsyncWatcher1); - CPPUNIT_TEST(testAsyncGetOperation); -#endif - CPPUNIT_TEST(testOperationsAndDisconnectConcurrently1); - CPPUNIT_TEST(testOperationsAndDisconnectConcurrently2); - CPPUNIT_TEST(testConcurrentOperations1); - CPPUNIT_TEST_SUITE_END(); - zhandle_t *zh; - FILE *logfile; - - static void watcher(zhandle_t *, int, int, const char *,void*){} -public: - Zookeeper_operations() { - logfile = openlogfile("Zookeeper_operations"); - } - - ~Zookeeper_operations() { - if (logfile) { - fflush(logfile); - fclose(logfile); - logfile = 0; - } - } - - void setUp() - { - zoo_set_log_stream(logfile); - - zoo_deterministic_conn_order(0); - zh=0; - } - - void tearDown() - { - zookeeper_close(zh); - } - - class AsyncGetOperationCompletion: public AsyncCompletion{ - public: - AsyncGetOperationCompletion():called_(false),rc_(ZAPIERROR){} - virtual void dataCompl(int rc, const char *value, int len, const Stat *stat){ - synchronized(mx_); - called_=true; - rc_=rc; - value_.erase(); - if(rc!=ZOK) return; - value_.assign(value,len); - if(stat) - stat_=*stat; - } - bool operator()()const{ - synchronized(mx_); - return called_; - } - mutable Mutex mx_; - bool called_; - int rc_; - string value_; - NodeStat stat_; - }; -#ifndef THREADED - // send two get data requests; verify that the corresponding completions called - void testConcurrentOperations1() - { - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - int fd=0; - int interest=0; - timeval tv; - // first operation - AsyncGetOperationCompletion res1; - zkServer.addOperationResponse(new ZooGetResponse("1",1)); - int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // second operation - AsyncGetOperationCompletion res2; - zkServer.addOperationResponse(new ZooGetResponse("2",1)); - rc=zoo_aget(zh,"/x/y/2",0,asyncCompletion,&res2); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // process the send queue - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - while((rc=zookeeper_process(zh,interest))==ZOK) { - millisleep(100); - //printf("%d\n", rc); - } - //printf("RC = %d", rc); - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - CPPUNIT_ASSERT_EQUAL((int)ZOK,res1.rc_); - CPPUNIT_ASSERT_EQUAL(string("1"),res1.value_); - CPPUNIT_ASSERT_EQUAL((int)ZOK,res2.rc_); - CPPUNIT_ASSERT_EQUAL(string("2"),res2.value_); - } - // send two getData requests and disconnect while the second request is - // outstanding; - // verify the completions are called - void testOperationsAndDisconnectConcurrently1() - { - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - int fd=0; - int interest=0; - timeval tv; - // first operation - AsyncGetOperationCompletion res1; - zkServer.addOperationResponse(new ZooGetResponse("1",1)); - int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // second operation - AsyncGetOperationCompletion res2; - zkServer.addOperationResponse(new ZooGetResponse("2",1)); - rc=zoo_aget(zh,"/x/y/2",0,asyncCompletion,&res2); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // process the send queue - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // simulate a disconnect - zkServer.setConnectionLost(); - rc=zookeeper_interest(zh,&fd,&interest,&tv); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZCONNECTIONLOSS,rc); - CPPUNIT_ASSERT_EQUAL((int)ZOK,res1.rc_); - CPPUNIT_ASSERT_EQUAL(string("1"),res1.value_); - CPPUNIT_ASSERT_EQUAL((int)ZCONNECTIONLOSS,res2.rc_); - CPPUNIT_ASSERT_EQUAL(string(""),res2.value_); - } - // send two getData requests and simulate timeout while the both request - // are pending; - // verify the completions are called - void testOperationsAndDisconnectConcurrently2() - { - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - int fd=0; - int interest=0; - timeval tv; - // first operation - AsyncGetOperationCompletion res1; - zkServer.addOperationResponse(new ZooGetResponse("1",1)); - int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // second operation - AsyncGetOperationCompletion res2; - zkServer.addOperationResponse(new ZooGetResponse("2",1)); - rc=zoo_aget(zh,"/x/y/2",0,asyncCompletion,&res2); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // simulate timeout - timeMock.tick(+10); // advance system time by 10 secs - // the next call to zookeeper_interest should return ZOPERATIONTIMEOUT - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOPERATIONTIMEOUT,rc); - // make sure the completions have been called - CPPUNIT_ASSERT_EQUAL((int)ZOPERATIONTIMEOUT,res1.rc_); - CPPUNIT_ASSERT_EQUAL((int)ZOPERATIONTIMEOUT,res2.rc_); - } - - class PingCountingServer: public ZookeeperServer{ - public: - PingCountingServer():pingCount_(0){} - // called when a client request is received - virtual void onMessageReceived(const RequestHeader& rh, iarchive* ia){ - if(rh.type==PING_OP){ - pingCount_++; - } - } - int pingCount_; - }; - - // establish a connection; idle for a while - // verify ping was sent at least once - void testPing() - { - const int TIMEOUT=9; // timeout in secs - Mock_gettimeofday timeMock; - PingCountingServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - // receive timeout is in milliseconds - zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - int fd=0; - int interest=0; - timeval tv; - // Round 1. - int rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // simulate waiting for the select() call to timeout; - // advance the system clock accordingly - timeMock.tick(tv); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - // verify no ping sent - CPPUNIT_ASSERT(zkServer.pingCount_==0); - - // Round 2. - // the client should have the idle threshold exceeded, by now - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // assume the socket is writable, so no idling here; move on to - // zookeeper_process immediately - rc=zookeeper_process(zh,interest); - // ZNOTHING means the client hasn't received a ping response yet - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - // verify a ping is sent - CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_); - - // Round 3. - // we're going to receive a server PING response and make sure - // that the client has updated its last_recv timestamp - zkServer.addRecvResponse(new PingResponse); - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // pseudo-sleep for a short while (10 ms) - timeMock.millitick(10); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // only one ping so far? - CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_); - CPPUNIT_ASSERT(timeMock==zh->last_recv); - - // Round 4 - // make sure that a ping is not sent if something is outstanding - AsyncGetOperationCompletion res1; - rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - timeMock.tick(tv); - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // pseudo-sleep for a short while (10 ms) - timeMock.millitick(10); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - // only one ping so far? - CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_); - } - - // simulate a watch arriving right before a ping is due - // assert the ping is sent nevertheless - void testTimeoutCausedByWatches1() - { - const int TIMEOUT=9; // timeout in secs - Mock_gettimeofday timeMock; - PingCountingServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - // receive timeout is in milliseconds - zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - int fd=0; - int interest=0; - timeval tv; - // Round 1. - int rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // simulate waiting for the select() call to timeout; - // advance the system clock accordingly - timeMock.tick(tv); - timeMock.tick(-1); // set the clock to a millisecond before a ping is due - // trigger a watch now - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHANGED_EVENT,"/x/y/z")); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // arrival of a watch sets the last_recv to the current time - CPPUNIT_ASSERT(timeMock==zh->last_recv); - // spend 1 millisecond by processing the watch - timeMock.tick(1); - - // Round 2. - // a ping is due; zookeeper_interest() must send it now - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // no delay here -- as if the socket is immediately writable - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - // verify a ping is sent - CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_); - } - - // similar to testTimeoutCausedByWatches1, but this time the watch is - // triggered while the client has an outstanding request - // assert the ping is sent on time - void testTimeoutCausedByWatches2() - { - const int TIMEOUT=9; // timeout in secs - Mock_gettimeofday now; - PingCountingServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - // receive timeout is in milliseconds - zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - // queue up a request; keep it pending (as if the server is busy or has died) - AsyncGetOperationCompletion res1; - zkServer.addOperationResponse(new ZooGetResponse("2",1)); - int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); - - int fd=0; - int interest=0; - timeval tv; - // Round 1. - // send the queued up zoo_aget() request - Mock_gettimeofday beginningOfTimes(now); // remember when we started - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // no delay -- the socket is writable - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // Round 2. - // what's next? - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // no response from the server yet -- waiting in the select() call - now.tick(tv); - // a watch has arrived, thus preventing the connection from timing out - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHANGED_EVENT,"/x/y/z")); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // read the watch message - CPPUNIT_ASSERT_EQUAL(0,zkServer.pingCount_); // not yet! - - //Round 3. - // now is the time to send a ping; make sure it's actually sent - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - // verify a ping is sent - CPPUNIT_ASSERT_EQUAL(1,zkServer.pingCount_); - // make sure only 1/3 of the timeout has passed - CPPUNIT_ASSERT_EQUAL((int32_t)TIMEOUT/3*1000,toMilliseconds(now-beginningOfTimes)); - } - -#else - class TestGetDataJob: public TestJob{ - public: - TestGetDataJob(ZookeeperServer* svr,zhandle_t* zh, int reps=500) - :svr_(svr),zh_(zh),rc_(ZAPIERROR),reps_(reps){} - virtual void run(){ - int i; - for(i=0;iaddOperationResponse(new ZooGetResponse("1",1)); - rc_=zoo_get(zh_,"/x/y/z",0,&buf,&size,0); - if(rc_!=ZOK){ - break; - } - } - } - ZookeeperServer* svr_; - zhandle_t* zh_; - int rc_; - int reps_; - }; - class TestConcurrentOpJob: public TestGetDataJob{ - public: - static const int REPS=500; - TestConcurrentOpJob(ZookeeperServer* svr,zhandle_t* zh): - TestGetDataJob(svr,zh,REPS){} - virtual TestJob* clone() const { - return new TestConcurrentOpJob(svr_,zh_); - } - virtual void validate(const char* file, int line) const{ - CPPUNIT_ASSERT_EQUAL_MESSAGE_LOC("ZOK != rc",(int)ZOK,rc_,file,line); - } - }; - void testConcurrentOperations1() - { - for(int counter=0; counter<50; counter++){ - // frozen time -- no timeouts and no pings - Mock_gettimeofday timeMock; - - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - - TestJobManager jmgr(TestConcurrentOpJob(&zkServer,zh),10); - jmgr.startAllJobs(); - jmgr.wait(); - // validate test results - VALIDATE_JOBS(jmgr); - } - } - class ZKGetJob: public TestJob{ - public: - static const int REPS=1000; - ZKGetJob(zhandle_t* zh) - :zh_(zh),rc_(ZAPIERROR){} - virtual TestJob* clone() const { - return new ZKGetJob(zh_); - } - virtual void run(){ - int i; - for(i=0;i -#include "CppAssertHelper.h" - -#include "ZKMocks.h" -#include "CollectionUtil.h" -#include "Util.h" - -class Zookeeper_watchers : public CPPUNIT_NS::TestFixture -{ - CPPUNIT_TEST_SUITE(Zookeeper_watchers); - CPPUNIT_TEST(testDefaultSessionWatcher1); - CPPUNIT_TEST(testDefaultSessionWatcher2); - CPPUNIT_TEST(testObjectSessionWatcher1); - CPPUNIT_TEST(testObjectSessionWatcher2); - CPPUNIT_TEST(testNodeWatcher1); - CPPUNIT_TEST(testChildWatcher1); - CPPUNIT_TEST(testChildWatcher2); - CPPUNIT_TEST_SUITE_END(); - - static void watcher(zhandle_t *, int, int, const char *,void*){} - zhandle_t *zh; - FILE *logfile; - -public: - - Zookeeper_watchers() { - logfile = openlogfile("Zookeeper_watchers"); - } - - ~Zookeeper_watchers() { - if (logfile) { - fflush(logfile); - fclose(logfile); - logfile = 0; - } - } - - void setUp() - { - zoo_set_log_stream(logfile); - - zoo_deterministic_conn_order(0); - zh=0; - } - - void tearDown() - { - zookeeper_close(zh); - } - - class ConnectionWatcher: public WatcherAction{ - public: - ConnectionWatcher():connected_(false),counter_(0){} - virtual void onConnectionEstablished(zhandle_t*){ - synchronized(mx_); - counter_++; - connected_=true; - } - SyncedBoolCondition isConnectionEstablished() const{ - return SyncedBoolCondition(connected_,mx_); - } - bool connected_; - int counter_; - }; - - class DisconnectWatcher: public WatcherAction{ - public: - DisconnectWatcher():disconnected_(false),counter_(0){} - virtual void onConnectionLost(zhandle_t*){ - synchronized(mx_); - counter_++; - disconnected_=true; - } - SyncedBoolCondition isDisconnected() const{ - return SyncedBoolCondition(disconnected_,mx_); - } - bool disconnected_; - int counter_; - }; - - class CountingDataWatcher: public WatcherAction{ - public: - CountingDataWatcher():disconnected_(false),counter_(0){} - virtual void onNodeValueChanged(zhandle_t*,const char* path){ - synchronized(mx_); - counter_++; - } - virtual void onConnectionLost(zhandle_t*){ - synchronized(mx_); - counter_++; - disconnected_=true; - } - bool disconnected_; - int counter_; - }; - - class DeletionCountingDataWatcher: public WatcherAction{ - public: - DeletionCountingDataWatcher():counter_(0){} - virtual void onNodeDeleted(zhandle_t*,const char* path){ - synchronized(mx_); - counter_++; - } - int counter_; - }; - - class ChildEventCountingWatcher: public WatcherAction{ - public: - ChildEventCountingWatcher():counter_(0){} - virtual void onChildChanged(zhandle_t*,const char* path){ - synchronized(mx_); - counter_++; - } - int counter_; - }; - -#ifndef THREADED - - // verify: the default watcher is called once for a session event - void testDefaultSessionWatcher1(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - ConnectionWatcher watcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &watcher,0); - CPPUNIT_ASSERT(zh!=0); - - int fd=0; - int interest=0; - timeval tv; - // open the socket - int rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTING_STATE,zoo_state(zh)); - // send the handshake packet to the server - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE,zoo_state(zh)); - // receive the server handshake response - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // verify connected - CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE,zoo_state(zh)); - CPPUNIT_ASSERT(watcher.connected_); - CPPUNIT_ASSERT_EQUAL(1,watcher.counter_); - } - - // test case: connect to server, set a default watcher, disconnect from the server - // verify: the default watcher is called once - void testDefaultSessionWatcher2(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - DisconnectWatcher watcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &watcher,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - // first operation - AsyncCompletion ignored; - zkServer.addOperationResponse(new ZooGetResponse("1",1)); - int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // this will process the response and activate the watcher - rc=zookeeper_process(zh,ZOOKEEPER_READ); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // now, disconnect - zkServer.setConnectionLost(); - rc=zookeeper_process(zh,ZOOKEEPER_READ); - CPPUNIT_ASSERT_EQUAL((int)ZCONNECTIONLOSS,rc); - // verify disconnected - CPPUNIT_ASSERT(watcher.disconnected_); - CPPUNIT_ASSERT_EQUAL(1,watcher.counter_); - } - - // testcase: connect to the server, set a watcher object on a node, - // disconnect from the server - // verify: the watcher object as well as the default watcher are called - void testObjectSessionWatcher1(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - DisconnectWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - AsyncCompletion ignored; - CountingDataWatcher wobject; - zkServer.addOperationResponse(new ZooStatResponse); - int rc=zoo_awexists(zh,"/x/y/1",activeWatcher,&wobject, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // this will process the response and activate the watcher - rc=zookeeper_process(zh,ZOOKEEPER_READ); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // now, disconnect - zkServer.setConnectionLost(); - rc=zookeeper_process(zh,ZOOKEEPER_READ); - CPPUNIT_ASSERT_EQUAL((int)ZCONNECTIONLOSS,rc); - - // verify the default watcher has been triggered - CPPUNIT_ASSERT(defWatcher.disconnected_); - // and triggered only once - CPPUNIT_ASSERT_EQUAL(1,defWatcher.counter_); - - // the path-specific watcher has been triggered as well - CPPUNIT_ASSERT(wobject.disconnected_); - // only once! - CPPUNIT_ASSERT_EQUAL(1,wobject.counter_); - } - - // testcase: connect to the server, set a watcher object on a node, - // set a def watcher on another node,disconnect from the server - // verify: the watcher object as well as the default watcher are called - void testObjectSessionWatcher2(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - DisconnectWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - // set the default watcher - AsyncCompletion ignored; - zkServer.addOperationResponse(new ZooStatResponse); - int rc=zoo_aexists(zh,"/a/b/c",1,asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - CountingDataWatcher wobject; - zkServer.addOperationResponse(new ZooStatResponse); - rc=zoo_awexists(zh,"/x/y/z",activeWatcher,&wobject, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // this will process the response and activate the watcher - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - // disconnect now - zkServer.setConnectionLost(); - rc=zookeeper_process(zh,ZOOKEEPER_READ); - CPPUNIT_ASSERT_EQUAL((int)ZCONNECTIONLOSS,rc); - - // verify the default watcher has been triggered - CPPUNIT_ASSERT(defWatcher.disconnected_); - // and triggered only once - CPPUNIT_ASSERT_EQUAL(1,defWatcher.counter_); - - // the path-specific watcher has been triggered as well - CPPUNIT_ASSERT(wobject.disconnected_); - // only once! - CPPUNIT_ASSERT_EQUAL(1,wobject.counter_); - } - - // testcase: register 2 node watches for different paths, trigger the watches - // verify: the data watchers are processed, the default watcher is not called - void testNodeWatcher1(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - DisconnectWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - AsyncCompletion ignored; - CountingDataWatcher wobject1; - zkServer.addOperationResponse(new ZooStatResponse); - int rc=zoo_awexists(zh,"/a/b/c",activeWatcher,&wobject1, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - CountingDataWatcher wobject2; - zkServer.addOperationResponse(new ZooStatResponse); - rc=zoo_awexists(zh,"/x/y/z",activeWatcher,&wobject2, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // this will process the response and activate the watcher - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - // we are all set now; let's trigger the watches - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHANGED_EVENT,"/a/b/c")); - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHANGED_EVENT,"/x/y/z")); - // make sure all watchers have been processed - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - CPPUNIT_ASSERT_EQUAL(1,wobject1.counter_); - CPPUNIT_ASSERT_EQUAL(1,wobject2.counter_); - CPPUNIT_ASSERT_EQUAL(0,defWatcher.counter_); - } - - // testcase: set up both a children and a data watchers on the node /a, then - // delete the node by sending a DELETE_EVENT event - // verify: both watchers are triggered - void testChildWatcher1(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - DeletionCountingDataWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - AsyncCompletion ignored; - DeletionCountingDataWatcher wobject1; - zkServer.addOperationResponse(new ZooStatResponse); - int rc=zoo_awexists(zh,"/a",activeWatcher,&wobject1, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - typedef ZooGetChildrenResponse::StringVector ZooVector; - zkServer.addOperationResponse(new ZooGetChildrenResponse( - Util::CollectionBuilder()("/a/1")("/a/2") - )); - DeletionCountingDataWatcher wobject2; - rc=zoo_awget_children(zh,"/a",activeWatcher, - &wobject2,asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // this will process the response and activate the watcher - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - // we are all set now; let's trigger the watches - zkServer.addRecvResponse(new ZNodeEvent(ZOO_DELETED_EVENT,"/a")); - // make sure the watchers have been processed - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - CPPUNIT_ASSERT_EQUAL(1,wobject1.counter_); - CPPUNIT_ASSERT_EQUAL(1,wobject2.counter_); - CPPUNIT_ASSERT_EQUAL(0,defWatcher.counter_); - } - - // testcase: create both a child and data watch on the node /a, send a ZOO_CHILD_EVENT - // verify: only the child watch triggered - void testChildWatcher2(){ - Mock_gettimeofday timeMock; - ZookeeperServer zkServer; - // must call zookeeper_close() while all the mocks are in scope - CloseFinally guard(&zh); - - ChildEventCountingWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - forceConnected(zh); - - AsyncCompletion ignored; - ChildEventCountingWatcher wobject1; - zkServer.addOperationResponse(new ZooStatResponse); - int rc=zoo_awexists(zh,"/a",activeWatcher,&wobject1, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - typedef ZooGetChildrenResponse::StringVector ZooVector; - zkServer.addOperationResponse(new ZooGetChildrenResponse( - Util::CollectionBuilder()("/a/1")("/a/2") - )); - ChildEventCountingWatcher wobject2; - rc=zoo_awget_children(zh,"/a",activeWatcher, - &wobject2,asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // this will process the response and activate the watcher - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - // we are all set now; let's trigger the watches - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHILD_EVENT,"/a")); - // make sure the watchers have been processed - while((rc=zookeeper_process(zh,ZOOKEEPER_READ))==ZOK) { - millisleep(100); - } - CPPUNIT_ASSERT_EQUAL((int)ZNOTHING,rc); - - CPPUNIT_ASSERT_EQUAL(0,wobject1.counter_); - CPPUNIT_ASSERT_EQUAL(1,wobject2.counter_); - CPPUNIT_ASSERT_EQUAL(0,defWatcher.counter_); - } - -#else - // verify: the default watcher is called once for a session event - void testDefaultSessionWatcher1(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_SESSION_EVENT,ZOO_CONNECTED_STATE); - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - ConnectionWatcher watcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &watcher,0); - CPPUNIT_ASSERT(zh!=0); - // wait till watcher proccessing has completed (the connection - // established event) - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); - - // verify the watcher has been triggered - CPPUNIT_ASSERT(ensureCondition(watcher.isConnectionEstablished(),1000)<1000); - // triggered only once - CPPUNIT_ASSERT_EQUAL(1,watcher.counter_); - } - - // test case: connect to server, set a default watcher, disconnect from the server - // verify: the default watcher is called once - void testDefaultSessionWatcher2(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_SESSION_EVENT,ZOO_CONNECTING_STATE); - DisconnectWatcher watcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &watcher,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - // set a default watch - AsyncCompletion ignored; - // a successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - int rc=zoo_aexists(zh,"/x/y/z",1,asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // now, initiate a disconnect - zkServer.setConnectionLost(); - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); - - // verify the watcher has been triggered - CPPUNIT_ASSERT(watcher.disconnected_); - // triggered only once - CPPUNIT_ASSERT_EQUAL(1,watcher.counter_); - } - - // testcase: connect to the server, set a watcher object on a node, - // disconnect from the server - // verify: the watcher object as well as the default watcher are called - void testObjectSessionWatcher1(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_SESSION_EVENT,ZOO_CONNECTING_STATE); - DisconnectWatcher defWatcher; - // use the tracker to find out when the watcher has been activated - WatcherActivationTracker activationTracker; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - - AsyncCompletion ignored; - // this successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - CountingDataWatcher wobject; - activationTracker.track(&wobject); - // set a path-specific watcher - int rc=zoo_awexists(zh,"/x/y/z",activeWatcher,&wobject, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // make sure the watcher gets activated before we continue - CPPUNIT_ASSERT(ensureCondition(activationTracker.isWatcherActivated(),1000)<1000); - - // now, initiate a disconnect - zkServer.setConnectionLost(); - // make sure all watchers have been processed - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); - - // verify the default watcher has been triggered - CPPUNIT_ASSERT(defWatcher.disconnected_); - // and triggered only once - CPPUNIT_ASSERT_EQUAL(1,defWatcher.counter_); - - // the path-specific watcher has been triggered as well - CPPUNIT_ASSERT(wobject.disconnected_); - // only once! - CPPUNIT_ASSERT_EQUAL(1,wobject.counter_); - } - - // testcase: connect to the server, set a watcher object on a node, - // set a def watcher on another node,disconnect from the server - // verify: the watcher object as well as the default watcher are called - void testObjectSessionWatcher2(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_SESSION_EVENT,ZOO_CONNECTING_STATE); - DisconnectWatcher defWatcher; - // use the tracker to find out when the watcher has been activated - WatcherActivationTracker activationTracker; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - - // set a default watch - AsyncCompletion ignored; - // a successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - activationTracker.track(&defWatcher); - int rc=zoo_aexists(zh,"/a/b/c",1,asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // make sure the watcher gets activated before we continue - CPPUNIT_ASSERT(ensureCondition(activationTracker.isWatcherActivated(),1000)<1000); - - // this successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - CountingDataWatcher wobject; - activationTracker.track(&wobject); - // set a path-specific watcher - rc=zoo_awexists(zh,"/x/y/z",activeWatcher,&wobject, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // make sure the watcher gets activated before we continue - CPPUNIT_ASSERT(ensureCondition(activationTracker.isWatcherActivated(),1000)<1000); - - // now, initiate a disconnect - zkServer.setConnectionLost(); - // make sure all watchers have been processed - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); - - // verify the default watcher has been triggered - CPPUNIT_ASSERT(defWatcher.disconnected_); - // and triggered only once - CPPUNIT_ASSERT_EQUAL(1,defWatcher.counter_); - - // the path-specific watcher has been triggered as well - CPPUNIT_ASSERT(wobject.disconnected_); - // only once! - CPPUNIT_ASSERT_EQUAL(1,wobject.counter_); - } - - // testcase: register 2 node watches for different paths, trigger the watches - // verify: the data watchers are processed, the default watcher is not called - void testNodeWatcher1(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_CHANGED_EVENT,0,false); - CountingDataWatcher defWatcher; - // use the tracker to find out when the watcher has been activated - WatcherActivationTracker activationTracker; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - - // don't care about completions - AsyncCompletion ignored; - // set a one-shot watch - // a successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - CountingDataWatcher wobject1; - activationTracker.track(&wobject1); - int rc=zoo_awexists(zh,"/a/b/c",activeWatcher,&wobject1, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // make sure the watcher gets activated before we continue - CPPUNIT_ASSERT(ensureCondition(activationTracker.isWatcherActivated(),1000)<1000); - - // this successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - CountingDataWatcher wobject2; - activationTracker.track(&wobject2); - // set a path-specific watcher - rc=zoo_awexists(zh,"/x/y/z",activeWatcher,&wobject2, - asyncCompletion,&ignored); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // make sure the watcher gets activated before we continue - CPPUNIT_ASSERT(ensureCondition(activationTracker.isWatcherActivated(),1000)<1000); - - // we are all set now; let's trigger the watches - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHANGED_EVENT,"/a/b/c")); - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHANGED_EVENT,"/x/y/z")); - // make sure all watchers have been processed - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.deliveryCounterEquals(2),1000)<1000); - - CPPUNIT_ASSERT_EQUAL(1,wobject1.counter_); - CPPUNIT_ASSERT_EQUAL(1,wobject2.counter_); - CPPUNIT_ASSERT_EQUAL(0,defWatcher.counter_); - } - - // testcase: set up both a children and a data watchers on the node /a, then - // delete the node (that is, send a DELETE_EVENT) - // verify: both watchers are triggered - void testChildWatcher1(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_DELETED_EVENT,0); - DeletionCountingDataWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - - // a successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - DeletionCountingDataWatcher wobject1; - Stat stat; - // add a node watch - int rc=zoo_wexists(zh,"/a",activeWatcher,&wobject1,&stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - typedef ZooGetChildrenResponse::StringVector ZooVector; - zkServer.addOperationResponse(new ZooGetChildrenResponse( - Util::CollectionBuilder()("/a/1")("/a/2") - )); - DeletionCountingDataWatcher wobject2; - String_vector children; - rc=zoo_wget_children(zh,"/a",activeWatcher,&wobject2,&children); - deallocate_String_vector(&children); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // we are all set now; let's trigger the watches - zkServer.addRecvResponse(new ZNodeEvent(ZOO_DELETED_EVENT,"/a")); - // make sure the watchers have been processed - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); - - CPPUNIT_ASSERT_EQUAL(1,wobject1.counter_); - CPPUNIT_ASSERT_EQUAL(1,wobject2.counter_); - CPPUNIT_ASSERT_EQUAL(0,defWatcher.counter_); - } - - // testcase: create both a child and data watch on the node /a, send a ZOO_CHILD_EVENT - // verify: only the child watch triggered - void testChildWatcher2(){ - Mock_gettimeofday timeMock; - // zookeeper simulator - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // must call zookeeper_close() while all the mocks are in the scope! - CloseFinally guard(&zh); - - // detects when all watchers have been delivered - WatcherDeliveryTracker deliveryTracker(ZOO_CHILD_EVENT,0); - ChildEventCountingWatcher defWatcher; - zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, - &defWatcher,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - // a successful server response will activate the watcher - zkServer.addOperationResponse(new ZooStatResponse); - ChildEventCountingWatcher wobject1; - Stat stat; - // add a node watch - int rc=zoo_wexists(zh,"/a",activeWatcher,&wobject1,&stat); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - typedef ZooGetChildrenResponse::StringVector ZooVector; - zkServer.addOperationResponse(new ZooGetChildrenResponse( - Util::CollectionBuilder()("/a/1")("/a/2") - )); - ChildEventCountingWatcher wobject2; - String_vector children; - rc=zoo_wget_children(zh,"/a",activeWatcher,&wobject2,&children); - deallocate_String_vector(&children); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // we are all set now; let's trigger the watches - zkServer.addRecvResponse(new ZNodeEvent(ZOO_CHILD_EVENT,"/a")); - // make sure the watchers have been processed - CPPUNIT_ASSERT(ensureCondition( - deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); - - CPPUNIT_ASSERT_EQUAL(0,wobject1.counter_); - CPPUNIT_ASSERT_EQUAL(1,wobject2.counter_); - CPPUNIT_ASSERT_EQUAL(0,defWatcher.counter_); - } - -#endif //THREADED -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_watchers); diff --git a/src/c/tests/TestZookeeperClose.cc b/src/c/tests/TestZookeeperClose.cc deleted file mode 100644 index edefa66e95d..00000000000 --- a/src/c/tests/TestZookeeperClose.cc +++ /dev/null @@ -1,472 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "ZKMocks.h" - -#ifdef THREADED -#include "PthreadMocks.h" -#endif - -using namespace std; - -class Zookeeper_close : public CPPUNIT_NS::TestFixture -{ - CPPUNIT_TEST_SUITE(Zookeeper_close); -#ifdef THREADED - CPPUNIT_TEST(testIOThreadStoppedOnExpire); -#endif - CPPUNIT_TEST(testCloseUnconnected); - CPPUNIT_TEST(testCloseUnconnected1); - CPPUNIT_TEST(testCloseConnected1); - CPPUNIT_TEST(testCloseFromWatcher1); - CPPUNIT_TEST_SUITE_END(); - zhandle_t *zh; - static void watcher(zhandle_t *, int, int, const char *,void*){} - FILE *logfile; -public: - - Zookeeper_close() { - logfile = openlogfile("Zookeeper_close"); - } - - ~Zookeeper_close() { - if (logfile) { - fflush(logfile); - fclose(logfile); - logfile = 0; - } - } - - void setUp() - { - zoo_set_log_stream(logfile); - - zoo_deterministic_conn_order(0); - zh=0; - } - - void tearDown() - { - zookeeper_close(zh); - } - - class CloseOnSessionExpired: public WatcherAction{ - public: - CloseOnSessionExpired(bool callClose=true): - callClose_(callClose),rc(ZOK){} - virtual void onSessionExpired(zhandle_t* zh){ - memcpy(&lzh,zh,sizeof(lzh)); - if(callClose_) - rc=zookeeper_close(zh); - } - zhandle_t lzh; - bool callClose_; - int rc; - }; - -#ifndef THREADED - void testCloseUnconnected() - { - zh=zookeeper_init("localhost:2121",watcher,10000,0,0,0); - CPPUNIT_ASSERT(zh!=0); - - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - // make a copy of zhandle before close() overwrites some of - // it members with NULLs - zhandle_t lzh; - memcpy(&lzh,zh,sizeof(lzh)); - int rc=zookeeper_close(zh); - zhandle_t* savezh=zh; zh=0; - freeMock.disable(); // disable mock's fake free()- use libc's free() instead - - // verify that zookeeper_close has done its job - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.addrs)); - // This cannot be maintained properly CPPUNIT_ASSERT_EQUAL(9,freeMock.callCounter); - } - void testCloseUnconnected1() - { - zh=zookeeper_init("localhost:2121",watcher,10000,0,0,0); - CPPUNIT_ASSERT(zh!=0); - // simulate connected state - zh->fd=ZookeeperServer::FD; - zh->state=ZOO_CONNECTED_STATE; - Mock_flush_send_queue zkMock; - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - // make a copy of zhandle before close() overwrites some of - // it members with NULLs - zhandle_t lzh; - memcpy(&lzh,zh,sizeof(lzh)); - int rc=zookeeper_close(zh); - zhandle_t* savezh=zh; zh=0; - freeMock.disable(); // disable mock's fake free()- use libc's free() instead - - // verify that zookeeper_close has done its job - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.addrs)); - // the close request sent? - CPPUNIT_ASSERT_EQUAL(1,zkMock.counter); - } - void testCloseConnected1() - { - ZookeeperServer zkServer; - // poll() will called from zookeeper_close() - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - - zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - - Mock_gettimeofday timeMock; - - int fd=0; - int interest=0; - timeval tv; - int rc=zookeeper_interest(zh,&fd,&interest,&tv); - - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTING_STATE,zoo_state(zh)); - CPPUNIT_ASSERT_EQUAL(ZOOKEEPER_READ|ZOOKEEPER_WRITE,interest); - - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE,zoo_state(zh)); - - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - rc=zookeeper_process(zh,interest); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE,zoo_state(zh)); - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - // make a copy of zhandle before close() overwrites some of - // it members with NULLs - zhandle_t lzh; - memcpy(&lzh,zh,sizeof(lzh)); - zookeeper_close(zh); - zhandle_t* savezh=zh; zh=0; - freeMock.disable(); // disable mock's fake free()- use libc's free() instead - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.addrs)); - // the close request sent? - CPPUNIT_ASSERT_EQUAL(1,(int)zkServer.closeSent); - } - void testCloseFromWatcher1() - { - Mock_gettimeofday timeMock; - - ZookeeperServer zkServer; - // make the server return a non-matching session id - zkServer.returnSessionExpired(); - // poll() will called from zookeeper_close() - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - - CloseOnSessionExpired closeAction; - zh=zookeeper_init("localhost:2121",activeWatcher,10000, - TEST_CLIENT_ID,&closeAction,0); - CPPUNIT_ASSERT(zh!=0); - - int fd=0; - int interest=0; - timeval tv; - // initiate connection - int rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTING_STATE,zoo_state(zh)); - CPPUNIT_ASSERT_EQUAL(ZOOKEEPER_READ|ZOOKEEPER_WRITE,interest); - rc=zookeeper_process(zh,interest); - // make sure the handshake in progress - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE,zoo_state(zh)); - rc=zookeeper_interest(zh,&fd,&interest,&tv); - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - // should call the watcher with ZOO_EXPIRED_SESSION_STATE state - rc=zookeeper_process(zh,interest); - zhandle_t* savezh=zh; zh=0; - freeMock.disable(); // disable mock's fake free()- use libc's free() instead - - CPPUNIT_ASSERT_EQUAL(ZOO_EXPIRED_SESSION_STATE,zoo_state(savezh)); - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(closeAction.lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(closeAction.lzh.addrs)); - // make sure the close request NOT sent - CPPUNIT_ASSERT_EQUAL(0,(int)zkServer.closeSent); - } -#else - void testCloseUnconnected() - { - // disable threading - MockPthreadZKNull pthreadMock; - zh=zookeeper_init("localhost:2121",watcher,10000,0,0,0); - - CPPUNIT_ASSERT(zh!=0); - adaptor_threads* adaptor=(adaptor_threads*)zh->adaptor_priv; - CPPUNIT_ASSERT(adaptor!=0); - - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - // make a copy of zhandle before close() overwrites some of - // it members with NULLs - zhandle_t lzh; - memcpy(&lzh,zh,sizeof(lzh)); - int rc=zookeeper_close(zh); - zhandle_t* savezh=zh; zh=0; - // we're done, disable mock's fake free(), use libc's free() instead - freeMock.disable(); - - // verify that zookeeper_close has done its job - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.addrs)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(adaptor)); - // Cannot be maintained accurately: CPPUNIT_ASSERT_EQUAL(10,freeMock.callCounter); - // threads - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(adaptor->io)); - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(adaptor->completion)); - // mutexes - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(&savezh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,MockPthreadsNull::getInvalidAccessCounter(&savezh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(&savezh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(0,MockPthreadsNull::getInvalidAccessCounter(&savezh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(&savezh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(0,MockPthreadsNull::getInvalidAccessCounter(&savezh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(&savezh->completions_to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,MockPthreadsNull::getInvalidAccessCounter(&savezh->completions_to_process.lock)); - // conditionals - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(&savezh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(0,MockPthreadsNull::getInvalidAccessCounter(&savezh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(1,MockPthreadsNull::getDestroyCounter(&savezh->completions_to_process.cond)); - CPPUNIT_ASSERT_EQUAL(0,MockPthreadsNull::getInvalidAccessCounter(&savezh->completions_to_process.cond)); - } - void testCloseUnconnected1() - { - for(int i=0; i<100;i++){ - zh=zookeeper_init("localhost:2121",watcher,10000,0,0,0); - CPPUNIT_ASSERT(zh!=0); - adaptor_threads* adaptor=(adaptor_threads*)zh->adaptor_priv; - CPPUNIT_ASSERT(adaptor!=0); - int rc=zookeeper_close(zh); - zh=0; - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - } - } - void testCloseConnected1() - { - // frozen time -- no timeouts and no pings - Mock_gettimeofday timeMock; - - for(int i=0;i<100;i++){ - ZookeeperServer zkServer; - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // use a checked version of pthread calls - CheckedPthread threadMock; - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - - zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); - CPPUNIT_ASSERT(zh!=0); - // make sure the client has connected - CPPUNIT_ASSERT(ensureCondition(ClientConnected(zh),1000)<1000); - // make a copy of zhandle before close() overwrites some of - // its members with NULLs - zhandle_t lzh; - memcpy(&lzh,zh,sizeof(lzh)); - int rc=zookeeper_close(zh); - zhandle_t* savezh=zh; zh=0; - // we're done, disable mock's fake free(), use libc's free() instead - freeMock.disable(); - - CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); - adaptor_threads* adaptor=(adaptor_threads*)lzh.adaptor_priv; - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh.addrs)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(adaptor)); - // threads - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(adaptor->io)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(adaptor->completion)); - // mutexes - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&savezh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&savezh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&savezh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&savezh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&savezh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&savezh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&savezh->completions_to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&savezh->completions_to_process.lock)); - // conditionals - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&savezh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&savezh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&savezh->completions_to_process.cond)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&savezh->completions_to_process.cond)); - } - } - - struct PointerFreed{ - PointerFreed(Mock_free_noop& freeMock,void* ptr): - freeMock_(freeMock),ptr_(ptr){} - bool operator()() const{return freeMock_.isFreed(ptr_); } - Mock_free_noop& freeMock_; - void* ptr_; - }; - // test if zookeeper_close may be called from a watcher callback on - // SESSION_EXPIRED event - void testCloseFromWatcher1() - { - // frozen time -- no timeouts and no pings - Mock_gettimeofday timeMock; - - for(int i=0;i<100;i++){ - ZookeeperServer zkServer; - // make the server return a non-matching session id - zkServer.returnSessionExpired(); - - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // use a checked version of pthread calls - CheckedPthread threadMock; - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - - CloseOnSessionExpired closeAction; - zh=zookeeper_init("localhost:2121",activeWatcher,10000, - TEST_CLIENT_ID,&closeAction,0); - - CPPUNIT_ASSERT(zh!=0); - // we rely on the fact that zh is freed the last right before - // zookeeper_close() returns... - CPPUNIT_ASSERT(ensureCondition(PointerFreed(freeMock,zh),1000)<1000); - zhandle_t* lzh=zh; - zh=0; - // we're done, disable mock's fake free(), use libc's free() instead - freeMock.disable(); - - CPPUNIT_ASSERT_EQUAL((int)ZOK,closeAction.rc); - adaptor_threads* adaptor=(adaptor_threads*)closeAction.lzh.adaptor_priv; - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(closeAction.lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(closeAction.lzh.addrs)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(adaptor)); - // threads - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(adaptor->io)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(adaptor->completion)); - // mutexes - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->completions_to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->completions_to_process.lock)); - // conditionals - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->completions_to_process.cond)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->completions_to_process.cond)); - } - } - - void testIOThreadStoppedOnExpire() - { - // frozen time -- no timeouts and no pings - Mock_gettimeofday timeMock; - - for(int i=0;i<100;i++){ - ZookeeperServer zkServer; - // make the server return a non-matching session id - zkServer.returnSessionExpired(); - - Mock_poll pollMock(&zkServer,ZookeeperServer::FD); - // use a checked version of pthread calls - CheckedPthread threadMock; - // do not call zookeeper_close() from the watcher - CloseOnSessionExpired closeAction(false); - zh=zookeeper_init("localhost:2121",activeWatcher,10000, - &testClientId,&closeAction,0); - - // this is to ensure that if any assert fires, zookeeper_close() - // will still be called while all the mocks are in the scope! - CloseFinally guard(&zh); - - CPPUNIT_ASSERT(zh!=0); - CPPUNIT_ASSERT(ensureCondition(SessionExpired(zh),1000)<1000); - CPPUNIT_ASSERT(ensureCondition(IOThreadStopped(zh),1000)<1000); - // make sure the watcher has been processed - CPPUNIT_ASSERT(ensureCondition(closeAction.isWatcherTriggered(),1000)<1000); - // make sure the threads have not been destroyed yet - adaptor_threads* adaptor=(adaptor_threads*)zh->adaptor_priv; - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getDestroyCounter(adaptor->io)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getDestroyCounter(adaptor->completion)); - // about to call zookeeper_close() -- no longer need the guard - guard.disarm(); - - // do not actually free the memory while in zookeeper_close() - Mock_free_noop freeMock; - zookeeper_close(zh); - zhandle_t* lzh=zh; zh=0; - // we're done, disable mock's fake free(), use libc's free() instead - freeMock.disable(); - - // memory - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(lzh)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(closeAction.lzh.hostname)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(closeAction.lzh.addrs)); - CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(adaptor)); - // threads - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(adaptor->io)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(adaptor->completion)); - // mutexes - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->to_process.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->to_send.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->sent_requests.lock)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->completions_to_process.lock)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->completions_to_process.lock)); - // conditionals - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->sent_requests.cond)); - CPPUNIT_ASSERT_EQUAL(1,CheckedPthread::getDestroyCounter(&lzh->completions_to_process.cond)); - CPPUNIT_ASSERT_EQUAL(0,CheckedPthread::getInvalidAccessCounter(&lzh->completions_to_process.cond)); - } - } - -#endif -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_close); diff --git a/src/c/tests/TestZookeeperInit.cc b/src/c/tests/TestZookeeperInit.cc deleted file mode 100644 index a9078ce1fb1..00000000000 --- a/src/c/tests/TestZookeeperInit.cc +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include "Util.h" -#include "LibCMocks.h" -#include "ZKMocks.h" - -#ifdef THREADED -#include "PthreadMocks.h" -#else -class MockPthreadsNull; -#endif - -using namespace std; - -class Zookeeper_init : public CPPUNIT_NS::TestFixture -{ - CPPUNIT_TEST_SUITE(Zookeeper_init); - CPPUNIT_TEST(testBasic); - CPPUNIT_TEST(testAddressResolution); - CPPUNIT_TEST(testMultipleAddressResolution); - CPPUNIT_TEST(testNullAddressString); - CPPUNIT_TEST(testEmptyAddressString); - CPPUNIT_TEST(testOneSpaceAddressString); - CPPUNIT_TEST(testTwoSpacesAddressString); - CPPUNIT_TEST(testInvalidAddressString1); - CPPUNIT_TEST(testInvalidAddressString2); - CPPUNIT_TEST(testNonexistentHost); - CPPUNIT_TEST(testOutOfMemory_init); - CPPUNIT_TEST(testOutOfMemory_getaddrs1); -#if !defined(__CYGWIN__) // not valid for cygwin - CPPUNIT_TEST(testOutOfMemory_getaddrs2); -#endif - CPPUNIT_TEST(testPermuteAddrsList); - CPPUNIT_TEST_SUITE_END(); - zhandle_t *zh; - MockPthreadsNull* pthreadMock; - static void watcher(zhandle_t *, int , int , const char *,void*){} - FILE *logfile; -public: - Zookeeper_init():zh(0),pthreadMock(0){ - logfile = openlogfile("Zookeeper_init"); - } - - ~Zookeeper_init() { - if (logfile) { - fflush(logfile); - fclose(logfile); - logfile = 0; - } - } - - void setUp() - { - zoo_set_log_stream(logfile); - - zoo_deterministic_conn_order(0); -#ifdef THREADED - // disable threading - pthreadMock=new MockPthreadZKNull; -#endif - zh=0; - } - - void tearDown() - { - zookeeper_close(zh); -#ifdef THREADED - delete pthreadMock; -#endif - } - - void testBasic() - { - const string EXPECTED_HOST("127.0.0.1:2121"); - const int EXPECTED_ADDRS_COUNT =1; - const int EXPECTED_RECV_TIMEOUT=10000; - clientid_t cid; - memset(&cid,0xFE,sizeof(cid)); - - zh=zookeeper_init(EXPECTED_HOST.c_str(),watcher,EXPECTED_RECV_TIMEOUT, - &cid,(void*)1,0); - - CPPUNIT_ASSERT(zh!=0); - CPPUNIT_ASSERT(zh->fd == -1); - CPPUNIT_ASSERT(zh->hostname!=0); - CPPUNIT_ASSERT_EQUAL(EXPECTED_ADDRS_COUNT,zh->addrs_count); - CPPUNIT_ASSERT_EQUAL(EXPECTED_HOST,string(zh->hostname)); - CPPUNIT_ASSERT(zh->state == 0); - CPPUNIT_ASSERT(zh->context == (void*)1); - CPPUNIT_ASSERT_EQUAL(EXPECTED_RECV_TIMEOUT,zh->recv_timeout); - CPPUNIT_ASSERT(zh->watcher == watcher); - CPPUNIT_ASSERT(zh->connect_index==0); - CPPUNIT_ASSERT(zh->primer_buffer.buffer==zh->primer_storage_buffer); - CPPUNIT_ASSERT(zh->primer_buffer.curr_offset ==0); - CPPUNIT_ASSERT(zh->primer_buffer.len == sizeof(zh->primer_storage_buffer)); - CPPUNIT_ASSERT(zh->primer_buffer.next == 0); - CPPUNIT_ASSERT(zh->last_zxid ==0); - CPPUNIT_ASSERT(memcmp(&zh->client_id,&cid,sizeof(cid))==0); - -#ifdef THREADED - // thread specific checks - adaptor_threads* adaptor=(adaptor_threads*)zh->adaptor_priv; - CPPUNIT_ASSERT(adaptor!=0); - CPPUNIT_ASSERT(pthreadMock->pthread_createCounter==2); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(adaptor->io)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(adaptor->completion)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(&zh->to_process.lock)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(&zh->to_send.lock)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(&zh->sent_requests.lock)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(&zh->completions_to_process.lock)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(&zh->sent_requests.cond)); - CPPUNIT_ASSERT(MockPthreadsNull::isInitialized(&zh->completions_to_process.cond)); -#endif - } - void testAddressResolution() - { - const char EXPECTED_IPS[][4]={{127,0,0,1}}; - const int EXPECTED_ADDRS_COUNT =COUNTOF(EXPECTED_IPS); - - zoo_deterministic_conn_order(1); - zh=zookeeper_init("127.0.0.1:2121",0,10000,0,0,0); - - CPPUNIT_ASSERT(zh!=0); - CPPUNIT_ASSERT_EQUAL(EXPECTED_ADDRS_COUNT,zh->addrs_count); - for(int i=0;iaddrs_count;i++){ - sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs[i]; - CPPUNIT_ASSERT(memcmp(EXPECTED_IPS[i],&addr->sin_addr,sizeof(addr->sin_addr))==0); - CPPUNIT_ASSERT_EQUAL(2121,(int)ntohs(addr->sin_port)); - } - } - void testMultipleAddressResolution() - { - const string EXPECTED_HOST("127.0.0.1:2121,127.0.0.2:3434"); - const char EXPECTED_IPS[][4]={{127,0,0,1},{127,0,0,2}}; - const int EXPECTED_ADDRS_COUNT =COUNTOF(EXPECTED_IPS); - - zoo_deterministic_conn_order(1); - zh=zookeeper_init(EXPECTED_HOST.c_str(),0,1000,0,0,0); - - CPPUNIT_ASSERT(zh!=0); - CPPUNIT_ASSERT_EQUAL(EXPECTED_ADDRS_COUNT,zh->addrs_count); - - for(int i=0;iaddrs_count;i++){ - sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs[i]; - CPPUNIT_ASSERT(memcmp(EXPECTED_IPS[i],&addr->sin_addr,sizeof(addr->sin_addr))==0); - if(i<1) - CPPUNIT_ASSERT_EQUAL(2121,(int)ntohs(addr->sin_port)); - else - CPPUNIT_ASSERT_EQUAL(3434,(int)ntohs(addr->sin_port)); - } - } - void testMultipleAddressWithSpace() - { - const string EXPECTED_HOST("127.0.0.1:2121, 127.0.0.2:3434"); - const char EXPECTED_IPS[][4]={{127,0,0,1},{127,0,0,2}}; - const int EXPECTED_ADDRS_COUNT =COUNTOF(EXPECTED_IPS); - - zoo_deterministic_conn_order(1); - zh=zookeeper_init(EXPECTED_HOST.c_str(),0,1000,0,0,0); - - CPPUNIT_ASSERT(zh!=0); - CPPUNIT_ASSERT_EQUAL(EXPECTED_ADDRS_COUNT,zh->addrs_count); - - for(int i=0;iaddrs_count;i++){ - sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs[i]; - CPPUNIT_ASSERT(memcmp(EXPECTED_IPS[i],&addr->sin_addr,sizeof(addr->sin_addr))==0); - if(i<1) - CPPUNIT_ASSERT_EQUAL(2121,(int)ntohs(addr->sin_port)); - else - CPPUNIT_ASSERT_EQUAL(3434,(int)ntohs(addr->sin_port)); - } - } - void testNullAddressString() - { - zh=zookeeper_init(NULL,0,0,0,0,0); - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(EINVAL,errno); - } - void testEmptyAddressString() - { - const string INVALID_HOST(""); - zh=zookeeper_init(INVALID_HOST.c_str(),0,0,0,0,0); - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(EINVAL,errno); - } - void testOneSpaceAddressString() - { - const string INVALID_HOST(" "); - zh=zookeeper_init(INVALID_HOST.c_str(),0,0,0,0,0); - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(EINVAL,errno); - } - void testTwoSpacesAddressString() - { - const string INVALID_HOST(" "); - zh=zookeeper_init(INVALID_HOST.c_str(),0,0,0,0,0); - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(EINVAL,errno); - } - void testInvalidAddressString1() - { - const string INVALID_HOST("host1"); - zh=zookeeper_init(INVALID_HOST.c_str(),0,0,0,0,0); - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(EINVAL,errno); - } - void testInvalidAddressString2() - { - const string INVALID_HOST("host1:1111+host:123"); - zh=zookeeper_init(INVALID_HOST.c_str(),0,0,0,0,0); - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(ENOENT,errno); - } - void testNonexistentHost() - { - const string EXPECTED_HOST("host1.blabadibla.bla.:1111"); - - zh=zookeeper_init(EXPECTED_HOST.c_str(),0,0,0,0,0); - - CPPUNIT_ASSERT(zh==0); - //With the switch to thread safe getaddrinfo, we don't get - //these global variables - //CPPUNIT_ASSERT_EQUAL(EINVAL,errno); - //CPPUNIT_ASSERT_EQUAL(HOST_NOT_FOUND,h_errno); - } - void testOutOfMemory_init() - { - Mock_calloc mock; - mock.callsBeforeFailure=0; // fail first calloc in init() - - zh=zookeeper_init("ahost:123",watcher,10000,0,0,0); - - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(ENOMEM,errno); - } - void testOutOfMemory_getaddrs1() - { - Mock_realloc reallocMock; - reallocMock.callsBeforeFailure=0; // fail on first call to realloc - - zh=zookeeper_init("127.0.0.1:123",0,0,0,0,0); - - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(ENOMEM,errno); - } - void testOutOfMemory_getaddrs2() - { - Mock_realloc reallocMock; - reallocMock.callsBeforeFailure=1; // fail on the second call to realloc - - zh=zookeeper_init("127.0.0.1:123,127.0.0.2:123,127.0.0.3:123,127.0.0.4:123,127.0.0.5:123,127.0.0.6:123,127.0.0.7:123,127.0.0.8:123,127.0.0.9:123,127.0.0.10:123,127.0.0.11:123,127.0.0.12:123,127.0.0.13:123,127.0.0.14:123,127.0.0.15:123,127.0.0.16:123,127.0.0.17:123",0,0,0,0,0); - - CPPUNIT_ASSERT(zh==0); - CPPUNIT_ASSERT_EQUAL(ENOMEM,errno); - } - void testPermuteAddrsList() - { - const char EXPECTED[][5]={"\0\0\0\0","\1\1\1\1","\2\2\2\2","\3\3\3\3"}; - const int EXPECTED_ADDR_COUNT=COUNTOF(EXPECTED); - - const int RAND_SEQ[]={0,1,2,3,1,3,2,0,-1}; - const int RAND_SIZE=COUNTOF(RAND_SEQ); - Mock_random randomMock; - randomMock.randomReturns.assign(RAND_SEQ,RAND_SEQ+RAND_SIZE-1); - zh=zookeeper_init("0.0.0.0:123,1.1.1.1:123,2.2.2.2:123,3.3.3.3:123",0,1000,0,0,0); - - CPPUNIT_ASSERT(zh!=0); - CPPUNIT_ASSERT_EQUAL(EXPECTED_ADDR_COUNT,zh->addrs_count); - const string EXPECTED_SEQ("3210"); - char ACTUAL_SEQ[EXPECTED_ADDR_COUNT+1]; ACTUAL_SEQ[EXPECTED_ADDR_COUNT]=0; - for(int i=0;iaddrs_count;i++){ - sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs[i]; - // match the first byte of the EXPECTED and of the actual address - ACTUAL_SEQ[i]=((char*)&addr->sin_addr)[0]+'0'; - } - CPPUNIT_ASSERT_EQUAL(EXPECTED_SEQ,string(ACTUAL_SEQ)); - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_init); diff --git a/src/c/tests/ThreadingUtil.cc b/src/c/tests/ThreadingUtil.cc deleted file mode 100644 index 6b0bfe5f3d8..00000000000 --- a/src/c/tests/ThreadingUtil.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "ThreadingUtil.h" -#include "LibCSymTable.h" - -#ifdef THREADED - -// **************************************************************************** -// Mutex wrapper -struct Mutex::Impl{ - Impl(){ - LIBC_SYMBOLS.pthread_mutex_init(&mut_, 0); - } - ~Impl(){ - LIBC_SYMBOLS.pthread_mutex_destroy(&mut_); - } - pthread_mutex_t mut_; -}; - -Mutex::Mutex():impl_(new Impl) {} -Mutex::~Mutex() { delete impl_;} -void Mutex::acquire() { - LIBC_SYMBOLS.pthread_mutex_lock(&impl_->mut_); -} -void Mutex::release() { - LIBC_SYMBOLS.pthread_mutex_unlock(&impl_->mut_); -} - -// **************************************************************************** -// Atomics -int32_t atomic_post_incr(volatile int32_t* operand, int32_t incr) -{ - int32_t result; - __asm__ __volatile__( - "lock xaddl %0,%1\n" - : "=r"(result), "=m"(*operand) - : "0"(incr) - : "memory"); - return result; -} -int32_t atomic_fetch_store(volatile int32_t *ptr, int32_t value) -{ - int32_t result; - __asm__ __volatile__("lock xchgl %0,%1\n" - : "=r"(result), "=m"(*ptr) - : "0"(value) - : "memory"); - return result; -} -#else -int32_t atomic_post_incr(volatile int32_t* operand, int32_t incr){ - int32_t v=*operand; - *operand+=incr; - return v; -} -int32_t atomic_fetch_store(volatile int32_t *ptr, int32_t value) -{ - int32_t result=*ptr; - *ptr=value; - return result; -} -#endif // THREADED diff --git a/src/c/tests/ThreadingUtil.h b/src/c/tests/ThreadingUtil.h deleted file mode 100644 index 9165412bc8b..00000000000 --- a/src/c/tests/ThreadingUtil.h +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef THREADINGUTIL_H_ -#define THREADINGUTIL_H_ - -#include - -#ifdef THREADED -#include "pthread.h" -#endif - -// ***************************************************************************** -// Threading primitives - -// atomic post-increment; returns the previous value of the operand -int32_t atomic_post_incr(volatile int32_t* operand, int32_t incr); -// atomic fetch&store; returns the previous value of the operand -int32_t atomic_fetch_store(volatile int32_t *operand, int32_t value); - -// a partial implementation of an atomic integer type -class AtomicInt{ -public: - explicit AtomicInt(int32_t init=0):v_(init){} - AtomicInt(const AtomicInt& other):v_(other){} - // assigment - AtomicInt& operator=(const AtomicInt& lhs){ - atomic_fetch_store(&v_,lhs); - return *this; - } - AtomicInt& operator=(int32_t i){ - atomic_fetch_store(&v_,i); - return *this; - } - // pre-increment - AtomicInt& operator++() { - atomic_post_incr(&v_,1); - return *this; - } - // pre-decrement - AtomicInt& operator--() { - atomic_post_incr(&v_,-1); - return *this; - } - // post-increment - AtomicInt operator++(int){ - return AtomicInt(atomic_post_incr(&v_,1)); - } - // post-decrement - AtomicInt operator--(int){ - return AtomicInt(atomic_post_incr(&v_,-1)); - } - - operator int() const{ - return atomic_post_incr(&v_,0); - } - int get() const{ - return atomic_post_incr(&v_,0); - } -private: - mutable int32_t v_; -}; - -#ifdef THREADED -// **************************************************************************** -#define VALIDATE_JOBS(jm) jm.validateJobs(__FILE__,__LINE__) -#define VALIDATE_JOB(j) j.validate(__FILE__,__LINE__) - -class Mutex{ -public: - Mutex(); - ~Mutex(); - void acquire(); - void release(); -private: - Mutex(const Mutex&); - Mutex& operator=(const Mutex&); - struct Impl; - Impl* impl_; -}; - -class MTLock{ -public: - MTLock(Mutex& m):m_(m){m.acquire();} - ~MTLock(){m_.release();} - Mutex& m_; -}; - -#define synchronized(m) MTLock __lock(m) - -// **************************************************************************** -class Latch { -public: - virtual ~Latch() {} - virtual void await() const =0; - virtual void signalAndWait() =0; - virtual void signal() =0; -}; - -class CountDownLatch: public Latch { -public: - CountDownLatch(int count):count_(count) { - pthread_cond_init(&cond_,0); - pthread_mutex_init(&mut_,0); - } - virtual ~CountDownLatch() { - pthread_mutex_lock(&mut_); - if(count_!=0) { - count_=0; - pthread_cond_broadcast(&cond_); - } - pthread_mutex_unlock(&mut_); - - pthread_cond_destroy(&cond_); - pthread_mutex_destroy(&mut_); - } - - virtual void await() const { - pthread_mutex_lock(&mut_); - awaitImpl(); - pthread_mutex_unlock(&mut_); - } - virtual void signalAndWait() { - pthread_mutex_lock(&mut_); - signalImpl(); - awaitImpl(); - pthread_mutex_unlock(&mut_); - } - virtual void signal() { - pthread_mutex_lock(&mut_); - signalImpl(); - pthread_mutex_unlock(&mut_); - } -private: - void awaitImpl() const{ - while(count_!=0) - pthread_cond_wait(&cond_,&mut_); - } - void signalImpl() { - if(count_>0) { - count_--; - pthread_cond_broadcast(&cond_); - } - } - int count_; - mutable pthread_mutex_t mut_; - mutable pthread_cond_t cond_; -}; - -class TestJob { -public: - typedef long JobId; - TestJob():hasRun_(false),startLatch_(0),endLatch_(0) {} - virtual ~TestJob() { - join(); - } - virtual TestJob* clone() const =0; - - virtual void run() =0; - virtual void validate(const char* file, int line) const =0; - - virtual void start(Latch* startLatch=0,Latch* endLatch=0) { - startLatch_=startLatch;endLatch_=endLatch; - hasRun_=true; - pthread_create(&thread_, 0, thread, this); - } - virtual JobId getJobId() const { - return (JobId)thread_; - } - virtual void join() { - if(!hasRun_) - return; - if(!pthread_equal(thread_,pthread_self())) - pthread_join(thread_,0); - else - pthread_detach(thread_); - } -private: - void awaitStart() { - if(startLatch_==0) return; - startLatch_->signalAndWait(); - } - void signalFinished() { - if(endLatch_==0) return; - endLatch_->signal(); - } - static void* thread(void* p) { - TestJob* j=(TestJob*)p; - j->awaitStart(); // wait for the start command - j->run(); - j->signalFinished(); - return 0; - } - bool hasRun_; - Latch* startLatch_; - Latch* endLatch_; - pthread_t thread_; -}; - -class TestJobManager { - typedef std::vector JobList; -public: - TestJobManager(const TestJob& tj,int threadCount=1): - startLatch_(threadCount),endLatch_(threadCount) - { - for(int i=0;istart(&startLatch_,&endLatch_); - } - virtual void startJobsImmediately() { - for(unsigned i=0;istart(0,&endLatch_); - } - virtual void wait() const { - endLatch_.await(); - } - virtual void validateJobs(const char* file, int line) const{ - for(unsigned i=0;ivalidate(file,line); - } -private: - JobList jobs_; - CountDownLatch startLatch_; - CountDownLatch endLatch_; -}; - -#else // THREADED -// single THREADED -class Mutex{ -public: - void acquire(){} - void release(){} -}; -#define synchronized(m) - -#endif // THREADED - -#endif /*THREADINGUTIL_H_*/ diff --git a/src/c/tests/Util.cc b/src/c/tests/Util.cc deleted file mode 100644 index 2b9da84eb57..00000000000 --- a/src/c/tests/Util.cc +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Util.h" -#include "string.h" - -const std::string EMPTY_STRING; - -TestConfig globalTestConfig; - -void millisleep(int ms){ - timespec ts; - ts.tv_sec=ms/1000; - ts.tv_nsec=(ms%1000)*1000000; // to nanoseconds - nanosleep(&ts,0); -} - -FILE *openlogfile(const char* testname) { - char name[1024]; - strcpy(name, "TEST-"); - strncpy(name + 5, testname, sizeof(name) - 5); -#ifdef THREADED - strcpy(name + strlen(name), "-mt.txt"); -#else - strcpy(name + strlen(name), "-st.txt"); -#endif - - FILE *logfile = fopen(name, "a"); - - if (logfile == 0) { - fprintf(stderr, "Can't open log file %s!\n", name); - return 0; - } - - return logfile; -} diff --git a/src/c/tests/Util.h b/src/c/tests/Util.h deleted file mode 100644 index 01e21ce25aa..00000000000 --- a/src/c/tests/Util.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UTIL_H_ -#define UTIL_H_ - -#include -#include -#include - -#include "zookeeper_log.h" - -// number of elements in array -#define COUNTOF(array) sizeof(array)/sizeof(array[0]) - -#define DECLARE_WRAPPER(ret,sym,sig) \ - extern "C" ret __real_##sym sig; \ - extern "C" ret __wrap_##sym sig - -#define CALL_REAL(sym,params) \ - __real_##sym params - -// must include "src/zookeeper_log.h" to be able to use this macro -#define TEST_TRACE(x) \ - log_message(ZOO_LOG_LEVEL_DEBUG,__LINE__,__func__,format_log_message x) - -extern const std::string EMPTY_STRING; - -// ***************************************************************************** -// A bit of wizardry to get to the bare type from a reference or a pointer -// to the type -template -struct TypeOp { - typedef T BareT; - typedef T ArgT; -}; - -// partial specialization for reference types -template -struct TypeOp{ - typedef T& ArgT; - typedef typename TypeOp::BareT BareT; -}; - -// partial specialization for pointers -template -struct TypeOp{ - typedef T* ArgT; - typedef typename TypeOp::BareT BareT; -}; - -// ***************************************************************************** -// Container utilities - -template -void putValue(std::map& map,const K& k, const V& v){ - typedef std::map Map; - typename Map::const_iterator it=map.find(k); - if(it==map.end()) - map.insert(typename Map::value_type(k,v)); - else - map[k]=v; -} - -template -bool getValue(const std::map& map,const K& k,V& v){ - typedef std::map Map; - typename Map::const_iterator it=map.find(k); - if(it==map.end()) - return false; - v=it->second; - return true; -} - -// ***************************************************************************** -// misc utils - -// millisecond sleep -void millisleep(int ms); -FILE *openlogfile(const char* name); -// evaluate given predicate until it returns true or the timeout -// (in millis) has expired -template -int ensureCondition(const Predicate& p,int timeout){ - int elapsed=0; - while(!p() && elapsed CmdLineOptList; -public: - typedef CmdLineOptList::const_iterator const_iterator; - TestConfig(){} - ~TestConfig(){} - void addConfigFromCmdLine(int argc, char* argv[]){ - if(argc>=2) - testName_=argv[1]; - for(int i=2; i - -// function to conveniently stream vectors -template -std::ostream& operator<<(std::ostream& os,const std::vector& c){ - typedef std::vector V; - os<<"["; - if(c.size()>0){ - for(typename V::const_iterator it=c.begin();it!=c.end();++it) - os<<*it<<","; - os.seekp(-1,std::ios::cur); - } - os<<"]"; - return os; -} - -#endif // _VECTOR_UTIL_H diff --git a/src/c/tests/ZKMocks.cc b/src/c/tests/ZKMocks.cc deleted file mode 100644 index a75dce6c409..00000000000 --- a/src/c/tests/ZKMocks.cc +++ /dev/null @@ -1,519 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include // for htonl -#include - -#include -#include - -#ifdef THREADED -#include "PthreadMocks.h" -#endif -#include "ZKMocks.h" - -using namespace std; - -TestClientId testClientId; -const char* TestClientId::PASSWD="1234567890123456"; - -HandshakeRequest* HandshakeRequest::parse(const std::string& buf){ - auto_ptr req(new HandshakeRequest); - - memcpy(&req->protocolVersion,buf.data(), sizeof(req->protocolVersion)); - req->protocolVersion = htonl(req->protocolVersion); - - int offset=sizeof(req->protocolVersion); - - memcpy(&req->lastZxidSeen,buf.data()+offset,sizeof(req->lastZxidSeen)); - req->lastZxidSeen = htonll(req->lastZxidSeen); - offset+=sizeof(req->lastZxidSeen); - - memcpy(&req->timeOut,buf.data()+offset,sizeof(req->timeOut)); - req->timeOut = htonl(req->timeOut); - offset+=sizeof(req->timeOut); - - memcpy(&req->sessionId,buf.data()+offset,sizeof(req->sessionId)); - req->sessionId = htonll(req->sessionId); - offset+=sizeof(req->sessionId); - - memcpy(&req->passwd_len,buf.data()+offset,sizeof(req->passwd_len)); - req->passwd_len = htonl(req->passwd_len); - offset+=sizeof(req->passwd_len); - - memcpy(req->passwd,buf.data()+offset,sizeof(req->passwd)); - if(testClientId.client_id==req->sessionId && - !memcmp(testClientId.passwd,req->passwd,sizeof(req->passwd))) - return req.release(); - // the request didn't match -- may not be a handshake request after all - return 0; -} - -// ***************************************************************************** -// watcher action implementation -void activeWatcher(zhandle_t *zh, int type, int state, const char *path,void* ctx){ - if(zh==0 || ctx==0) return; - WatcherAction* action=(WatcherAction*)ctx; - - if(type==ZOO_SESSION_EVENT){ - if(state==ZOO_EXPIRED_SESSION_STATE) - action->onSessionExpired(zh); - else if(state==ZOO_CONNECTING_STATE) - action->onConnectionLost(zh); - else if(state==ZOO_CONNECTED_STATE) - action->onConnectionEstablished(zh); - }else if(type==ZOO_CHANGED_EVENT) - action->onNodeValueChanged(zh,path); - else if(type==ZOO_DELETED_EVENT) - action->onNodeDeleted(zh,path); - else if(type==ZOO_CHILD_EVENT) - action->onChildChanged(zh,path); - // TODO: implement for the rest of the event types - // ... - action->setWatcherTriggered(); -} -SyncedBoolCondition WatcherAction::isWatcherTriggered() const{ - return SyncedBoolCondition(triggered_,mx_); -} - -// ***************************************************************************** -// a set of async completion signatures -void asyncCompletion(int rc, ACL_vector *acl,Stat *stat, const void *data){ - assert("Completion data is NULL"&&data); - static_cast((void*)data)->aclCompl(rc,acl,stat); -} -void asyncCompletion(int rc, const char *value, int len, const Stat *stat, - const void *data){ - assert("Completion data is NULL"&&data); - static_cast((void*)data)->dataCompl(rc,value,len,stat); -} -void asyncCompletion(int rc, const Stat *stat, const void *data){ - assert("Completion data is NULL"&&data); - static_cast((void*)data)->statCompl(rc,stat); -} -void asyncCompletion(int rc, const char *value, const void *data){ - assert("Completion data is NULL"&&data); - static_cast((void*)data)->stringCompl(rc,value); -} -void asyncCompletion(int rc,const String_vector *strings, const void *data){ - assert("Completion data is NULL"&&data); - static_cast((void*)data)->stringsCompl(rc,strings); -} -void asyncCompletion(int rc, const void *data){ - assert("Completion data is NULL"&&data); - static_cast((void*)data)->voidCompl(rc); -} - -// ***************************************************************************** -// a predicate implementation -bool IOThreadStopped::operator()() const{ -#ifdef THREADED - adaptor_threads* adaptor=(adaptor_threads*)zh_->adaptor_priv; - return CheckedPthread::isTerminated(adaptor->io); -#else - assert("IOThreadStopped predicate is only for use with THREADED client"&& false); - return false; -#endif -} - -//****************************************************************************** -// -DECLARE_WRAPPER(int,flush_send_queue,(zhandle_t*zh, int timeout)) -{ - if(!Mock_flush_send_queue::mock_) - return CALL_REAL(flush_send_queue,(zh,timeout)); - return Mock_flush_send_queue::mock_->call(zh,timeout); -} - -Mock_flush_send_queue* Mock_flush_send_queue::mock_=0; - -//****************************************************************************** -// -DECLARE_WRAPPER(int32_t,get_xid,()) -{ - if(!Mock_get_xid::mock_) - return CALL_REAL(get_xid,()); - return Mock_get_xid::mock_->call(); -} - -Mock_get_xid* Mock_get_xid::mock_=0; - -//****************************************************************************** -// activateWatcher mock - -DECLARE_WRAPPER(void,activateWatcher,(zhandle_t *zh, watcher_registration_t* reg, int rc)) -{ - if(!Mock_activateWatcher::mock_){ - CALL_REAL(activateWatcher,(zh, reg,rc)); - }else{ - Mock_activateWatcher::mock_->call(zh, reg,rc); - } -} -Mock_activateWatcher* Mock_activateWatcher::mock_=0; - -class ActivateWatcherWrapper: public Mock_activateWatcher{ -public: - ActivateWatcherWrapper():ctx_(0),activated_(false){} - - virtual void call(zhandle_t *zh, watcher_registration_t* reg, int rc){ - CALL_REAL(activateWatcher,(zh, reg,rc)); - synchronized(mx_); - if(reg->context==ctx_){ - activated_=true; - ctx_=0; - } - } - - void setContext(void* ctx){ - synchronized(mx_); - ctx_=ctx; - activated_=false; - } - - SyncedBoolCondition isActivated() const{ - return SyncedBoolCondition(activated_,mx_); - } - mutable Mutex mx_; - void* ctx_; - bool activated_; -}; - -WatcherActivationTracker::WatcherActivationTracker(): - wrapper_(new ActivateWatcherWrapper) -{ -} - -WatcherActivationTracker::~WatcherActivationTracker(){ - delete wrapper_; -} - -void WatcherActivationTracker::track(void* ctx){ - wrapper_->setContext(ctx); -} - -SyncedBoolCondition WatcherActivationTracker::isWatcherActivated() const{ - return wrapper_->isActivated(); -} - -//****************************************************************************** -// -DECLARE_WRAPPER(void,deliverWatchers,(zhandle_t* zh,int type,int state, const char* path, watcher_object_list_t **list)) -{ - if(!Mock_deliverWatchers::mock_){ - CALL_REAL(deliverWatchers,(zh,type,state,path, list)); - }else{ - Mock_deliverWatchers::mock_->call(zh,type,state,path, list); - } -} - -Mock_deliverWatchers* Mock_deliverWatchers::mock_=0; - -struct RefCounterValue{ - RefCounterValue(zhandle_t* const& zh,int32_t expectedCounter,Mutex& mx): - zh_(zh),expectedCounter_(expectedCounter),mx_(mx){} - bool operator()() const{ - { - synchronized(mx_); - if(zh_==0) - return false; - } - return inc_ref_counter(zh_,0)==expectedCounter_; - } - zhandle_t* const& zh_; - int32_t expectedCounter_; - Mutex& mx_; -}; - - -class DeliverWatchersWrapper: public Mock_deliverWatchers{ -public: - DeliverWatchersWrapper(int type,int state,bool terminate): - type_(type),state_(state), - allDelivered_(false),terminate_(terminate),zh_(0),deliveryCounter_(0){} - virtual void call(zhandle_t* zh,int type,int state, const char* path, watcher_object_list **list){ - { - synchronized(mx_); - zh_=zh; - allDelivered_=false; - } - CALL_REAL(deliverWatchers,(zh,type,state,path, list)); - if(type_==type && state_==state){ - if(terminate_){ - // prevent zhandle_t from being prematurely distroyed; - // this will also ensure that zookeeper_close() cleanups the thread - // resources by calling finish_adaptor() - inc_ref_counter(zh,1); - terminateZookeeperThreads(zh); - } - synchronized(mx_); - allDelivered_=true; - deliveryCounter_++; - } - } - SyncedBoolCondition isDelivered() const{ - if(terminate_){ - int i=ensureCondition(RefCounterValue(zh_,1,mx_),1000); - assert(i<1000); - } - return SyncedBoolCondition(allDelivered_,mx_); - } - void resetDeliveryCounter(){ - synchronized(mx_); - deliveryCounter_=0; - } - SyncedIntegerEqual deliveryCounterEquals(int expected) const{ - if(terminate_){ - int i=ensureCondition(RefCounterValue(zh_,1,mx_),1000); - assert(i<1000); - } - return SyncedIntegerEqual(deliveryCounter_,expected,mx_); - } - int type_; - int state_; - mutable Mutex mx_; - bool allDelivered_; - bool terminate_; - zhandle_t* zh_; - int deliveryCounter_; -}; - -WatcherDeliveryTracker::WatcherDeliveryTracker( - int type,int state,bool terminateCompletionThread): - deliveryWrapper_(new DeliverWatchersWrapper( - type,state,terminateCompletionThread)){ -} - -WatcherDeliveryTracker::~WatcherDeliveryTracker(){ - delete deliveryWrapper_; -} - -SyncedBoolCondition WatcherDeliveryTracker::isWatcherProcessingCompleted() const{ - return deliveryWrapper_->isDelivered(); -} - -void WatcherDeliveryTracker::resetDeliveryCounter(){ - deliveryWrapper_->resetDeliveryCounter(); -} - -SyncedIntegerEqual WatcherDeliveryTracker::deliveryCounterEquals(int expected) const{ - return deliveryWrapper_->deliveryCounterEquals(expected); -} - -//****************************************************************************** -// -string HandshakeResponse::toString() const { - string buf; - int32_t tmp=htonl(protocolVersion); - buf.append((char*)&tmp,sizeof(tmp)); - tmp=htonl(timeOut); - buf.append((char*)&tmp,sizeof(tmp)); - int64_t tmp64=htonll(sessionId); - buf.append((char*)&tmp64,sizeof(sessionId)); - tmp=htonl(passwd_len); - buf.append((char*)&tmp,sizeof(tmp)); - buf.append(passwd,sizeof(passwd)); - // finally set the buffer length - tmp=htonl(buf.size()+sizeof(tmp)); - buf.insert(0,(char*)&tmp, sizeof(tmp)); - return buf; -} - -string ZooGetResponse::toString() const{ - oarchive* oa=create_buffer_oarchive(); - - ReplyHeader h = {xid_,1,ZOK}; - serialize_ReplyHeader(oa, "hdr", &h); - - GetDataResponse resp; - char buf[1024]; - assert("GetDataResponse is too long"&&data_.size()<=sizeof(buf)); - resp.data.len=data_.size(); - resp.data.buff=buf; - data_.copy(resp.data.buff, data_.size()); - resp.stat=stat_; - serialize_GetDataResponse(oa, "reply", &resp); - int32_t len=htonl(get_buffer_len(oa)); - string res((char*)&len,sizeof(len)); - res.append(get_buffer(oa),get_buffer_len(oa)); - - close_buffer_oarchive(&oa,1); - return res; -} - -string ZooStatResponse::toString() const{ - oarchive* oa=create_buffer_oarchive(); - - ReplyHeader h = {xid_,1,rc_}; - serialize_ReplyHeader(oa, "hdr", &h); - - SetDataResponse resp; - resp.stat=stat_; - serialize_SetDataResponse(oa, "reply", &resp); - int32_t len=htonl(get_buffer_len(oa)); - string res((char*)&len,sizeof(len)); - res.append(get_buffer(oa),get_buffer_len(oa)); - - close_buffer_oarchive(&oa,1); - return res; -} - -string ZooGetChildrenResponse::toString() const{ - oarchive* oa=create_buffer_oarchive(); - - ReplyHeader h = {xid_,1,rc_}; - serialize_ReplyHeader(oa, "hdr", &h); - - GetChildrenResponse resp; - // populate the string vector - allocate_String_vector(&resp.children,strings_.size()); - for(int i=0;i<(int)strings_.size();++i) - resp.children.data[i]=strdup(strings_[i].c_str()); - serialize_GetChildrenResponse(oa, "reply", &resp); - deallocate_GetChildrenResponse(&resp); - - int32_t len=htonl(get_buffer_len(oa)); - string res((char*)&len,sizeof(len)); - res.append(get_buffer(oa),get_buffer_len(oa)); - - close_buffer_oarchive(&oa,1); - return res; -} - -string ZNodeEvent::toString() const{ - oarchive* oa=create_buffer_oarchive(); - struct WatcherEvent evt = {type_,0,(char*)path_.c_str()}; - struct ReplyHeader h = {WATCHER_EVENT_XID,0,ZOK }; - - serialize_ReplyHeader(oa, "hdr", &h); - serialize_WatcherEvent(oa, "event", &evt); - - int32_t len=htonl(get_buffer_len(oa)); - string res((char*)&len,sizeof(len)); - res.append(get_buffer(oa),get_buffer_len(oa)); - - close_buffer_oarchive(&oa,1); - return res; -} - -string PingResponse::toString() const{ - oarchive* oa=create_buffer_oarchive(); - - ReplyHeader h = {PING_XID,1,ZOK}; - serialize_ReplyHeader(oa, "hdr", &h); - - int32_t len=htonl(get_buffer_len(oa)); - string res((char*)&len,sizeof(len)); - res.append(get_buffer(oa),get_buffer_len(oa)); - - close_buffer_oarchive(&oa,1); - return res; -} - -//****************************************************************************** -// Zookeeper server simulator -// -bool ZookeeperServer::hasMoreRecv() const{ - return recvHasMore.get()!=0 || connectionLost; -} - -ssize_t ZookeeperServer::callRecv(int s,void *buf,size_t len,int flags){ - if(connectionLost){ - recvReturnBuffer.erase(); - return 0; - } - // done transmitting the current buffer? - if(recvReturnBuffer.size()==0){ - synchronized(recvQMx); - if(recvQueue.empty()){ - recvErrno=EAGAIN; - return Mock_socket::callRecv(s,buf,len,flags); - } - --recvHasMore; - Element& el=recvQueue.front(); - if(el.first!=0){ - recvReturnBuffer=el.first->toString(); - delete el.first; - } - recvErrno=el.second; - recvQueue.pop_front(); - } - return Mock_socket::callRecv(s,buf,len,flags); -} - -void ZookeeperServer::onMessageReceived(const RequestHeader& rh, iarchive* ia){ - // no-op by default -} - -void ZookeeperServer::notifyBufferSent(const std::string& buffer){ - if(HandshakeRequest::isValid(buffer)){ - // could be a connect request - auto_ptr req(HandshakeRequest::parse(buffer)); - if(req.get()!=0){ - // handle the handshake - int64_t sessId=sessionExpired?req->sessionId+1:req->sessionId; - sessionExpired=false; - addRecvResponse(new HandshakeResponse(sessId)); - return; - } - // not a connect request -- fall thru - } - // parse the buffer to extract the request type and its xid - iarchive *ia=create_buffer_iarchive((char*)buffer.data(), buffer.size()); - RequestHeader rh; - deserialize_RequestHeader(ia,"hdr",&rh); - // notify the "server" a client request has arrived - if (rh.xid == -8) { - Element e = Element(new ZooStatResponse,0); - e.first->setXID(-8); - addRecvResponse(e); - close_buffer_iarchive(&ia); - return; - } else { - onMessageReceived(rh,ia); - } - close_buffer_iarchive(&ia); - if(rh.type==CLOSE_OP){ - ++closeSent; - return; // no reply for close requests - } - // get the next response from the response queue and append it to the receive list - Element e; - { - synchronized(respQMx); - if(respQueue.empty()) - return; - e=respQueue.front(); - respQueue.pop_front(); - } - e.first->setXID(rh.xid); - addRecvResponse(e); -} - -void forceConnected(zhandle_t* zh){ - // simulate connected state - zh->state=ZOO_CONNECTED_STATE; - zh->fd=ZookeeperServer::FD; - zh->input_buffer=0; - gettimeofday(&zh->last_recv,0); - gettimeofday(&zh->last_send,0); -} - -void terminateZookeeperThreads(zhandle_t* zh){ - // this will cause the zookeeper threads to terminate - zh->close_requested=1; -} diff --git a/src/c/tests/ZKMocks.h b/src/c/tests/ZKMocks.h deleted file mode 100644 index fbcfc4f0804..00000000000 --- a/src/c/tests/ZKMocks.h +++ /dev/null @@ -1,509 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ZKMOCKS_H_ -#define ZKMOCKS_H_ - -#include -#include "src/zk_adaptor.h" - -#include "Util.h" -#include "LibCMocks.h" -#include "MocksBase.h" - -// ***************************************************************************** -// sets internal zhandle_t members to certain values to simulate the client -// connected state. This function should only be used with the single-threaded -// Async API tests! -void forceConnected(zhandle_t* zh); - -/** - * Gracefully terminates zookeeper I/O and completion threads. - */ -void terminateZookeeperThreads(zhandle_t* zh); - -// ***************************************************************************** -// Abstract watcher action -struct SyncedBoolCondition; - -class WatcherAction{ -public: - WatcherAction():triggered_(false){} - virtual ~WatcherAction(){} - - virtual void onSessionExpired(zhandle_t*){} - virtual void onConnectionEstablished(zhandle_t*){} - virtual void onConnectionLost(zhandle_t*){} - virtual void onNodeValueChanged(zhandle_t*,const char* path){} - virtual void onNodeDeleted(zhandle_t*,const char* path){} - virtual void onChildChanged(zhandle_t*,const char* path){} - - SyncedBoolCondition isWatcherTriggered() const; - void setWatcherTriggered(){ - synchronized(mx_); - triggered_=true; - } - -protected: - mutable Mutex mx_; - bool triggered_; -}; -// zh->context is a pointer to a WatcherAction instance -// based on the event type and state, the watcher calls a specific watcher -// action method -void activeWatcher(zhandle_t *zh, int type, int state, const char *path,void* ctx); - -// ***************************************************************************** -// a set of async completion signatures -class AsyncCompletion{ -public: - virtual ~AsyncCompletion(){} - virtual void aclCompl(int rc, ACL_vector *acl,Stat *stat){} - virtual void dataCompl(int rc, const char *value, int len, const Stat *stat){} - virtual void statCompl(int rc, const Stat *stat){} - virtual void stringCompl(int rc, const char *value){} - virtual void stringsCompl(int rc,const String_vector *strings){} - virtual void voidCompl(int rc){} -}; -void asyncCompletion(int rc, ACL_vector *acl,Stat *stat, const void *data); -void asyncCompletion(int rc, const char *value, int len, const Stat *stat, - const void *data); -void asyncCompletion(int rc, const Stat *stat, const void *data); -void asyncCompletion(int rc, const char *value, const void *data); -void asyncCompletion(int rc,const String_vector *strings, const void *data); -void asyncCompletion(int rc, const void *data); - -// ***************************************************************************** -// some common predicates to use with ensureCondition(): -// checks if the connection is established -struct ClientConnected{ - ClientConnected(zhandle_t* zh):zh_(zh){} - bool operator()() const{ - return zoo_state(zh_)==ZOO_CONNECTED_STATE; - } - zhandle_t* zh_; -}; -// check in the session expired -struct SessionExpired{ - SessionExpired(zhandle_t* zh):zh_(zh){} - bool operator()() const{ - return zoo_state(zh_)==ZOO_EXPIRED_SESSION_STATE; - } - zhandle_t* zh_; -}; -// checks if the IO thread has stopped; CheckedPthread must be active -struct IOThreadStopped{ - IOThreadStopped(zhandle_t* zh):zh_(zh){} - bool operator()() const; - zhandle_t* zh_; -}; - -// a synchronized boolean condition -struct SyncedBoolCondition{ - SyncedBoolCondition(const bool& cond,Mutex& mx):cond_(cond),mx_(mx){} - bool operator()() const{ - synchronized(mx_); - return cond_; - } - const bool& cond_; - Mutex& mx_; -}; - -// a synchronized integer comparison -struct SyncedIntegerEqual{ - SyncedIntegerEqual(const int& cond,int expected,Mutex& mx): - cond_(cond),expected_(expected),mx_(mx){} - bool operator()() const{ - synchronized(mx_); - return cond_==expected_; - } - const int& cond_; - const int expected_; - Mutex& mx_; -}; - -// ***************************************************************************** -// make sure to call zookeeper_close() even in presence of exceptions -struct CloseFinally{ - CloseFinally(zhandle_t** zh):zh_(zh){} - ~CloseFinally(){ - execute(); - } - int execute(){ - if(zh_==0)return ZOK; - zhandle_t* lzh=*zh_; - *zh_=0; - disarm(); - return zookeeper_close(lzh); - } - void disarm(){zh_=0;} - zhandle_t ** zh_; -}; - -struct TestClientId: clientid_t{ - static const int SESSION_ID=123456789; - static const char* PASSWD; - TestClientId(){ - client_id=SESSION_ID; - memcpy(passwd,PASSWD,sizeof(passwd)); - } -}; - -// ***************************************************************************** -// special client id recongnized by the ZK server simulator -extern TestClientId testClientId; -#define TEST_CLIENT_ID &testClientId - -// ***************************************************************************** -// -struct HandshakeRequest: public connect_req -{ - static HandshakeRequest* parse(const std::string& buf); - static bool isValid(const std::string& buf){ - // this is just quick and dirty check before we go and parse the request - return buf.size()==HANDSHAKE_REQ_SIZE; - } -}; - -// ***************************************************************************** -// flush_send_queue -class Mock_flush_send_queue: public Mock -{ -public: - Mock_flush_send_queue():counter(0),callReturns(ZOK){mock_=this;} - ~Mock_flush_send_queue(){mock_=0;} - - int counter; - int callReturns; - virtual int call(zhandle_t* zh, int timeout){ - counter++; - return callReturns; - } - - static Mock_flush_send_queue* mock_; -}; - -// ***************************************************************************** -// get_xid -class Mock_get_xid: public Mock -{ -public: - static const int32_t XID=123456; - Mock_get_xid(int retValue=XID):callReturns(retValue){mock_=this;} - ~Mock_get_xid(){mock_=0;} - - int callReturns; - virtual int call(){ - return callReturns; - } - - static Mock_get_xid* mock_; -}; - -// ***************************************************************************** -// activateWatcher -class Mock_activateWatcher: public Mock{ -public: - Mock_activateWatcher(){mock_=this;} - virtual ~Mock_activateWatcher(){mock_=0;} - - virtual void call(zhandle_t *zh, watcher_registration_t* reg, int rc){} - static Mock_activateWatcher* mock_; -}; - -class ActivateWatcherWrapper; -class WatcherActivationTracker{ -public: - WatcherActivationTracker(); - ~WatcherActivationTracker(); - - void track(void* ctx); - SyncedBoolCondition isWatcherActivated() const; -private: - ActivateWatcherWrapper* wrapper_; -}; - -// ***************************************************************************** -// deliverWatchers -class Mock_deliverWatchers: public Mock{ -public: - Mock_deliverWatchers(){mock_=this;} - virtual ~Mock_deliverWatchers(){mock_=0;} - - virtual void call(zhandle_t* zh,int type,int state, const char* path, watcher_object_list **){} - static Mock_deliverWatchers* mock_; -}; - -class DeliverWatchersWrapper; -class WatcherDeliveryTracker{ -public: - // filters deliveries by state and type - WatcherDeliveryTracker(int type,int state,bool terminateCompletionThread=true); - ~WatcherDeliveryTracker(); - - // if the thread termination requested (see the ctor params) - // this function will wait for the I/O and completion threads to - // terminate before returning a SyncBoolCondition instance - SyncedBoolCondition isWatcherProcessingCompleted() const; - void resetDeliveryCounter(); - SyncedIntegerEqual deliveryCounterEquals(int expected) const; -private: - DeliverWatchersWrapper* deliveryWrapper_; -}; - -// ***************************************************************************** -// a zookeeper Stat wrapper -struct NodeStat: public Stat -{ - NodeStat(){ - czxid=0; - mzxid=0; - ctime=0; - mtime=0; - version=1; - cversion=0; - aversion=0; - ephemeralOwner=0; - } - NodeStat(const Stat& other){ - memcpy(this,&other,sizeof(*this)); - } -}; - -// ***************************************************************************** -// Abstract server Response -class Response -{ -public: - virtual ~Response(){} - - virtual void setXID(int32_t xid){} - // this method is used by the ZookeeperServer class to serialize - // the instance of Response - virtual std::string toString() const =0; -}; - -// ***************************************************************************** -// Handshake response -class HandshakeResponse: public Response -{ -public: - HandshakeResponse(int64_t sessId=1) - :protocolVersion(1),timeOut(10000),sessionId(sessId),passwd_len(sizeof(passwd)) - { - memcpy(passwd,"1234567890123456",sizeof(passwd)); - } - int32_t protocolVersion; - int32_t timeOut; - int64_t sessionId; - int32_t passwd_len; - char passwd[16]; - virtual std::string toString() const ; -}; - -// zoo_get() response -class ZooGetResponse: public Response -{ -public: - ZooGetResponse(const char* data, int len,int32_t xid=0,int rc=ZOK,const Stat& stat=NodeStat()) - :xid_(xid),data_(data,len),rc_(rc),stat_(stat) - { - } - virtual std::string toString() const; - virtual void setXID(int32_t xid) {xid_=xid;} - -private: - int32_t xid_; - std::string data_; - int rc_; - Stat stat_; -}; - -// zoo_exists(), zoo_set() response -class ZooStatResponse: public Response -{ -public: - ZooStatResponse(int32_t xid=0,int rc=ZOK,const Stat& stat=NodeStat()) - :xid_(xid),rc_(rc),stat_(stat) - { - } - virtual std::string toString() const; - virtual void setXID(int32_t xid) {xid_=xid;} - -private: - int32_t xid_; - int rc_; - Stat stat_; -}; - -// zoo_get_children() -class ZooGetChildrenResponse: public Response -{ -public: - typedef std::vector StringVector; - ZooGetChildrenResponse(const StringVector& v,int rc=ZOK): - xid_(0),strings_(v),rc_(rc) - { - } - - virtual std::string toString() const; - virtual void setXID(int32_t xid) {xid_=xid;} - - int32_t xid_; - StringVector strings_; - int rc_; -}; - -// PING response -class PingResponse: public Response -{ -public: - virtual std::string toString() const; -}; - -// watcher znode event -class ZNodeEvent: public Response -{ -public: - ZNodeEvent(int type,const char* path):type_(type),path_(path){} - - virtual std::string toString() const; - -private: - int type_; - std::string path_; -}; - -// **************************************************************************** -// Zookeeper server simulator - -class ZookeeperServer: public Mock_socket -{ -public: - ZookeeperServer(): - serverDownSkipCount_(-1),sessionExpired(false),connectionLost(false) - { - connectReturns=-1; - connectErrno=EWOULDBLOCK; - } - virtual ~ZookeeperServer(){ - clearRecvQueue(); - clearRespQueue(); - } - virtual int callClose(int fd){ - if(fd!=FD) - return LIBC_SYMBOLS.close(fd); - clearRecvQueue(); - clearRespQueue(); - return Mock_socket::callClose(fd); - } - // connection handling - // what to do when the handshake request comes in? - int serverDownSkipCount_; - // this will cause getsockopt(zh->fd,SOL_SOCKET,SO_ERROR,&error,&len) return - // a failure after skipCount dropped to zero, thus simulating a server down - // condition - // passing skipCount==-1 will make every connect attempt succeed - void setServerDown(int skipCount=0){ - serverDownSkipCount_=skipCount; - optvalSO_ERROR=0; - } - virtual void setSO_ERROR(void *optval,socklen_t len){ - if(serverDownSkipCount_!=-1){ - if(serverDownSkipCount_==0) - optvalSO_ERROR=ECONNREFUSED; - else - serverDownSkipCount_--; - } - Mock_socket::setSO_ERROR(optval,len); - } - - // this is a trigger that gets reset back to false - // a connect request will return a non-matching session id thus causing - // the client throw SESSION_EXPIRED - volatile bool sessionExpired; - void returnSessionExpired(){ sessionExpired=true; } - - // this is a one shot trigger that gets reset back to false - // next recv call will return 0 length, thus simulating a connecton loss - volatile bool connectionLost; - void setConnectionLost() {connectionLost=true;} - - // recv - // this queue is used for server responses: client's recv() system call - // returns next available message from this queue - typedef std::pair Element; - typedef std::deque ResponseList; - ResponseList recvQueue; - mutable Mutex recvQMx; - AtomicInt recvHasMore; - ZookeeperServer& addRecvResponse(Response* resp, int errnum=0){ - synchronized(recvQMx); - recvQueue.push_back(Element(resp,errnum)); - ++recvHasMore; - return *this; - } - ZookeeperServer& addRecvResponse(int errnum){ - synchronized(recvQMx); - recvQueue.push_back(Element(0,errnum)); - ++recvHasMore; - return *this; - } - ZookeeperServer& addRecvResponse(const Element& e){ - synchronized(recvQMx); - recvQueue.push_back(e); - ++recvHasMore; - return *this; - } - void clearRecvQueue(){ - synchronized(recvQMx); - recvHasMore=0; - for(unsigned i=0; i /tmp/zk.log & - pid=$! - echo -n $! > /tmp/zk.pid - else - mkdir -p "${base_dir}/build/tmp/zkdata" - java -cp "$CLASSPATH" org.apache.zookeeper.server.ZooKeeperServerMain 22181 "${base_dir}/build/tmp/zkdata" 3000 $ZKMAXCNXNS &> "${base_dir}/build/tmp/zk.log" & - pid=$! - echo -n $pid > "${base_dir}/build/tmp/zk.pid" - fi - - # wait max 120 seconds for server to be ready to server clients - # this handles testing on slow hosts - success=false - for i in {1..40} - do - if ps -p $pid > /dev/null - then - if java -cp "$CLASSPATH" org.apache.zookeeper.ZooKeeperMain -server localhost:22181 ls / > /dev/null 2>&1 - then - # server not up yet - wait - sleep 5 - else - # server is up and serving client connections - success=true - break - fi - else - # server died - exit now - echo -n " ZooKeeper server process failed" - break - fi - done - - if $success - then - ## in case for debug, but generally don't use as it messes up the - ## console test output - echo -n " ZooKeeper server started" - else - echo -n " ZooKeeper server NOT started" - fi - - ;; -stop) - # Already killed above - ;; -*) - echo "Unknown command " + $1 - exit 2 -esac - diff --git a/src/contrib/bookkeeper/README.txt b/src/contrib/bookkeeper/README.txt deleted file mode 100644 index 910bd54e2d6..00000000000 --- a/src/contrib/bookkeeper/README.txt +++ /dev/null @@ -1,62 +0,0 @@ -BookKeeper README - -1- Overview -BookKeeper is a highly available logging service. As many critical services rely upon write-ahead logs to provide persistence along with high performance, an alternative to make such a service highly available despite the failures of individual servers it to offload write-ahead logs to an external service. - -This is exactly what BookKeeper provides. With BookKeeper, a service (or application) writes to a set of servers dedicated to storing such logs. An example of such an application is the Namenode of the Hadoop Distributed File System. - -The main components of BookKeeper are: -* Client: Applications interact with BookKeeper through the interface of of a BookKeeper client; -* Ledger: A ledger is our equivalent to a log file. Clients read entries from and write entries to ledgers; -* Bookie: Bookies are BookKeeper servers and they store the content of ledgers. Typically there are multiple bookies implementing a ledger. - -2- How to compile -Run "ant" from "trunk/contrib/bookkeeper". This will generate the bookkeeper jar in "trunk/build/contrib/bookkeeper". - -3- Setting up - -A typical BookKeeper configuration includes a set of bookies and a ZooKeeper ensemble, where the ZooKeeper instance stores metadata for BookKeeper. As an example of such metadata, BookKeeper clients learn about available bookies by consulting a ZooKeeper service. - -To set up BookKeeper, follow these steps: -* Once bookies and ZooKeeper servers are running, create two znodes: "/ledgers" and "/ledgers/available". -* To run a bookie, run the java class "org.apache.bookkeeper.proto.BookieServer". It takes 3 parameters: a port, one directory path for transaction logs, and one directory path for indexes and data. Here is an example: java -cp .:bookkeeper.jar:../ZooKeeper/zookeeper-.jar:/usr/local/apache-log4j-1.2.15/log4j-1.2.15.jar -Dlog4j.configuration=log4j.properties org.apache.bookkeeper.proto.BookieServer 3181 /disk1/bk/ /disk2/bk/ -* For each bookie b, if is the host name of b and is the bookie port, then create a znode "/ledgers/available/:". -* It is ready to run! - -For test purposes, there is a class named "org.apache.bookkeeper.util.LocalBookkeeper" which runs a custom number on BookKeeper servers, along with a ZooKeeper server, on a single node. A typical invocation would be: -java -cp: org.apache.bookkeeper.util.LocalBookKeeper - -4- Developing applications - -BookKeeper is written in Java. When implementing an application that uses BookKeeper, follow these steps: - -a. Instantiate a BookKeeper object. The single parameter to the BookKeeper constructor is a list of ZooKeeper servers; -b. Once we have a BookKeeper object, we can create a ledger with createLedger. The default call to createLedger takes a single parameter, which is supposed to be for password authentication, but currently it has no effect. A call to createLedger returns a ledger handle (type LedgerHandle); -c. Once we have a ledger, we can write to the ledger by calling either addEntry or asyncAddEntry. The first call is synchronous, whereas the second call is asynchronous, and both write byte arrays as entries. To use the asynchronous version, the application has to implement the AddCallback interface; -d. Ideally, once the application finishes writing to the ledger, it should close it by calling close on the ledger handle. If it doesn't then BookKeeper will try to recover the ledger when a client tries to open it. By closing the ledger properly, we avoid this recovery step, which is recommended but not mandatory; -e. Before reading from a ledger, a client has to open it by calling openLedger on a BookKeeper object, and readEntries or asycnReadEntries to read entries. Both read calls take as input two entry numbers, n1 and n2, and return all entries from n1 through n2. - -Here is a simple example of a method that creates a BookKeeper object, creates a ledger, writes an entry to the ledger, and closes it: - -BookKeeper bk; -LedgerHandle lh; - -public void allInOne(String servers) throws KeeperException, IOException, InterruptedException{ - bk = new BookKeeper(servers); - try{ - lh = bk.createLedger(new byte[] {'a', 'b'}); - bk.addEntry(lh, new byte[]{'a', 'b'}); - bk.close(lh); - } catch (BKException e) { - e.printStackTrace(); - } - } - -5- Selecting quorum mode and number of bookies (advanced) - -There are two methods to store ledgers with BookKeeper: - -a. Self-verifying: Each entry includes a digest that is used to guarantee that upon a read, the value read is the same as the one written. This mode requires n > 2t bookies, and quorums of size t + 1. By default, a call to createLedger uses this method and 3 servers; -b. Generic: Entries do not include a digest, and it requires more replicas: n > 3t and quorums of size 2t + 1. - -The quorum mode and number of bookies can be selected through the createLedger method. diff --git a/src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/MySqlClient.java b/src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/MySqlClient.java deleted file mode 100644 index 84f9330375c..00000000000 --- a/src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/MySqlClient.java +++ /dev/null @@ -1,137 +0,0 @@ -package org.apache.bookkeeper.benchmark; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.FileOutputStream; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; - -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.QuorumEngine; -import org.apache.log4j.Logger; - - -import org.apache.zookeeper.KeeperException; - -public class MySqlClient { - static Logger LOG = Logger.getLogger(QuorumEngine.class); - - BookKeeper x; - LedgerHandle lh; - Integer entryId; - HashMap map; - - FileOutputStream fStream; - FileOutputStream fStreamLocal; - long start, lastId; - Connection con; - Statement stmt; - - - public MySqlClient(String hostport, String user, String pass) - throws ClassNotFoundException { - entryId = 0; - map = new HashMap(); - Class.forName("com.mysql.jdbc.Driver"); - // database is named "bookkeeper" - String url = "jdbc:mysql://" + hostport + "/bookkeeper"; - try { - con = DriverManager.getConnection(url, user, pass); - stmt = con.createStatement(); - // drop table and recreate it - stmt.execute("DROP TABLE IF EXISTS data;"); - stmt.execute("create table data(transaction_id bigint PRIMARY KEY AUTO_INCREMENT, content TEXT);"); - LOG.info("Database initialization terminated"); - } catch (SQLException e) { - - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - public void closeHandle() throws KeeperException, InterruptedException, SQLException{ - con.close(); - } - /** - * First parameter is an integer defining the length of the message - * Second parameter is the number of writes - * Third parameter is host:port - * Fourth parameter is username - * Fifth parameter is password - * @param args - * @throws ClassNotFoundException - * @throws SQLException - */ - public static void main(String[] args) throws ClassNotFoundException, SQLException { - int lenght = Integer.parseInt(args[1]); - StringBuilder sb = new StringBuilder(); - while(lenght-- > 0){ - sb.append('a'); - } - try { - MySqlClient c = new MySqlClient(args[2], args[3], args[4]); - c.writeSameEntryBatch(sb.toString().getBytes(), Integer.parseInt(args[0])); - c.writeSameEntry(sb.toString().getBytes(), Integer.parseInt(args[0])); - c.closeHandle(); - } catch (NumberFormatException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (KeeperException e) { - e.printStackTrace(); - } - - } - - /** - * Adds data entry to the DB - * @param data the entry to be written, given as a byte array - * @param times the number of times the entry should be written on the DB */ - void writeSameEntryBatch(byte[] data, int times) throws InterruptedException, SQLException{ - start = System.currentTimeMillis(); - int count = times; - String content = new String(data); - System.out.println("Data: " + content + ", " + data.length); - while(count-- > 0){ - stmt.addBatch("insert into data(content) values(\"" + content + "\");"); - } - LOG.info("Finished writing batch SQL command in ms: " + (System.currentTimeMillis() - start)); - start = System.currentTimeMillis(); - stmt.executeBatch(); - System.out.println("Finished " + times + " writes in ms: " + (System.currentTimeMillis() - start)); - LOG.info("Ended computation"); - } - - void writeSameEntry(byte[] data, int times) throws InterruptedException, SQLException{ - start = System.currentTimeMillis(); - int count = times; - String content = new String(data); - System.out.println("Data: " + content + ", " + data.length); - while(count-- > 0){ - stmt.executeUpdate("insert into data(content) values(\"" + content + "\");"); - } - System.out.println("Finished " + times + " writes in ms: " + (System.currentTimeMillis() - start)); - LOG.info("Ended computation"); - } - -} \ No newline at end of file diff --git a/src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/TestClient.java b/src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/TestClient.java deleted file mode 100644 index 5fbf8da6270..00000000000 --- a/src/contrib/bookkeeper/benchmark/org/apache/bookkeeper/benchmark/TestClient.java +++ /dev/null @@ -1,252 +0,0 @@ -package org.apache.bookkeeper.benchmark; -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.HashMap; - -import org.apache.bookkeeper.client.AddCallback; -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.LedgerSequence; -import org.apache.bookkeeper.client.QuorumEngine; -import org.apache.bookkeeper.client.ReadCallback; -import org.apache.bookkeeper.client.LedgerHandle.QMode; -import org.apache.log4j.Logger; - -import org.apache.zookeeper.KeeperException; - -/** - * This is a simple test program to compare the performance of writing to - * BookKeeper and to the local file system. - * - */ - -public class TestClient - implements AddCallback, ReadCallback{ - private static final Logger LOG = Logger.getLogger(TestClient.class); - - BookKeeper x; - LedgerHandle lh; - Integer entryId; - HashMap map; - - FileOutputStream fStream; - FileOutputStream fStreamLocal; - long start, lastId; - - public TestClient() { - entryId = 0; - map = new HashMap(); - } - - public TestClient(String servers) throws KeeperException, IOException, InterruptedException{ - this(); - x = new BookKeeper(servers); - try{ - lh = x.createLedger(new byte[] {'a', 'b'}); - } catch (BKException e) { - LOG.error(e.toString()); - } - } - - public TestClient(String servers, int ensSize, int qSize) - throws KeeperException, IOException, InterruptedException{ - this(); - x = new BookKeeper(servers); - try{ - lh = x.createLedger(ensSize, qSize, QMode.VERIFIABLE, new byte[] {'a', 'b'}); - } catch (BKException e) { - LOG.error(e.toString()); - } - } - - public TestClient(FileOutputStream fStream) - throws FileNotFoundException { - this.fStream = fStream; - this.fStreamLocal = new FileOutputStream("./local.log"); - } - - - public Integer getFreshEntryId(int val){ - ++this.entryId; - synchronized (map) { - map.put(this.entryId, val); - } - return this.entryId; - } - - public boolean removeEntryId(Integer id){ - boolean retVal = false; - synchronized (map) { - map.remove(id); - retVal = true; - - if(map.size() == 0) map.notifyAll(); - else{ - if(map.size() < 4) - LOG.error(map.toString()); - } - } - return retVal; - } - - public void closeHandle() throws KeeperException, InterruptedException{ - x.closeLedger(lh); - } - /** - * First says if entries should be written to BookKeeper (0) or to the local - * disk (1). Second parameter is an integer defining the length of a ledger entry. - * Third parameter is the number of writes. - * - * @param args - */ - public static void main(String[] args) { - - int lenght = Integer.parseInt(args[1]); - StringBuilder sb = new StringBuilder(); - while(lenght-- > 0){ - sb.append('a'); - } - - Integer selection = Integer.parseInt(args[0]); - switch(selection){ - case 0: - StringBuilder servers_sb = new StringBuilder(); - for (int i = 4; i < args.length; i++){ - servers_sb.append(args[i] + " "); - } - - String servers = servers_sb.toString().trim().replace(' ', ','); - try { - TestClient c = new TestClient(servers, Integer.parseInt(args[3]), Integer.parseInt(args[4])); - c.writeSameEntryBatch(sb.toString().getBytes(), Integer.parseInt(args[2])); - //c.writeConsecutiveEntriesBatch(Integer.parseInt(args[0])); - c.closeHandle(); - } catch (NumberFormatException e) { - LOG.error(e); - } catch (InterruptedException e) { - LOG.error(e); - } catch (KeeperException e) { - LOG.error(e); - } catch (IOException e) { - LOG.error(e); - } - break; - case 1: - - try{ - TestClient c = new TestClient(new FileOutputStream(args[2])); - c.writeSameEntryBatchFS(sb.toString().getBytes(), Integer.parseInt(args[3])); - } catch(FileNotFoundException e){ - LOG.error(e); - } - break; - case 2: - break; - } - } - - void writeSameEntryBatch(byte[] data, int times) throws InterruptedException{ - start = System.currentTimeMillis(); - int count = times; - LOG.debug("Data: " + new String(data) + ", " + data.length); - while(count-- > 0){ - x.asyncAddEntry(lh, data, this, this.getFreshEntryId(2)); - } - LOG.debug("Finished " + times + " async writes in ms: " + (System.currentTimeMillis() - start)); - synchronized (map) { - if(map.size() != 0) - map.wait(); - } - LOG.debug("Finished processing in ms: " + (System.currentTimeMillis() - start)); - - LOG.debug("Ended computation"); - } - - void writeConsecutiveEntriesBatch(int times) throws InterruptedException{ - start = System.currentTimeMillis(); - int count = times; - while(count-- > 0){ - byte[] write = new byte[2]; - int j = count%100; - int k = (count+1)%100; - write[0] = (byte) j; - write[1] = (byte) k; - x.asyncAddEntry(lh, write, this, this.getFreshEntryId(2)); - } - LOG.debug("Finished " + times + " async writes in ms: " + (System.currentTimeMillis() - start)); - synchronized (map) { - if(map.size() != 0) - map.wait(); - } - LOG.debug("Finished processing writes (ms): " + (System.currentTimeMillis() - start)); - - Integer mon = Integer.valueOf(0); - synchronized(mon){ - try{ - x.asyncReadEntries(lh, 1, times - 1, this, mon); - mon.wait(); - } catch (BKException e){ - LOG.error(e); - } - } - LOG.error("Ended computation"); - } - - void writeSameEntryBatchFS(byte[] data, int times) { - int count = times; - LOG.debug("Data: " + data.length + ", " + times); - try{ - start = System.currentTimeMillis(); - while(count-- > 0){ - fStream.write(data); - fStreamLocal.write(data); - fStream.flush(); - } - fStream.close(); - System.out.println("Finished processing writes (ms): " + (System.currentTimeMillis() - start)); - } catch(IOException e){ - LOG.error(e); - } - } - - - public void addComplete(int rc, long ledgerId, long entryId, Object ctx) { - this.removeEntryId((Integer) ctx); - } - - public void readComplete(int rc, long ledgerId, LedgerSequence seq, Object ctx){ - System.out.println("Read callback: " + rc); - while(seq.hasMoreElements()){ - LedgerEntry le = seq.nextElement(); - LOG.debug(new String(le.getEntry())); - } - synchronized(ctx){ - ctx.notify(); - } - } -} diff --git a/src/contrib/bookkeeper/build.xml b/src/contrib/bookkeeper/build.xml deleted file mode 100644 index acd287b13a8..00000000000 --- a/src/contrib/bookkeeper/build.xml +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tests failed! - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/bookkeeper/conf/log4j.properties b/src/contrib/bookkeeper/conf/log4j.properties deleted file mode 100644 index c294b3d4eca..00000000000 --- a/src/contrib/bookkeeper/conf/log4j.properties +++ /dev/null @@ -1,72 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# - -# -# ZooKeeper Logging Configuration -# - -# Format is " (, )+ - -# DEFAULT: console appender only -log4j.rootLogger=INFO, CONSOLE - -# Example with rolling log file -#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE - -# Example with rolling log file and tracing -#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE - -# -# Log INFO level and above messages to the console -# -log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.Threshold=INFO -log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n - -# -# Add ROLLINGFILE to rootLogger to get log file output -# Log DEBUG level and above messages to a log file -log4j.appender.ROLLINGFILE=org.apache.log4j.ConsoleAppender -log4j.appender.ROLLINGFILE.Threshold=DEBUG -log4j.appender.ROLLINGFILE.File=bookkeeper.log -log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout -log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n - -# Max log file size of 10MB -log4j.appender.ROLLINGFILE.MaxFileSize=10MB -# uncomment the next line to limit number of backup files -#log4j.appender.ROLLINGFILE.MaxBackupIndex=10 - -log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout -log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n - - -# -# Add TRACEFILE to rootLogger to get log file output -# Log DEBUG level and above messages to a log file -log4j.appender.TRACEFILE=org.apache.log4j.FileAppender -log4j.appender.TRACEFILE.Threshold=TRACE -log4j.appender.TRACEFILE.File=bookkeeper_trace.log - -log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout -### Notice we are including log4j's NDC here (%x) -log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n diff --git a/src/contrib/bookkeeper/ivy.xml b/src/contrib/bookkeeper/ivy.xml deleted file mode 100644 index dcd897aea3a..00000000000 --- a/src/contrib/bookkeeper/ivy.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - BookKeeper - - - - - - - - - - - - - - - diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/Bookie.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/Bookie.java deleted file mode 100644 index 40278a3289a..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/Bookie.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.concurrent.LinkedBlockingQueue; - -import org.apache.bookkeeper.bookie.BookieException; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; - - - -/** - * Implements a bookie. - * - */ - -public class Bookie extends Thread { - HashMap ledgers = new HashMap(); - static Logger LOG = Logger.getLogger(Bookie.class); - - final File journalDirectory; - - final File ledgerDirectories[]; - - // ZK registration path for this bookie - static final String BOOKIE_REGISTRATION_PATH = "/ledgers/available/"; - - // ZooKeeper client instance for the Bookie - ZooKeeper zk; - - public static class NoLedgerException extends IOException { - private static final long serialVersionUID = 1L; - private long ledgerId; - public NoLedgerException(long ledgerId) { - this.ledgerId = ledgerId; - } - public long getLedgerId() { - return ledgerId; - } - } - public static class NoEntryException extends IOException { - private static final long serialVersionUID = 1L; - private long ledgerId; - private long entryId; - public NoEntryException(long ledgerId, long entryId) { - super("Entry " + entryId + " not found in " + ledgerId); - this.ledgerId = ledgerId; - this.entryId = entryId; - } - public long getLedger() { - return ledgerId; - } - public long getEntry() { - return entryId; - } - } - - EntryLogger entryLogger; - LedgerCache ledgerCache; - class SyncThread extends Thread { - volatile boolean running = true; - public SyncThread() { - super("SyncThread"); - } - @Override - public void run() { - while(running) { - synchronized(this) { - try { - wait(100); - if (!entryLogger.testAndClearSomethingWritten()) { - continue; - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - continue; - } - } - lastLogMark.markLog(); - try { - ledgerCache.flushLedger(true); - } catch (IOException e) { - e.printStackTrace(); - } - try { - entryLogger.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - lastLogMark.rollLog(); - } - } - } - SyncThread syncThread = new SyncThread(); - public Bookie(int port, String zkServers, File journalDirectory, File ledgerDirectories[]) throws IOException { - instantiateZookeeperClient(port, zkServers); - this.journalDirectory = journalDirectory; - this.ledgerDirectories = ledgerDirectories; - entryLogger = new EntryLogger(ledgerDirectories, this); - ledgerCache = new LedgerCache(ledgerDirectories); - lastLogMark.readLog(); - final long markedLogId = lastLogMark.txnLogId; - if (markedLogId > 0) { - File logFiles[] = journalDirectory.listFiles(); - ArrayList logs = new ArrayList(); - for(File f: logFiles) { - String name = f.getName(); - if (!name.endsWith(".txn")) { - continue; - } - String idString = name.split("\\.")[0]; - long id = Long.parseLong(idString, 16); - if (id < markedLogId) { - continue; - } - logs.add(id); - } - Collections.sort(logs); - if (logs.size() == 0 || logs.get(0) != markedLogId) { - throw new IOException("Recovery log " + markedLogId + " is missing"); - } - // TODO: When reading in the journal logs that need to be synced, we - // should use BufferedChannels instead to minimize the amount of - // system calls done. - ByteBuffer lenBuff = ByteBuffer.allocate(4); - ByteBuffer recBuff = ByteBuffer.allocate(64*1024); - for(Long id: logs) { - FileChannel recLog = openChannel(id); - while(true) { - lenBuff.clear(); - fullRead(recLog, lenBuff); - if (lenBuff.remaining() != 0) { - break; - } - lenBuff.flip(); - int len = lenBuff.getInt(); - if (len == 0) { - break; - } - recBuff.clear(); - if (recBuff.remaining() < len) { - recBuff = ByteBuffer.allocate(len); - } - recBuff.limit(len); - if (fullRead(recLog, recBuff) != len) { - // This seems scary, but it just means that this is where we - // left off writing - break; - } - recBuff.flip(); - long ledgerId = recBuff.getLong(); - // XXX we net to make sure we set the master keys appropriately! - LedgerDescriptor handle = getHandle(ledgerId, false); - try { - recBuff.rewind(); - handle.addEntry(recBuff); - } finally { - putHandle(handle); - } - } - } - } - setDaemon(true); - LOG.debug("I'm starting a bookie with journal directory " + journalDirectory.getName()); - start(); - syncThread.start(); - } - - /** - * Instantiate the ZooKeeper client for the Bookie. - */ - private void instantiateZookeeperClient(int port, String zkServers) throws IOException { - if (zkServers == null) { - LOG.warn("No ZK servers passed to Bookie constructor so BookKeeper clients won't know about this server!"); - zk = null; - return; - } - // Create the ZooKeeper client instance - zk = new ZooKeeper(zkServers, 10000, new Watcher() { - @Override - public void process(WatchedEvent event) { - // TODO: handle session disconnects and expires - if (LOG.isDebugEnabled()) { - LOG.debug("Process: " + event.getType() + " " + event.getPath()); - } - } - }); - // Create the ZK ephemeral node for this Bookie. - try { - zk.create(BOOKIE_REGISTRATION_PATH + InetAddress.getLocalHost().getHostAddress() + ":" + port, new byte[0], - Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); - } catch (Exception e) { - LOG.fatal("ZK exception registering ephemeral Znode for Bookie!", e); - // Throw an IOException back up. This will cause the Bookie - // constructor to error out. Alternatively, we could do a System - // exit here as this is a fatal error. - throw new IOException(e); - } - } - - private static int fullRead(FileChannel fc, ByteBuffer bb) throws IOException { - int total = 0; - while(bb.remaining() > 0) { - int rc = fc.read(bb); - if (rc <= 0) { - return total; - } - total += rc; - } - return total; - } - private void putHandle(LedgerDescriptor handle) { - synchronized (ledgers) { - handle.decRef(); - } - } - - private LedgerDescriptor getHandle(long ledgerId, boolean readonly, byte[] masterKey) throws IOException { - LedgerDescriptor handle = null; - synchronized (ledgers) { - handle = ledgers.get(ledgerId); - if (handle == null) { - if (readonly) { - throw new NoLedgerException(ledgerId); - } - handle = createHandle(ledgerId, readonly); - ledgers.put(ledgerId, handle); - handle.setMasterKey(ByteBuffer.wrap(masterKey)); - } - handle.incRef(); - } - return handle; - } - - private LedgerDescriptor getHandle(long ledgerId, boolean readonly) throws IOException { - LedgerDescriptor handle = null; - synchronized (ledgers) { - handle = ledgers.get(ledgerId); - if (handle == null) { - if (readonly) { - throw new NoLedgerException(ledgerId); - } - handle = createHandle(ledgerId, readonly); - ledgers.put(ledgerId, handle); - } - handle.incRef(); - } - return handle; - } - - - private LedgerDescriptor createHandle(long ledgerId, boolean readOnly) throws IOException { - return new LedgerDescriptor(ledgerId, entryLogger, ledgerCache); - } - - static class QueueEntry { - QueueEntry(ByteBuffer entry, long ledgerId, long entryId, - WriteCallback cb, Object ctx) { - this.entry = entry.duplicate(); - this.cb = cb; - this.ctx = ctx; - this.ledgerId = ledgerId; - this.entryId = entryId; - } - - ByteBuffer entry; - - long ledgerId; - - long entryId; - - WriteCallback cb; - - Object ctx; - } - - LinkedBlockingQueue queue = new LinkedBlockingQueue(); - - public final static long preAllocSize = 4*1024*1024; - - public final static ByteBuffer zeros = ByteBuffer.allocate(512); - - class LastLogMark { - long txnLogId; - long txnLogPosition; - LastLogMark lastMark; - LastLogMark(long logId, long logPosition) { - this.txnLogId = logId; - this.txnLogPosition = logPosition; - } - synchronized void setLastLogMark(long logId, long logPosition) { - txnLogId = logId; - txnLogPosition = logPosition; - } - synchronized void markLog() { - lastMark = new LastLogMark(txnLogId, txnLogPosition); - } - synchronized void rollLog() { - byte buff[] = new byte[16]; - ByteBuffer bb = ByteBuffer.wrap(buff); - bb.putLong(txnLogId); - bb.putLong(txnLogPosition); - for(File dir: ledgerDirectories) { - File file = new File(dir, "lastMark"); - try { - FileOutputStream fos = new FileOutputStream(file); - fos.write(buff); - fos.getChannel().force(true); - fos.close(); - } catch (IOException e) { - LOG.error("Problems writing to " + file, e); - } - } - } - synchronized void readLog() { - byte buff[] = new byte[16]; - ByteBuffer bb = ByteBuffer.wrap(buff); - for(File dir: ledgerDirectories) { - File file = new File(dir, "lastMark"); - try { - FileInputStream fis = new FileInputStream(file); - fis.read(buff); - fis.close(); - bb.clear(); - long i = bb.getLong(); - long p = bb.getLong(); - if (i > txnLogId) { - txnLogId = i; - } - if (p > txnLogPosition) { - txnLogPosition = p; - } - } catch (IOException e) { - LOG.error("Problems reading from " + file + " (this is okay if it is the first time starting this bookie"); - } - } - } - } - - private LastLogMark lastLogMark = new LastLogMark(0, 0); - - @Override - public void run() { - LinkedList toFlush = new LinkedList(); - ByteBuffer lenBuff = ByteBuffer.allocate(4); - try { - long logId = System.currentTimeMillis(); - FileChannel logFile = openChannel(logId); - BufferedChannel bc = new BufferedChannel(logFile, 65536); - zeros.clear(); - long nextPrealloc = preAllocSize; - long lastFlushPosition = 0; - logFile.write(zeros, nextPrealloc); - // TODO: Currently, when we roll over the journal logs, the older - // ones are never garbage collected. We should remove a journal log - // once all of its entries have been synced with the entry logs. - while (true) { - QueueEntry qe = null; - if (toFlush.isEmpty()) { - qe = queue.take(); - } else { - qe = queue.poll(); - if (qe == null || bc.position() > lastFlushPosition + 512*1024) { - //logFile.force(false); - bc.flush(true); - lastFlushPosition = bc.position(); - lastLogMark.setLastLogMark(logId, lastFlushPosition); - for (QueueEntry e : toFlush) { - e.cb.writeComplete(0, e.ledgerId, e.entryId, null, e.ctx); - } - toFlush.clear(); - } - } - if (qe == null) { - continue; - } - lenBuff.clear(); - lenBuff.putInt(qe.entry.remaining()); - lenBuff.flip(); - // - // we should be doing the following, but then we run out of - // direct byte buffers - // logFile.write(new ByteBuffer[] { lenBuff, qe.entry }); - bc.write(lenBuff); - bc.write(qe.entry); - if (bc.position() > nextPrealloc) { - nextPrealloc = (logFile.size() / preAllocSize + 1) * preAllocSize; - zeros.clear(); - logFile.write(zeros, nextPrealloc); - } - toFlush.add(qe); - } - } catch (Exception e) { - LOG.fatal("Bookie thread exiting", e); - } - } - - private FileChannel openChannel(long logId) throws FileNotFoundException { - FileChannel logFile = new RandomAccessFile(new File(journalDirectory, - Long.toHexString(logId) + ".txn"), - "rw").getChannel(); - return logFile; - } - - public void shutdown() throws InterruptedException { - // Shutdown the ZK client - if(zk != null) zk.close(); - this.interrupt(); - this.join(); - syncThread.running = false; - syncThread.join(); - for(LedgerDescriptor d: ledgers.values()) { - d.close(); - } - // Shutdown the EntryLogger which has the GarbageCollector Thread running - entryLogger.shutdown(); - } - - public void addEntry(ByteBuffer entry, WriteCallback cb, Object ctx, byte[] masterKey) - throws IOException, BookieException { - long ledgerId = entry.getLong(); - LedgerDescriptor handle = getHandle(ledgerId, false, masterKey); - - if(!handle.cmpMasterKey(ByteBuffer.wrap(masterKey))){ - throw BookieException.create(BookieException.Code.UnauthorizedAccessException); - } - try { - entry.rewind(); - long entryId = handle.addEntry(entry); - entry.rewind(); - if (LOG.isTraceEnabled()) { - LOG.trace("Adding " + entryId + "@" + ledgerId); - } - queue.add(new QueueEntry(entry, ledgerId, entryId, cb, ctx)); - } finally { - putHandle(handle); - } - } - - public ByteBuffer readEntry(long ledgerId, long entryId) throws IOException { - LedgerDescriptor handle = getHandle(ledgerId, true); - try { - if (LOG.isTraceEnabled()) { - LOG.trace("Reading " + entryId + "@" + ledgerId); - } - return handle.readEntry(entryId); - } finally { - putHandle(handle); - } - } - - // The rest of the code is test stuff - static class CounterCallback implements WriteCallback { - int count; - - synchronized public void writeComplete(int rc, long l, long e, InetSocketAddress addr, Object ctx) { - count--; - if (count == 0) { - notifyAll(); - } - } - - synchronized public void incCount() { - count++; - } - - synchronized public void waitZero() throws InterruptedException { - while (count > 0) { - wait(); - } - } - } - - /** - * @param args - * @throws IOException - * @throws InterruptedException - */ - public static void main(String[] args) throws IOException, - InterruptedException, BookieException { - Bookie b = new Bookie(5000, null, new File("/tmp"), new File[] { new File("/tmp") }); - CounterCallback cb = new CounterCallback(); - long start = System.currentTimeMillis(); - for (int i = 0; i < 100000; i++) { - ByteBuffer buff = ByteBuffer.allocate(1024); - buff.putLong(1); - buff.putLong(i); - buff.limit(1024); - buff.position(0); - cb.incCount(); - b.addEntry(buff, cb, null, new byte[0]); - } - cb.waitZero(); - long end = System.currentTimeMillis(); - System.out.println("Took " + (end-start) + "ms"); - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BookieException.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BookieException.java deleted file mode 100644 index d60f40f4804..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BookieException.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.apache.bookkeeper.bookie; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - - - import java.lang.Exception; - - @SuppressWarnings("serial") -public abstract class BookieException extends Exception { - - private int code; - public BookieException(int code){ - this.code = code; - } - - public static BookieException create(int code){ - switch(code){ - case Code.UnauthorizedAccessException: - return new BookieUnauthorizedAccessException(); - default: - return new BookieIllegalOpException(); - } - } - - public interface Code { - int OK = 0; - int UnauthorizedAccessException = -1; - - int IllegalOpException = -100; - } - - public void setCode(int code){ - this.code = code; - } - - public int getCode(){ - return this.code; - } - - public String getMessage(int code){ - switch(code){ - case Code.OK: - return "No problem"; - case Code.UnauthorizedAccessException: - return "Error while reading ledger"; - default: - return "Invalid operation"; - } - } - - public static class BookieUnauthorizedAccessException extends BookieException { - public BookieUnauthorizedAccessException(){ - super(Code.UnauthorizedAccessException); - } - } - - public static class BookieIllegalOpException extends BookieException { - public BookieIllegalOpException(){ - super(Code.UnauthorizedAccessException); - } - } -} \ No newline at end of file diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BufferedChannel.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BufferedChannel.java deleted file mode 100644 index c025ebe2366..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/BufferedChannel.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; - -/** - * Provides a buffering layer in front of a FileChannel. - */ -public class BufferedChannel -{ - ByteBuffer writeBuffer; - ByteBuffer readBuffer; - private FileChannel bc; - long position; - int capacity; - long readBufferStartPosition; - long writeBufferStartPosition; - BufferedChannel(FileChannel bc, int capacity) throws IOException { - this.bc = bc; - this.capacity = capacity; - position = bc.position(); - writeBufferStartPosition = position; - } -/* public void close() throws IOException { - bc.close(); - } -*/ -// public boolean isOpen() { -// return bc.isOpen(); -// } - - synchronized public int write(ByteBuffer src) throws IOException { - int copied = 0; - if (writeBuffer == null) { - writeBuffer = ByteBuffer.allocateDirect(capacity); - } - while(src.remaining() > 0) { - int truncated = 0; - if (writeBuffer.remaining() < src.remaining()) { - truncated = src.remaining() - writeBuffer.remaining(); - src.limit(src.limit()-truncated); - } - copied += src.remaining(); - writeBuffer.put(src); - src.limit(src.limit()+truncated); - if (writeBuffer.remaining() == 0) { - writeBuffer.flip(); - bc.write(writeBuffer); - writeBuffer.clear(); - writeBufferStartPosition = bc.position(); - } - } - position += copied; - return copied; - } - - public long position() { - return position; - } - - /** - * Retrieve the current size of the underlying FileChannel - * - * @return FileChannel size measured in bytes - * - * @throws IOException if some I/O error occurs reading the FileChannel - */ - public long size() throws IOException { - return bc.size(); - } - - public void flush(boolean sync) throws IOException { - synchronized(this) { - if (writeBuffer == null) { - return; - } - writeBuffer.flip(); - bc.write(writeBuffer); - writeBuffer.clear(); - writeBufferStartPosition = bc.position(); - } - if (sync) { - bc.force(false); - } - } - - /*public Channel getInternalChannel() { - return bc; - }*/ - synchronized public int read(ByteBuffer buff, long pos) throws IOException { - if (readBuffer == null) { - readBuffer = ByteBuffer.allocateDirect(capacity); - readBufferStartPosition = Long.MIN_VALUE; - } - int rc = buff.remaining(); - while(buff.remaining() > 0) { - // check if it is in the write buffer - if (writeBuffer != null && writeBufferStartPosition <= pos) { - long positionInBuffer = pos - writeBufferStartPosition; - long bytesToCopy = writeBuffer.position()-positionInBuffer; - if (bytesToCopy > buff.remaining()) { - bytesToCopy = buff.remaining(); - } - if (bytesToCopy == 0) { - throw new IOException("Read past EOF"); - } - ByteBuffer src = writeBuffer.duplicate(); - src.position((int) positionInBuffer); - src.limit((int) (positionInBuffer+bytesToCopy)); - buff.put(src); - pos+= bytesToCopy; - // first check if there is anything we can grab from the readBuffer - } else if (readBufferStartPosition <= pos && pos < readBufferStartPosition+readBuffer.capacity()) { - long positionInBuffer = pos - readBufferStartPosition; - long bytesToCopy = readBuffer.capacity()-positionInBuffer; - if (bytesToCopy > buff.remaining()) { - bytesToCopy = buff.remaining(); - } - ByteBuffer src = readBuffer.duplicate(); - src.position((int) positionInBuffer); - src.limit((int) (positionInBuffer+bytesToCopy)); - buff.put(src); - pos += bytesToCopy; - // let's read it - } else { - readBufferStartPosition = pos; - readBuffer.clear(); - // make sure that we don't overlap with the write buffer - if (readBufferStartPosition + readBuffer.capacity() >= writeBufferStartPosition) { - readBufferStartPosition = writeBufferStartPosition - readBuffer.capacity(); - if (readBufferStartPosition < 0) { - readBuffer.put(LedgerEntryPage.zeroPage, 0, (int)-readBufferStartPosition); - } - } - while(readBuffer.remaining() > 0) { - if (bc.read(readBuffer, readBufferStartPosition+readBuffer.position()) <= 0) { - throw new IOException("Short read"); - } - } - readBuffer.put(LedgerEntryPage.zeroPage, 0, readBuffer.remaining()); - readBuffer.clear(); - } - } - return rc; - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/EntryLogger.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/EntryLogger.java deleted file mode 100644 index 30e5e161ae1..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/EntryLogger.java +++ /dev/null @@ -1,487 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.AsyncCallback; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.KeeperException.Code; - -/** - * This class manages the writing of the bookkeeper entries. All the new - * entries are written to a common log. The LedgerCache will have pointers - * into files created by this class with offsets into the files to find - * the actual ledger entry. The entry log files created by this class are - * identified by a long. - */ -public class EntryLogger { - private static final Logger LOG = Logger.getLogger(EntryLogger.class); - private File dirs[]; - // This is a handle to the Bookie parent instance. We need this to get - // access to the LedgerCache as well as the ZooKeeper client handle. - private final Bookie bookie; - - private long logId; - /** - * The maximum size of a entry logger file. - */ - final static long LOG_SIZE_LIMIT = Long.getLong("logSizeLimit", 2 * 1024 * 1024 * 1024L); - private volatile BufferedChannel logChannel; - /** - * The 1K block at the head of the entry logger file - * that contains the fingerprint and (future) meta-data - */ - final static int LOGFILE_HEADER_SIZE = 1024; - final ByteBuffer LOGFILE_HEADER = ByteBuffer.allocate(LOGFILE_HEADER_SIZE); - - // this indicates that a write has happened since the last flush - private volatile boolean somethingWritten = false; - - // ZK ledgers related String constants - static final String LEDGERS_PATH = "/ledgers"; - static final String LEDGER_NODE_PREFIX = "L"; - static final String AVAILABLE_NODE = "available"; - - // Maps entry log files to the set of ledgers that comprise the file. - private ConcurrentMap> entryLogs2LedgersMap = new ConcurrentHashMap>(); - // This is the thread that garbage collects the entry logs that do not - // contain any active ledgers in them. - GarbageCollectorThread gcThread = new GarbageCollectorThread(); - // This is how often we want to run the Garbage Collector Thread (in milliseconds). - // This should be passed as a System property. Default it to 1000 ms (1sec). - final static int gcWaitTime = Integer.getInteger("gcWaitTime", 1000); - - /** - * Create an EntryLogger that stores it's log files in the given - * directories - */ - public EntryLogger(File dirs[], Bookie bookie) throws IOException { - this.dirs = dirs; - this.bookie = bookie; - // Initialize the entry log header buffer. This cannot be a static object - // since in our unit tests, we run multiple Bookies and thus EntryLoggers - // within the same JVM. All of these Bookie instances access this header - // so there can be race conditions when entry logs are rolled over and - // this header buffer is cleared before writing it into the new logChannel. - LOGFILE_HEADER.put("BKLO".getBytes()); - // Find the largest logId - for(File f: dirs) { - long lastLogId = getLastLogId(f); - if (lastLogId >= logId) { - logId = lastLogId+1; - } - } - createLogId(logId); - // Start the Garbage Collector thread to prune unneeded entry logs. - gcThread.start(); - } - - /** - * Maps entry log files to open channels. - */ - private ConcurrentHashMap channels = new ConcurrentHashMap(); - - /** - * This is the garbage collector thread that runs in the background to - * remove any entry log files that no longer contains any active ledger. - */ - class GarbageCollectorThread extends Thread { - volatile boolean running = true; - - public GarbageCollectorThread() { - super("GarbageCollectorThread"); - } - - @Override - public void run() { - while (running) { - synchronized (this) { - try { - wait(gcWaitTime); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - continue; - } - } - // Initialization check. No need to run any logic if we are still starting up. - if (entryLogs2LedgersMap.isEmpty() || bookie.ledgerCache == null - || bookie.ledgerCache.activeLedgers == null) { - continue; - } - // First sync ZK to make sure we're reading the latest active/available ledger nodes. - bookie.zk.sync(LEDGERS_PATH, new AsyncCallback.VoidCallback() { - @Override - public void processResult(int rc, String path, Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("ZK error syncing the ledgers node when getting children: ", KeeperException - .create(KeeperException.Code.get(rc), path)); - return; - } - // Sync has completed successfully so now we can poll ZK - // and read in the latest set of active ledger nodes. - List ledgerNodes; - try { - ledgerNodes = bookie.zk.getChildren(LEDGERS_PATH, null); - } catch (Exception e) { - LOG.error("Error polling ZK for the available ledger nodes: ", e); - // We should probably wait a certain amount of time before retrying in case of temporary issues. - return; - } - if (LOG.isDebugEnabled()) { - LOG.debug("Retrieved current set of ledger nodes: " + ledgerNodes); - } - // Convert the ZK retrieved ledger nodes to a HashSet for easier comparisons. - HashSet allActiveLedgers = new HashSet(ledgerNodes.size(), 1.0f); - for (String ledgerNode : ledgerNodes) { - try { - // The available node is also stored in this path so ignore that. - // That node is the path for the set of available Bookie Servers. - if (ledgerNode.equals(AVAILABLE_NODE)) - continue; - String parts[] = ledgerNode.split(LEDGER_NODE_PREFIX); - allActiveLedgers.add(Long.parseLong(parts[parts.length - 1])); - } catch (NumberFormatException e) { - LOG.fatal("Error extracting ledgerId from ZK ledger node: " + ledgerNode); - // This is a pretty bad error as it indicates a ledger node in ZK - // has an incorrect format. For now just continue and consider - // this as a non-existent ledger. - continue; - } - } - ConcurrentMap curActiveLedgers = bookie.ledgerCache.activeLedgers; - if (LOG.isDebugEnabled()) { - LOG.debug("All active ledgers from ZK: " + allActiveLedgers); - LOG.debug("Current active ledgers from Bookie: " + curActiveLedgers.keySet()); - } - // Remove any active ledgers that don't exist in ZK. - for (Long ledger : curActiveLedgers.keySet()) { - if (!allActiveLedgers.contains(ledger)) { - // Remove it from the current active ledgers set and also from all - // LedgerCache data references to the ledger, i.e. the physical ledger index file. - LOG.info("Removing a non-active/deleted ledger: " + ledger); - curActiveLedgers.remove(ledger); - try { - bookie.ledgerCache.deleteLedger(ledger); - } catch (IOException e) { - LOG.error("Exception when deleting the ledger index file on the Bookie: ", e); - } - } - } - // Loop through all of the entry logs and remove the non-active ledgers. - for (Long entryLogId : entryLogs2LedgersMap.keySet()) { - ConcurrentHashMap entryLogLedgers = entryLogs2LedgersMap.get(entryLogId); - for (Long entryLogLedger : entryLogLedgers.keySet()) { - // Remove the entry log ledger from the set if it isn't active. - if (!bookie.ledgerCache.activeLedgers.containsKey(entryLogLedger)) { - entryLogLedgers.remove(entryLogLedger); - } - } - if (entryLogLedgers.isEmpty()) { - // This means the entry log is not associated with any active ledgers anymore. - // We can remove this entry log file now. - LOG.info("Deleting entryLogId " + entryLogId + " as it has no active ledgers!"); - File entryLogFile; - try { - entryLogFile = findFile(entryLogId); - } catch (FileNotFoundException e) { - LOG.error("Trying to delete an entryLog file that could not be found: " - + entryLogId + ".log"); - continue; - } - entryLogFile.delete(); - channels.remove(entryLogId); - entryLogs2LedgersMap.remove(entryLogId); - } - } - }; - }, null); - } - } - } - - /** - * Creates a new log file with the given id. - */ - private void createLogId(long logId) throws IOException { - List list = Arrays.asList(dirs); - Collections.shuffle(list); - File firstDir = list.get(0); - if (logChannel != null) { - logChannel.flush(true); - } - logChannel = new BufferedChannel(new RandomAccessFile(new File(firstDir, Long.toHexString(logId)+".log"), "rw").getChannel(), 64*1024); - logChannel.write((ByteBuffer) LOGFILE_HEADER.clear()); - channels.put(logId, logChannel); - for(File f: dirs) { - setLastLogId(f, logId); - } - // Extract all of the ledger ID's that comprise all of the entry logs - // (except for the current new one which is still being written to). - extractLedgersFromEntryLogs(); - } - - /** - * writes the given id to the "lastId" file in the given directory. - */ - private void setLastLogId(File dir, long logId) throws IOException { - FileOutputStream fos; - fos = new FileOutputStream(new File(dir, "lastId")); - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos)); - try { - bw.write(Long.toHexString(logId) + "\n"); - bw.flush(); - } finally { - try { - fos.close(); - } catch (IOException e) { - } - } - } - - /** - * reads id from the "lastId" file in the given directory. - */ - private long getLastLogId(File f) { - FileInputStream fis; - try { - fis = new FileInputStream(new File(f, "lastId")); - } catch (FileNotFoundException e) { - return -1; - } - BufferedReader br = new BufferedReader(new InputStreamReader(fis)); - try { - String lastIdString = br.readLine(); - return Long.parseLong(lastIdString); - } catch (IOException e) { - return -1; - } catch(NumberFormatException e) { - return -1; - } finally { - try { - fis.close(); - } catch (IOException e) { - } - } - } - - private void openNewChannel() throws IOException { - createLogId(++logId); - } - - synchronized void flush() throws IOException { - if (logChannel != null) { - logChannel.flush(true); - } - } - synchronized long addEntry(long ledger, ByteBuffer entry) throws IOException { - if (logChannel.position() + entry.remaining() + 4 > LOG_SIZE_LIMIT) { - openNewChannel(); - } - ByteBuffer buff = ByteBuffer.allocate(4); - buff.putInt(entry.remaining()); - buff.flip(); - logChannel.write(buff); - long pos = logChannel.position(); - logChannel.write(entry); - //logChannel.flush(false); - somethingWritten = true; - return (logId << 32L) | pos; - } - - byte[] readEntry(long ledgerId, long entryId, long location) throws IOException { - long entryLogId = location >> 32L; - long pos = location & 0xffffffffL; - ByteBuffer sizeBuff = ByteBuffer.allocate(4); - pos -= 4; // we want to get the ledgerId and length to check - BufferedChannel fc; - try { - fc = getChannelForLogId(entryLogId); - } catch (FileNotFoundException e) { - FileNotFoundException newe = new FileNotFoundException(e.getMessage() + " for " + ledgerId + " with location " + location); - newe.setStackTrace(e.getStackTrace()); - throw newe; - } - if (fc.read(sizeBuff, pos) != sizeBuff.capacity()) { - throw new IOException("Short read from entrylog " + entryLogId); - } - pos += 4; - sizeBuff.flip(); - int entrySize = sizeBuff.getInt(); - // entrySize does not include the ledgerId - if (entrySize > 1024*1024) { - LOG.error("Sanity check failed for entry size of " + entrySize + " at location " + pos + " in " + entryLogId); - - } - byte data[] = new byte[entrySize]; - ByteBuffer buff = ByteBuffer.wrap(data); - int rc = fc.read(buff, pos); - if ( rc != data.length) { - throw new IOException("Short read for " + ledgerId + "@" + entryId + " in " + entryLogId + "@" + pos + "("+rc+"!="+data.length+")"); - } - buff.flip(); - long thisLedgerId = buff.getLong(); - if (thisLedgerId != ledgerId) { - throw new IOException("problem found in " + entryLogId + "@" + entryId + " at position + " + pos + " entry belongs to " + thisLedgerId + " not " + ledgerId); - } - long thisEntryId = buff.getLong(); - if (thisEntryId != entryId) { - throw new IOException("problem found in " + entryLogId + "@" + entryId + " at position + " + pos + " entry is " + thisEntryId + " not " + entryId); - } - - return data; - } - - private BufferedChannel getChannelForLogId(long entryLogId) throws IOException { - BufferedChannel fc = channels.get(entryLogId); - if (fc != null) { - return fc; - } - File file = findFile(entryLogId); - FileChannel newFc = new RandomAccessFile(file, "rw").getChannel(); - // If the file already exists before creating a BufferedChannel layer above it, - // set the FileChannel's position to the end so the write buffer knows where to start. - newFc.position(newFc.size()); - synchronized (channels) { - fc = channels.get(entryLogId); - if (fc != null){ - newFc.close(); - return fc; - } - fc = new BufferedChannel(newFc, 8192); - channels.put(entryLogId, fc); - return fc; - } - } - - private File findFile(long logId) throws FileNotFoundException { - for(File d: dirs) { - File f = new File(d, Long.toHexString(logId)+".log"); - if (f.exists()) { - return f; - } - } - throw new FileNotFoundException("No file for log " + Long.toHexString(logId)); - } - - synchronized public boolean testAndClearSomethingWritten() { - try { - return somethingWritten; - } finally { - somethingWritten = false; - } - } - - /** - * Method to read in all of the entry logs (those that we haven't done so yet), - * and find the set of ledger ID's that make up each entry log file. - */ - private void extractLedgersFromEntryLogs() throws IOException { - // Extract it for every entry log except for the current one. - // Entry Log ID's are just a long value that starts at 0 and increments - // by 1 when the log fills up and we roll to a new one. - ByteBuffer sizeBuff = ByteBuffer.allocate(4); - BufferedChannel bc; - for (long entryLogId = 0; entryLogId < logId; entryLogId++) { - // Comb the current entry log file if it has not already been extracted. - if (entryLogs2LedgersMap.containsKey(entryLogId)) { - continue; - } - LOG.info("Extracting the ledgers from entryLogId: " + entryLogId); - // Get the BufferedChannel for the current entry log file - try { - bc = getChannelForLogId(entryLogId); - } catch (FileNotFoundException e) { - // If we can't find the entry log file, just log a warning message and continue. - // This could be a deleted/garbage collected entry log. - LOG.warn("Entry Log file not found in log directories: " + entryLogId + ".log"); - continue; - } - // Start the read position in the current entry log file to be after - // the header where all of the ledger entries are. - long pos = LOGFILE_HEADER_SIZE; - ConcurrentHashMap entryLogLedgers = new ConcurrentHashMap(); - // Read through the entry log file and extract the ledger ID's. - while (true) { - // Check if we've finished reading the entry log file. - if (pos >= bc.size()) { - break; - } - if (bc.read(sizeBuff, pos) != sizeBuff.capacity()) { - throw new IOException("Short read from entrylog " + entryLogId); - } - pos += 4; - sizeBuff.flip(); - int entrySize = sizeBuff.getInt(); - if (entrySize > 1024 * 1024) { - LOG.error("Sanity check failed for entry size of " + entrySize + " at location " + pos + " in " - + entryLogId); - } - byte data[] = new byte[entrySize]; - ByteBuffer buff = ByteBuffer.wrap(data); - int rc = bc.read(buff, pos); - if (rc != data.length) { - throw new IOException("Short read for entryLog " + entryLogId + "@" + pos + "(" + rc + "!=" - + data.length + ")"); - } - buff.flip(); - long ledgerId = buff.getLong(); - entryLogLedgers.put(ledgerId, true); - // Advance position to the next entry and clear sizeBuff. - pos += entrySize; - sizeBuff.clear(); - } - LOG.info("Retrieved all ledgers that comprise entryLogId: " + entryLogId + ", values: " + entryLogLedgers); - entryLogs2LedgersMap.put(entryLogId, entryLogLedgers); - } - } - - /** - * Shutdown method to gracefully stop all threads spawned in this class and exit. - * - * @throws InterruptedException if there is an exception stopping threads. - */ - public void shutdown() throws InterruptedException { - gcThread.running = false; - gcThread.interrupt(); - gcThread.join(); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/FileInfo.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/FileInfo.java deleted file mode 100644 index 310bef04b80..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/FileInfo.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; - -/** - * This is the file handle for a ledger's index file that maps entry ids to location. - * It is used by LedgerCache. - */ -class FileInfo { - private FileChannel fc; - private final File lf; - /** - * The fingerprint of a ledger index file - */ - private byte header[] = "BKLE\0\0\0\0".getBytes(); - static final long START_OF_DATA = 1024; - private long size; - private int useCount; - private boolean isClosed; - public FileInfo(File lf) throws IOException { - this.lf = lf; - fc = new RandomAccessFile(lf, "rws").getChannel(); - size = fc.size(); - if (size == 0) { - fc.write(ByteBuffer.wrap(header)); - } - } - - synchronized public long size() { - long rc = size-START_OF_DATA; - if (rc < 0) { - rc = 0; - } - return rc; - } - - synchronized public int read(ByteBuffer bb, long position) throws IOException { - int total = 0; - while(bb.remaining() > 0) { - int rc = fc.read(bb, position+START_OF_DATA); - if (rc <= 0) { - throw new IOException("Short read"); - } - total += rc; - } - return total; - } - - synchronized public void close() throws IOException { - isClosed = true; - if (useCount == 0) { - fc.close(); - } - } - - synchronized public long write(ByteBuffer[] buffs, long position) throws IOException { - long total = 0; - try { - fc.position(position+START_OF_DATA); - while(buffs[buffs.length-1].remaining() > 0) { - long rc = fc.write(buffs); - if (rc <= 0) { - throw new IOException("Short write"); - } - total += rc; - } - } finally { - long newsize = position+START_OF_DATA+total; - if (newsize > size) { - size = newsize; - } - } - return total; - } - - synchronized public void use() { - useCount++; - } - - synchronized public void release() { - useCount--; - if (isClosed && useCount == 0) { - try { - fc.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /** - * Getter to a handle on the actual ledger index file. - * This is used when we are deleting a ledger and want to physically remove the index file. - */ - File getFile() { - return lf; - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerCache.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerCache.java deleted file mode 100644 index bb9ac45d1f7..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerCache.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.log4j.Logger; - -/** - * This class maps a ledger entry number into a location (entrylogid, offset) in - * an entry log file. It does user level caching to more efficiently manage disk - * head scheduling. - */ -public class LedgerCache { - private final static Logger LOG = Logger.getLogger(LedgerDescriptor.class); - - final File ledgerDirectories[]; - - public LedgerCache(File ledgerDirectories[]) { - this.ledgerDirectories = ledgerDirectories; - // Retrieve all of the active ledgers. - getActiveLedgers(); - } - /** - * the list of potentially clean ledgers - */ - LinkedList cleanLedgers = new LinkedList(); - - /** - * the list of potentially dirty ledgers - */ - LinkedList dirtyLedgers = new LinkedList(); - - HashMap fileInfoCache = new HashMap(); - - LinkedList openLedgers = new LinkedList(); - - // Stores the set of active (non-deleted) ledgers. - ConcurrentMap activeLedgers = new ConcurrentHashMap(); - - static int OPEN_FILE_LIMIT = 900; - static { - if (System.getProperty("openFileLimit") != null) { - OPEN_FILE_LIMIT = Integer.parseInt(System.getProperty("openFileLimit")); - } - LOG.info("openFileLimit is " + OPEN_FILE_LIMIT); - } - - // allocate half of the memory to the page cache - private static int pageLimit = (int)(Runtime.getRuntime().maxMemory() / 3) / LedgerEntryPage.PAGE_SIZE; - static { - LOG.info("maxMemory = " + Runtime.getRuntime().maxMemory()); - if (System.getProperty("pageLimit") != null) { - pageLimit = Integer.parseInt(System.getProperty("pageLimit")); - } - LOG.info("pageLimit is " + pageLimit); - } - // The number of pages that have actually been used - private int pageCount; - HashMap> pages = new HashMap>(); - - private void putIntoTable(HashMap> table, LedgerEntryPage lep) { - HashMap map = table.get(lep.getLedger()); - if (map == null) { - map = new HashMap(); - table.put(lep.getLedger(), map); - } - map.put(lep.getFirstEntry(), lep); - } - - private static LedgerEntryPage getFromTable(HashMap> table, Long ledger, Long firstEntry) { - HashMap map = table.get(ledger); - if (map != null) { - return map.get(firstEntry); - } - return null; - } - - synchronized private LedgerEntryPage getLedgerEntryPage(Long ledger, Long firstEntry, boolean onlyDirty) { - LedgerEntryPage lep = getFromTable(pages, ledger, firstEntry); - try { - if (onlyDirty && lep.isClean()) { - return null; - } - return lep; - } finally { - if (lep != null) { - lep.usePage(); - } - } - } - - public void putEntryOffset(long ledger, long entry, long offset) throws IOException { - int offsetInPage = (int) (entry%LedgerEntryPage.ENTRIES_PER_PAGES); - // find the id of the first entry of the page that has the entry - // we are looking for - long pageEntry = entry-offsetInPage; - LedgerEntryPage lep = getLedgerEntryPage(ledger, pageEntry, false); - if (lep == null) { - // find a free page - lep = grabCleanPage(ledger, pageEntry); - updatePage(lep); - synchronized(this) { - putIntoTable(pages, lep); - } - } - if (lep != null) { - lep.setOffset(offset, offsetInPage*8); - lep.releasePage(); - return; - } - } - - public long getEntryOffset(long ledger, long entry) throws IOException { - int offsetInPage = (int) (entry%LedgerEntryPage.ENTRIES_PER_PAGES); - // find the id of the first entry of the page that has the entry - // we are looking for - long pageEntry = entry-offsetInPage; - LedgerEntryPage lep = getLedgerEntryPage(ledger, pageEntry, false); - try { - if (lep == null) { - lep = grabCleanPage(ledger, pageEntry); - synchronized(this) { - putIntoTable(pages, lep); - } - updatePage(lep); - - } - return lep.getOffset(offsetInPage*8); - } finally { - if (lep != null) { - lep.releasePage(); - } - } - } - - static final private String getLedgerName(long ledgerId) { - int parent = (int) (ledgerId & 0xff); - int grandParent = (int) ((ledgerId & 0xff00) >> 8); - StringBuilder sb = new StringBuilder(); - sb.append(Integer.toHexString(grandParent)); - sb.append('/'); - sb.append(Integer.toHexString(parent)); - sb.append('/'); - sb.append(Long.toHexString(ledgerId)); - sb.append(".idx"); - return sb.toString(); - } - - static final private void checkParents(File f) throws IOException { - File parent = f.getParentFile(); - if (parent.exists()) { - return; - } - if (parent.mkdirs() == false) { - throw new IOException("Counldn't mkdirs for " + parent); - } - } - - static final private Random rand = new Random(); - - static final private File pickDirs(File dirs[]) { - return dirs[rand.nextInt(dirs.length)]; - } - - FileInfo getFileInfo(Long ledger, boolean create) throws IOException { - synchronized(fileInfoCache) { - FileInfo fi = fileInfoCache.get(ledger); - if (fi == null) { - String ledgerName = getLedgerName(ledger); - File lf = null; - for(File d: ledgerDirectories) { - lf = new File(d, ledgerName); - if (lf.exists()) { - break; - } - lf = null; - } - if (lf == null) { - if (!create) { - throw new Bookie.NoLedgerException(ledger); - } - File dir = pickDirs(ledgerDirectories); - lf = new File(dir, ledgerName); - checkParents(lf); - // A new ledger index file has been created for this Bookie. - // Add this new ledger to the set of active ledgers. - if (LOG.isDebugEnabled()) { - LOG.debug("New ledger index file created for ledgerId: " + ledger); - } - activeLedgers.put(ledger, true); - } - if (openLedgers.size() > OPEN_FILE_LIMIT) { - fileInfoCache.remove(openLedgers.removeFirst()).close(); - } - fi = new FileInfo(lf); - fileInfoCache.put(ledger, fi); - openLedgers.add(ledger); - } - if (fi != null) { - fi.use(); - } - return fi; - } - } - private void updatePage(LedgerEntryPage lep) throws IOException { - if (!lep.isClean()) { - throw new IOException("Trying to update a dirty page"); - } - FileInfo fi = null; - try { - fi = getFileInfo(lep.getLedger(), true); - long pos = lep.getFirstEntry()*8; - if (pos >= fi.size()) { - lep.zeroPage(); - } else { - lep.readPage(fi); - } - } finally { - if (fi != null) { - fi.release(); - } - } - } - - void flushLedger(boolean doAll) throws IOException { - synchronized(dirtyLedgers) { - if (dirtyLedgers.isEmpty()) { - synchronized(this) { - for(Long l: pages.keySet()) { - if (LOG.isTraceEnabled()) { - LOG.trace("Adding " + Long.toHexString(l) + " to dirty pages"); - } - dirtyLedgers.add(l); - } - } - } - if (dirtyLedgers.isEmpty()) { - return; - } - while(!dirtyLedgers.isEmpty()) { - Long l = dirtyLedgers.removeFirst(); - LinkedList firstEntryList; - synchronized(this) { - HashMap pageMap = pages.get(l); - if (pageMap == null || pageMap.isEmpty()) { - continue; - } - firstEntryList = new LinkedList(); - for(Map.Entry entry: pageMap.entrySet()) { - LedgerEntryPage lep = entry.getValue(); - if (lep.isClean()) { - if (LOG.isTraceEnabled()) { - LOG.trace("Page is clean " + lep); - } - continue; - } - firstEntryList.add(lep.getFirstEntry()); - } - } - // Now flush all the pages of a ledger - List entries = new ArrayList(firstEntryList.size()); - FileInfo fi = null; - try { - for(Long firstEntry: firstEntryList) { - LedgerEntryPage lep = getLedgerEntryPage(l, firstEntry, true); - if (lep != null) { - entries.add(lep); - } - } - Collections.sort(entries, new Comparator() { - @Override - public int compare(LedgerEntryPage o1, LedgerEntryPage o2) { - return (int)(o1.getFirstEntry()-o2.getFirstEntry()); - }}); - ArrayList versions = new ArrayList(entries.size()); - fi = getFileInfo(l, true); - int start = 0; - long lastOffset = -1; - for(int i = 0; i < entries.size(); i++) { - versions.add(i, entries.get(i).getVersion()); - if (lastOffset != -1 && (entries.get(i).getFirstEntry() - lastOffset) != LedgerEntryPage.ENTRIES_PER_PAGES) { - // send up a sequential list - int count = i - start; - if (count == 0) { - System.out.println("Count cannot possibly be zero!"); - } - writeBuffers(l, entries, fi, start, count); - start = i; - } - lastOffset = entries.get(i).getFirstEntry(); - } - if (entries.size()-start == 0 && entries.size() != 0) { - System.out.println("Nothing to write, but there were entries!"); - } - writeBuffers(l, entries, fi, start, entries.size()-start); - synchronized(this) { - for(int i = 0; i < entries.size(); i++) { - LedgerEntryPage lep = entries.get(i); - lep.setClean(versions.get(i)); - } - } - } finally { - for(LedgerEntryPage lep: entries) { - lep.releasePage(); - } - if (fi != null) { - fi.release(); - } - } - if (!doAll) { - break; - } - // Yeild. if we are doing all the ledgers we don't want to block other flushes that - // need to happen - try { - dirtyLedgers.wait(1); - } catch (InterruptedException e) { - // just pass it on - Thread.currentThread().interrupt(); - } - } - } - } - - private void writeBuffers(Long ledger, - List entries, FileInfo fi, - int start, int count) throws IOException { - if (LOG.isTraceEnabled()) { - LOG.trace("Writing " + count + " buffers of " + Long.toHexString(ledger)); - } - if (count == 0) { - //System.out.println("Count is zero!"); - return; - } - ByteBuffer buffs[] = new ByteBuffer[count]; - for(int j = 0; j < count; j++) { - buffs[j] = entries.get(start+j).getPageToWrite(); - if (entries.get(start+j).getLedger() != ledger) { - throw new IOException("Writing to " + ledger + " but page belongs to " + entries.get(start+j).getLedger()); - } - } - long totalWritten = 0; - while(buffs[buffs.length-1].remaining() > 0) { - long rc = fi.write(buffs, entries.get(start+0).getFirstEntry()*8); - if (rc <= 0) { - throw new IOException("Short write to ledger " + ledger + " rc = " + rc); - } - //System.out.println("Wrote " + rc + " to " + ledger); - totalWritten += rc; - } - if (totalWritten != count*LedgerEntryPage.PAGE_SIZE) { - throw new IOException("Short write to ledger " + ledger + " wrote " + totalWritten + " expected " + count*LedgerEntryPage.PAGE_SIZE); - } - } - private LedgerEntryPage grabCleanPage(long ledger, long entry) throws IOException { - if (entry % LedgerEntryPage.ENTRIES_PER_PAGES != 0) { - throw new IllegalArgumentException(entry + " is not a multiple of " + LedgerEntryPage.ENTRIES_PER_PAGES); - } - synchronized(this) { - if (pageCount < pageLimit) { - // let's see if we can allocate something - LedgerEntryPage lep = new LedgerEntryPage(); - lep.setLedger(ledger); - lep.setFirstEntry(entry); - // note, this will not block since it is a new page - lep.usePage(); - pageCount++; - return lep; - } - } - - outerLoop: - while(true) { - synchronized(cleanLedgers) { - if (cleanLedgers.isEmpty()) { - flushLedger(false); - synchronized(this) { - for(Long l: pages.keySet()) { - cleanLedgers.add(l); - } - } - } - synchronized(this) { - Long cleanLedger = cleanLedgers.getFirst(); - Map map = pages.get(cleanLedger); - if (map == null || map.isEmpty()) { - cleanLedgers.removeFirst(); - continue; - } - Iterator> it = map.entrySet().iterator(); - LedgerEntryPage lep = it.next().getValue(); - while((lep.inUse() || !lep.isClean())) { - if (it.hasNext()) { - continue outerLoop; - } - lep = it.next().getValue(); - } - it.remove(); - if (map.isEmpty()) { - pages.remove(lep.getLedger()); - } - lep.usePage(); - lep.zeroPage(); - lep.setLedger(ledger); - lep.setFirstEntry(entry); - return lep; - } - } - } - } - - public long getLastEntry(long ledgerId) { - long lastEntry = 0; - // Find the last entry in the cache - synchronized(this) { - Map map = pages.get(ledgerId); - if (map != null) { - for(LedgerEntryPage lep: map.values()) { - if (lep.getFirstEntry() + LedgerEntryPage.ENTRIES_PER_PAGES < lastEntry) { - continue; - } - lep.usePage(); - long highest = lep.getLastEntry(); - if (highest > lastEntry) { - lastEntry = highest; - } - lep.releasePage(); - } - } - } - - return lastEntry; - } - - /** - * This method will look within the ledger directories for the ledger index - * files. That will comprise the set of active ledgers this particular - * BookieServer knows about that have not yet been deleted by the BookKeeper - * Client. This is called only once during initialization. - */ - private void getActiveLedgers() { - // Ledger index files are stored in a file hierarchy with a parent and - // grandParent directory. We'll have to go two levels deep into these - // directories to find the index files. - for (File ledgerDirectory : ledgerDirectories) { - for (File grandParent : ledgerDirectory.listFiles()) { - if (grandParent.isDirectory()) { - for (File parent : grandParent.listFiles()) { - if (parent.isDirectory()) { - for (File index : parent.listFiles()) { - if (!index.isFile() || !index.getName().endsWith(".idx")) { - continue; - } - // We've found a ledger index file. The file name is the - // HexString representation of the ledgerId. - String ledgerIdInHex = index.getName().substring(0, index.getName().length() - 4); - activeLedgers.put(Long.parseLong(ledgerIdInHex, 16), true); - } - } - } - } - } - } - if (LOG.isDebugEnabled()) { - LOG.debug("Active ledgers found: " + activeLedgers); - } - } - - /** - * This method is called whenever a ledger is deleted by the BookKeeper Client - * and we want to remove all relevant data for it stored in the LedgerCache. - */ - void deleteLedger(long ledgerId) throws IOException { - if (LOG.isDebugEnabled()) - LOG.debug("Deleting ledgerId: " + ledgerId); - // Delete the ledger's index file and close the FileInfo - FileInfo fi = getFileInfo(ledgerId, false); - fi.getFile().delete(); - fi.close(); - - // Remove it from the activeLedgers set - activeLedgers.remove(ledgerId); - - // Now remove it from all the other lists and maps. - // These data structures need to be synchronized first before removing entries. - synchronized(this) { - pages.remove(ledgerId); - } - synchronized(fileInfoCache) { - fileInfoCache.remove(ledgerId); - } - synchronized(cleanLedgers) { - cleanLedgers.remove(ledgerId); - } - synchronized(dirtyLedgers) { - dirtyLedgers.remove(ledgerId); - } - synchronized(openLedgers) { - openLedgers.remove(ledgerId); - } - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerDescriptor.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerDescriptor.java deleted file mode 100644 index 653b34be37f..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerDescriptor.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import org.apache.log4j.Logger; - - - -/** - * Implements a ledger inside a bookie. In particular, it implements operations - * to write entries to a ledger and read entries from a ledger. - * - */ -public class LedgerDescriptor { - final static Logger LOG = Logger.getLogger(LedgerDescriptor.class); - LedgerCache ledgerCache; - LedgerDescriptor(long ledgerId, EntryLogger entryLogger, LedgerCache ledgerCache) { - this.ledgerId = ledgerId; - this.entryLogger = entryLogger; - this.ledgerCache = ledgerCache; - } - - private ByteBuffer masterKey = null; - - void setMasterKey(ByteBuffer masterKey){ - this.masterKey = masterKey; - } - - boolean cmpMasterKey(ByteBuffer masterKey){ - return this.masterKey.equals(masterKey); - } - - private long ledgerId; - EntryLogger entryLogger; - private int refCnt; - synchronized public void incRef() { - refCnt++; - } - synchronized public void decRef() { - refCnt--; - } - synchronized public int getRefCnt() { - return refCnt; - } - long addEntry(ByteBuffer entry) throws IOException { - long ledgerId = entry.getLong(); - if (ledgerId != this.ledgerId) { - throw new IOException("Entry for ledger " + ledgerId + " was sent to " + this.ledgerId); - } - long entryId = entry.getLong(); - entry.rewind(); - - /* - * Log the entry - */ - long pos = entryLogger.addEntry(ledgerId, entry); - - - /* - * Set offset of entry id to be the current ledger position - */ - ledgerCache.putEntryOffset(ledgerId, entryId, pos); - return entryId; - } - ByteBuffer readEntry(long entryId) throws IOException { - long offset; - /* - * If entryId is -1, then return the last written. - */ - if (entryId == -1) { - long lastEntry = ledgerCache.getLastEntry(ledgerId); - FileInfo fi = null; - try { - fi = ledgerCache.getFileInfo(ledgerId, false); - long size = fi.size(); - // we may not have the last entry in the cache - if (size > lastEntry*8) { - ByteBuffer bb = ByteBuffer.allocate(LedgerEntryPage.PAGE_SIZE); - long position = size-LedgerEntryPage.PAGE_SIZE; - if (position < 0) { - position = 0; - } - fi.read(bb, position); - bb.flip(); - long startingEntryId = position/8; - for(int i = LedgerEntryPage.ENTRIES_PER_PAGES-1; i >= 0; i--) { - if (bb.getLong(i*8) != 0) { - if (lastEntry < startingEntryId+i) { - lastEntry = startingEntryId+i; - } - break; - } - } - } - } finally { - if (fi != null) { - fi.release(); - } - } - entryId = lastEntry; - } - - offset = ledgerCache.getEntryOffset(ledgerId, entryId); - if (offset == 0) { - throw new Bookie.NoEntryException(ledgerId, entryId); - } - return ByteBuffer.wrap(entryLogger.readEntry(ledgerId, entryId, offset)); - } - void close() { - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerEntryPage.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerEntryPage.java deleted file mode 100644 index 58847eb4872..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/LedgerEntryPage.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * This is a page in the LedgerCache. It holds the locations - * (entrylogfile, offset) for entry ids. - */ -public class LedgerEntryPage { - public static final int PAGE_SIZE = 8192; - public static final int ENTRIES_PER_PAGES = PAGE_SIZE/8; - private long ledger = -1; - private long firstEntry = -1; - private ByteBuffer page = ByteBuffer.allocateDirect(PAGE_SIZE); - private boolean clean = true; - private boolean pinned = false; - private int useCount; - private int version; - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getLedger()); - sb.append('@'); - sb.append(getFirstEntry()); - sb.append(clean ? " clean " : " dirty "); - sb.append(useCount); - return sb.toString(); - } - synchronized public void usePage() { - useCount++; - } - synchronized public void pin() { - pinned = true; - } - synchronized public void unpin() { - pinned = false; - } - synchronized public boolean isPinned() { - return pinned; - } - synchronized public void releasePage() { - useCount--; - if (useCount < 0) { - throw new IllegalStateException("Use count has gone below 0"); - } - } - synchronized private void checkPage() { - if (useCount <= 0) { - throw new IllegalStateException("Page not marked in use"); - } - } - @Override - public boolean equals(Object other) { - LedgerEntryPage otherLEP = (LedgerEntryPage) other; - return otherLEP.getLedger() == getLedger() && otherLEP.getFirstEntry() == getFirstEntry(); - } - @Override - public int hashCode() { - return (int)getLedger() ^ (int)(getFirstEntry()); - } - void setClean(int versionOfCleaning) { - this.clean = (versionOfCleaning == version); - } - boolean isClean() { - return clean; - } - public void setOffset(long offset, int position) { - checkPage(); - version++; - this.clean = false; - page.putLong(position, offset); - } - public long getOffset(int position) { - checkPage(); - return page.getLong(position); - } - static final byte zeroPage[] = new byte[64*1024]; - public void zeroPage() { - checkPage(); - page.clear(); - page.put(zeroPage, 0, page.remaining()); - clean = true; - } - public void readPage(FileInfo fi) throws IOException { - checkPage(); - page.clear(); - while(page.remaining() != 0) { - if (fi.read(page, getFirstEntry()*8) <= 0) { - throw new IOException("Short page read of ledger " + getLedger() + " tried to get " + page.capacity() + " from position " + getFirstEntry()*8 + " still need " + page.remaining()); - } - } - clean = true; - } - public ByteBuffer getPageToWrite() { - checkPage(); - page.clear(); - return page; - } - void setLedger(long ledger) { - this.ledger = ledger; - } - long getLedger() { - return ledger; - } - int getVersion() { - return version; - } - void setFirstEntry(long firstEntry) { - if (firstEntry % ENTRIES_PER_PAGES != 0) { - throw new IllegalArgumentException(firstEntry + " is not a multiple of " + ENTRIES_PER_PAGES); - } - this.firstEntry = firstEntry; - } - long getFirstEntry() { - return firstEntry; - } - public boolean inUse() { - return useCount > 0; - } - public long getLastEntry() { - for(int i = ENTRIES_PER_PAGES - 1; i >= 0; i--) { - if (getOffset(i*8) > 0) { - return i + firstEntry; - } - } - return 0; - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/MarkerFileChannel.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/MarkerFileChannel.java deleted file mode 100644 index 8ed63bb29f5..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/bookie/MarkerFileChannel.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.bookie; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; - -/** - * This class is just a stub that can be used in collections with - * FileChannels - */ -public class MarkerFileChannel extends FileChannel { - - @Override - public void force(boolean metaData) throws IOException { - // TODO Auto-generated method stub - - } - - @Override - public FileLock lock(long position, long size, boolean shared) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public MappedByteBuffer map(MapMode mode, long position, long size) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public long position() throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public FileChannel position(long newPosition) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public int read(ByteBuffer dst) throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int read(ByteBuffer dst, long position) throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long size() throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long transferFrom(ReadableByteChannel src, long position, long count) - throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long transferTo(long position, long count, WritableByteChannel target) - throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public FileChannel truncate(long size) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public FileLock tryLock(long position, long size, boolean shared) - throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public int write(ByteBuffer src) throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int write(ByteBuffer src, long position) throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException { - // TODO Auto-generated method stub - return 0; - } - - @Override - protected void implCloseChannel() throws IOException { - // TODO Auto-generated method stub - - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/AsyncCallback.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/AsyncCallback.java deleted file mode 100644 index 07a2e58ea60..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/AsyncCallback.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.apache.bookkeeper.client; - -import java.util.Enumeration; - -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with this - * work for additional information regarding copyright ownership. The ASF - * licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -public interface AsyncCallback { - public interface AddCallback { - /** - * Callback declaration - * - * @param rc - * return code - * @param ledgerId - * ledger identifier - * @param entryId - * entry identifier - * @param ctx - * control object - */ - void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx); - } - - public interface CloseCallback { - /** - * Callback definition - * - * @param rc - * return code - * @param ledgerId - * ledger identifier - * @param ctx - * control object - */ - void closeComplete(int rc, LedgerHandle lh, Object ctx); - } - - public interface CreateCallback { - /** - * Declaration of callback method - * - * @param rc - * return status - * @param lh - * ledger handle - * @param ctx - * control object - */ - - void createComplete(int rc, LedgerHandle lh, Object ctx); - } - - public interface OpenCallback { - /** - * Callback for asynchronous call to open ledger - * - * @param rc - * Return code - * @param lh - * ledger handle - * @param ctx - * control object - */ - - public void openComplete(int rc, LedgerHandle lh, Object ctx); - - } - - public interface ReadCallback { - /** - * Callback declaration - * - * @param rc - * return code - * @param ledgerId - * ledger identifier - * @param seq - * sequence of entries - * @param ctx - * control object - */ - - void readComplete(int rc, LedgerHandle lh, Enumeration seq, - Object ctx); - } - - public interface DeleteCallback { - /** - * Callback definition for delete operations - * - * @param rc - * return code - * @param ctx - * control object - */ - void deleteComplete(int rc, Object ctx); - } - - public interface RecoverCallback { - /** - * Callback definition for bookie recover operations - * - * @param rc - * return code - * @param ctx - * control object - */ - void recoverComplete(int rc, Object ctx); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BKException.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BKException.java deleted file mode 100644 index 67da981afe6..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BKException.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.apache.bookkeeper.client; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.lang.Exception; - -/** - * Class the enumerates all the possible error conditions - * - */ - -@SuppressWarnings("serial") -public abstract class BKException extends Exception { - - private int code; - - BKException(int code) { - this.code = code; - } - - /** - * Create an exception from an error code - * @param code return error code - * @return correponding exception - */ - public static BKException create(int code) { - switch (code) { - case Code.ReadException: - return new BKReadException(); - case Code.QuorumException: - return new BKQuorumException(); - case Code.NoBookieAvailableException: - return new BKBookieException(); - case Code.DigestNotInitializedException: - return new BKDigestNotInitializedException(); - case Code.DigestMatchException: - return new BKDigestMatchException(); - case Code.NotEnoughBookiesException: - return new BKNotEnoughBookiesException(); - case Code.NoSuchLedgerExistsException: - return new BKNoSuchLedgerExistsException(); - case Code.BookieHandleNotAvailableException: - return new BKBookieHandleNotAvailableException(); - case Code.ZKException: - return new ZKException(); - case Code.LedgerRecoveryException: - return new BKLedgerRecoveryException(); - case Code.LedgerClosedException: - return new BKLedgerClosedException(); - case Code.WriteException: - return new BKWriteException(); - case Code.NoSuchEntryException: - return new BKNoSuchEntryException(); - case Code.IncorrectParameterException: - return new BKIncorrectParameterException(); - default: - return new BKIllegalOpException(); - } - } - - /** - * List of return codes - * - */ - public interface Code { - int OK = 0; - int ReadException = -1; - int QuorumException = -2; - int NoBookieAvailableException = -3; - int DigestNotInitializedException = -4; - int DigestMatchException = -5; - int NotEnoughBookiesException = -6; - int NoSuchLedgerExistsException = -7; - int BookieHandleNotAvailableException = -8; - int ZKException = -9; - int LedgerRecoveryException = -10; - int LedgerClosedException = -11; - int WriteException = -12; - int NoSuchEntryException = -13; - int IncorrectParameterException = -14; - - int IllegalOpException = -100; - } - - public void setCode(int code) { - this.code = code; - } - - public int getCode() { - return this.code; - } - - public static String getMessage(int code) { - switch (code) { - case Code.OK: - return "No problem"; - case Code.ReadException: - return "Error while reading ledger"; - case Code.QuorumException: - return "Invalid quorum size on ensemble size"; - case Code.NoBookieAvailableException: - return "Invalid quorum size on ensemble size"; - case Code.DigestNotInitializedException: - return "Digest engine not initialized"; - case Code.DigestMatchException: - return "Entry digest does not match"; - case Code.NotEnoughBookiesException: - return "Not enough non-faulty bookies available"; - case Code.NoSuchLedgerExistsException: - return "No such ledger exists"; - case Code.BookieHandleNotAvailableException: - return "Bookie handle is not available"; - case Code.ZKException: - return "Error while using ZooKeeper"; - case Code.LedgerRecoveryException: - return "Error while recovering ledger"; - case Code.LedgerClosedException: - return "Attempt to write to a closed ledger"; - case Code.WriteException: - return "Write failed on bookie"; - case Code.NoSuchEntryException: - return "No such entry"; - case Code.IncorrectParameterException: - return "Incorrect parameter input"; - default: - return "Invalid operation"; - } - } - - public static class BKReadException extends BKException { - public BKReadException() { - super(Code.ReadException); - } - } - - public static class BKNoSuchEntryException extends BKException { - public BKNoSuchEntryException() { - super(Code.NoSuchEntryException); - } - } - - public static class BKQuorumException extends BKException { - public BKQuorumException() { - super(Code.QuorumException); - } - } - - public static class BKBookieException extends BKException { - public BKBookieException() { - super(Code.NoBookieAvailableException); - } - } - - public static class BKDigestNotInitializedException extends BKException { - public BKDigestNotInitializedException() { - super(Code.DigestNotInitializedException); - } - } - - public static class BKDigestMatchException extends BKException { - public BKDigestMatchException() { - super(Code.DigestMatchException); - } - } - - public static class BKIllegalOpException extends BKException { - public BKIllegalOpException() { - super(Code.IllegalOpException); - } - } - - public static class BKNotEnoughBookiesException extends BKException { - public BKNotEnoughBookiesException() { - super(Code.NotEnoughBookiesException); - } - } - - public static class BKWriteException extends BKException { - public BKWriteException() { - super(Code.WriteException); - } - } - - public static class BKNoSuchLedgerExistsException extends BKException { - public BKNoSuchLedgerExistsException() { - super(Code.NoSuchLedgerExistsException); - } - } - - public static class BKBookieHandleNotAvailableException extends BKException { - public BKBookieHandleNotAvailableException() { - super(Code.BookieHandleNotAvailableException); - } - } - - public static class ZKException extends BKException { - public ZKException() { - super(Code.ZKException); - } - } - - public static class BKLedgerRecoveryException extends BKException { - public BKLedgerRecoveryException() { - super(Code.LedgerRecoveryException); - } - } - - public static class BKLedgerClosedException extends BKException { - public BKLedgerClosedException() { - super(Code.LedgerClosedException); - } - } - - public static class BKIncorrectParameterException extends BKException { - public BKIncorrectParameterException() { - super(Code.IncorrectParameterException); - } - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookKeeper.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookKeeper.java deleted file mode 100644 index 99166b89c7f..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookKeeper.java +++ /dev/null @@ -1,410 +0,0 @@ -package org.apache.bookkeeper.client; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.IOException; -import java.util.concurrent.Executors; - -import org.apache.bookkeeper.client.AsyncCallback.CreateCallback; -import org.apache.bookkeeper.client.AsyncCallback.DeleteCallback; -import org.apache.bookkeeper.client.AsyncCallback.OpenCallback; -import org.apache.bookkeeper.client.BKException.Code; -import org.apache.bookkeeper.proto.BookieClient; -import org.apache.bookkeeper.util.OrderedSafeExecutor; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; - -/** - * BookKeeper client. We assume there is one single writer to a ledger at any - * time. - * - * There are four possible operations: start a new ledger, write to a ledger, - * read from a ledger and delete a ledger. - * - * The exceptions resulting from synchronous calls and error code resulting from - * asynchronous calls can be found in the class {@link BKException}. - * - * - */ - -public class BookKeeper implements OpenCallback, CreateCallback, DeleteCallback { - - static final Logger LOG = Logger.getLogger(BookKeeper.class); - - ZooKeeper zk = null; - // whether the zk handle is one we created, or is owned by whoever - // instantiated us - boolean ownZKHandle = false; - - ClientSocketChannelFactory channelFactory; - // whether the socket factory is one we created, or is owned by whoever - // instantiated us - boolean ownChannelFactory = false; - - BookieClient bookieClient; - BookieWatcher bookieWatcher; - - OrderedSafeExecutor callbackWorker = new OrderedSafeExecutor(Runtime - .getRuntime().availableProcessors()); - OrderedSafeExecutor mainWorkerPool = new OrderedSafeExecutor(Runtime - .getRuntime().availableProcessors()); - - /** - * Create a bookkeeper client. A zookeeper client and a client socket factory - * will be instantiated as part of this constructor. - * - * @param servers - * A list of one of more servers on which zookeeper is running. The - * client assumes that the running bookies have been registered with - * zookeeper under the path - * {@link BookieWatcher#BOOKIE_REGISTRATION_PATH} - * @throws IOException - * @throws InterruptedException - * @throws KeeperException - */ - public BookKeeper(String servers) throws IOException, InterruptedException, - KeeperException { - this(new ZooKeeper(servers, 10000, new Watcher() { - @Override - public void process(WatchedEvent event) { - // TODO: handle session disconnects and expires - if (LOG.isDebugEnabled()) { - LOG.debug("Process: " + event.getType() + " " + event.getPath()); - } - } - }), new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), - Executors.newCachedThreadPool())); - - ownZKHandle = true; - ownChannelFactory = true; - } - - /** - * Create a bookkeeper client but use the passed in zookeeper client instead - * of instantiating one. - * - * @param zk - * Zookeeper client instance connected to the zookeeper with which - * the bookies have registered - * @throws InterruptedException - * @throws KeeperException - */ - public BookKeeper(ZooKeeper zk) throws InterruptedException, KeeperException { - this(zk, new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), - Executors.newCachedThreadPool())); - ownChannelFactory = true; - } - - /** - * Create a bookkeeper client but use the passed in zookeeper client and - * client socket channel factory instead of instantiating those. - * - * @param zk - * Zookeeper client instance connected to the zookeeper with which - * the bookies have registered - * @param channelFactory - * A factory that will be used to create connections to the bookies - * @throws InterruptedException - * @throws KeeperException - */ - public BookKeeper(ZooKeeper zk, ClientSocketChannelFactory channelFactory) - throws InterruptedException, KeeperException { - if (zk == null || channelFactory == null) { - throw new NullPointerException(); - } - this.zk = zk; - this.channelFactory = channelFactory; - bookieWatcher = new BookieWatcher(this); - bookieWatcher.readBookiesBlocking(); - bookieClient = new BookieClient(channelFactory, mainWorkerPool); - } - - /** - * There are 2 digest types that can be used for verification. The CRC32 is - * cheap to compute but does not protect against byzantine bookies (i.e., a - * bookie might report fake bytes and a matching CRC32). The MAC code is more - * expensive to compute, but is protected by a password, i.e., a bookie can't - * report fake bytes with a mathching MAC unless it knows the password - */ - public enum DigestType { - MAC, CRC32 - }; - - public ZooKeeper getZkHandle() { - return zk; - } - - /** - * Get the BookieClient, currently used for doing bookie recovery. - * - * @return BookieClient for the BookKeeper instance. - */ - public BookieClient getBookieClient() { - return bookieClient; - } - - /** - * Creates a new ledger asynchronously. To create a ledger, we need to specify - * the ensemble size, the quorum size, the digest type, a password, a callback - * implementation, and an optional control object. The ensemble size is how - * many bookies the entries should be striped among and the quorum size is the - * degree of replication of each entry. The digest type is either a MAC or a - * CRC. Note that the CRC option is not able to protect a client against a - * bookie that replaces an entry. The password is used not only to - * authenticate access to a ledger, but also to verify entries in ledgers. - * - * @param ensSize - * ensemble size - * @param qSize - * quorum size - * @param digestType - * digest type, either MAC or CRC32 - * @param passwd - * password - * @param cb - * createCallback implementation - * @param ctx - * optional control object - */ - public void asyncCreateLedger(int ensSize, int qSize, DigestType digestType, - byte[] passwd, CreateCallback cb, Object ctx) { - - new LedgerCreateOp(this, ensSize, qSize, digestType, passwd, cb, ctx) - .initiate(); - - } - - /** - * Create callback implementation for synchronous create call. - * - * @param rc - * return code - * @param lh - * ledger handle object - * @param ctx - * optional control object - */ - public void createComplete(int rc, LedgerHandle lh, Object ctx) { - SyncCounter counter = (SyncCounter) ctx; - counter.setLh(lh); - counter.setrc(rc); - counter.dec(); - } - - /** - * Creates a new ledger. Default of 3 servers, and quorum of 2 servers. - * - * @param digestType - * digest type, either MAC or CRC32 - * @param passwd - * password - * @return - * @throws KeeperException - * @throws InterruptedException - * @throws BKException - */ - public LedgerHandle createLedger(DigestType digestType, byte passwd[]) - throws KeeperException, BKException, InterruptedException, IOException { - return createLedger(3, 2, digestType, passwd); - } - - /** - * Synchronous call to create ledger. Parameters match those of - * {@link #asyncCreateLedger(int, int, DigestType, byte[], CreateCallback, Object)} - * - * @param ensSize - * @param qSize - * @param digestType - * @param passwd - * @return - * @throws KeeperException - * @throws InterruptedException - * @throws IOException - * @throws BKException - */ - public LedgerHandle createLedger(int ensSize, int qSize, - DigestType digestType, byte passwd[]) throws KeeperException, - InterruptedException, IOException, BKException { - SyncCounter counter = new SyncCounter(); - counter.inc(); - /* - * Calls asynchronous version - */ - asyncCreateLedger(ensSize, qSize, digestType, passwd, this, counter); - - /* - * Wait - */ - counter.block(0); - if (counter.getLh() == null) { - LOG.error("ZooKeeper error: " + counter.getrc()); - throw BKException.create(Code.ZKException); - } - - return counter.getLh(); - } - - /** - * Open existing ledger asynchronously for reading. - * - * @param lId - * ledger identifier - * @param digestType - * digest type, either MAC or CRC32 - * @param passwd - * password - * @param ctx - * optional control object - */ - public void asyncOpenLedger(long lId, DigestType digestType, byte passwd[], - OpenCallback cb, Object ctx) { - - new LedgerOpenOp(this, lId, digestType, passwd, cb, ctx).initiate(); - - } - - /** - * Callback method for synchronous open operation - * - * @param rc - * return code - * @param lh - * ledger handle - * @param ctx - * optional control object - */ - public void openComplete(int rc, LedgerHandle lh, Object ctx) { - SyncCounter counter = (SyncCounter) ctx; - counter.setLh(lh); - - LOG.debug("Open complete: " + rc); - - counter.setrc(rc); - counter.dec(); - } - - /** - * Synchronous open ledger call - * - * @param lId - * ledger identifier - * @param digestType - * digest type, either MAC or CRC32 - * @param passwd - * password - * @return - * @throws InterruptedException - * @throws BKException - */ - - public LedgerHandle openLedger(long lId, DigestType digestType, byte passwd[]) - throws BKException, InterruptedException { - SyncCounter counter = new SyncCounter(); - counter.inc(); - - /* - * Calls async open ledger - */ - asyncOpenLedger(lId, digestType, passwd, this, counter); - - /* - * Wait - */ - counter.block(0); - if (counter.getrc() != BKException.Code.OK) - throw BKException.create(counter.getrc()); - - return counter.getLh(); - } - - /** - * Deletes a ledger asynchronously. - * - * @param lId - * ledger Id - * @param cb - * deleteCallback implementation - * @param ctx - * optional control object - */ - public void asyncDeleteLedger(long lId, DeleteCallback cb, Object ctx) { - new LedgerDeleteOp(this, lId, cb, ctx).initiate(); - } - - /** - * Delete callback implementation for synchronous delete call. - * - * @param rc - * return code - * @param ctx - * optional control object - */ - public void deleteComplete(int rc, Object ctx) { - SyncCounter counter = (SyncCounter) ctx; - counter.setrc(rc); - counter.dec(); - } - - /** - * Synchronous call to delete a ledger. Parameters match those of - * {@link #asyncDeleteLedger(long, DeleteCallback, Object)} - * - * @param lId - * ledgerId - * @throws InterruptedException - * @throws BKException - */ - public void deleteLedger(long lId) throws InterruptedException, BKException { - SyncCounter counter = new SyncCounter(); - counter.inc(); - // Call asynchronous version - asyncDeleteLedger(lId, this, counter); - // Wait - counter.block(0); - if (counter.getrc() != KeeperException.Code.OK.intValue()) { - LOG.error("ZooKeeper error deleting ledger node: " + counter.getrc()); - throw BKException.create(Code.ZKException); - } - } - - /** - * Shuts down client. - * - */ - public void halt() throws InterruptedException { - bookieClient.close(); - bookieWatcher.halt(); - if (ownChannelFactory) { - channelFactory.releaseExternalResources(); - } - if (ownZKHandle) { - zk.close(); - } - callbackWorker.shutdown(); - mainWorkerPool.shutdown(); - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookieWatcher.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookieWatcher.java deleted file mode 100644 index 0063825d9c4..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/BookieWatcher.java +++ /dev/null @@ -1,204 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException; -import org.apache.bookkeeper.util.SafeRunnable; -import org.apache.bookkeeper.util.StringUtils; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.AsyncCallback.ChildrenCallback; -import org.apache.zookeeper.KeeperException.Code; - -/** - * This class is responsible for maintaining a consistent view of what bookies - * are available by reading Zookeeper (and setting watches on the bookie nodes). - * When a bookie fails, the other parts of the code turn to this class to find a - * replacement - * - */ -class BookieWatcher implements Watcher, ChildrenCallback { - static final Logger logger = Logger.getLogger(BookieWatcher.class); - - public static final String BOOKIE_REGISTRATION_PATH = "/ledgers/available"; - static final Set EMPTY_SET = new HashSet(); - public static int ZK_CONNECT_BACKOFF_SEC = 1; - - BookKeeper bk; - ScheduledExecutorService scheduler; - - Set knownBookies = new HashSet(); - - SafeRunnable reReadTask = new SafeRunnable() { - @Override - public void safeRun() { - readBookies(); - } - }; - - public BookieWatcher(BookKeeper bk) { - this.bk = bk; - this.scheduler = Executors.newSingleThreadScheduledExecutor(); - } - - public void halt(){ - scheduler.shutdown(); - } - - public void readBookies() { - readBookies(this); - } - - public void readBookies(ChildrenCallback callback) { - bk.getZkHandle().getChildren( BOOKIE_REGISTRATION_PATH, this, callback, null); - } - - @Override - public void process(WatchedEvent event) { - readBookies(); - } - - @Override - public void processResult(int rc, String path, Object ctx, List children) { - - if (rc != KeeperException.Code.OK.intValue()) { - //logger.error("Error while reading bookies", KeeperException.create(Code.get(rc), path)); - // try the read after a second again - scheduler.schedule(reReadTask, ZK_CONNECT_BACKOFF_SEC, TimeUnit.SECONDS); - return; - } - - // Read the bookie addresses into a set for efficient lookup - Set newBookieAddrs = new HashSet(); - for (String bookieAddrString : children) { - InetSocketAddress bookieAddr; - try { - bookieAddr = StringUtils.parseAddr(bookieAddrString); - } catch (IOException e) { - logger.error("Could not parse bookie address: " + bookieAddrString + ", ignoring this bookie"); - continue; - } - newBookieAddrs.add(bookieAddr); - } - - synchronized (this) { - knownBookies = newBookieAddrs; - } - } - - /** - * Blocks until bookies are read from zookeeper, used in the {@link BookKeeper} constructor. - * @throws InterruptedException - * @throws KeeperException - */ - public void readBookiesBlocking() throws InterruptedException, KeeperException { - final LinkedBlockingQueue queue = new LinkedBlockingQueue(); - readBookies(new ChildrenCallback() { - public void processResult(int rc, String path, Object ctx, List children) { - try { - BookieWatcher.this.processResult(rc, path, ctx, children); - queue.put(rc); - } catch (InterruptedException e) { - logger.error("Interruped when trying to read bookies in a blocking fashion"); - throw new RuntimeException(e); - } - } - }); - int rc = queue.take(); - - if (rc != KeeperException.Code.OK.intValue()) { - throw KeeperException.create(Code.get(rc)); - } - } - - /** - * Wrapper over the {@link #getAdditionalBookies(Set, int)} method when there is no exclusion list (or exisiting bookies) - * @param numBookiesNeeded - * @return - * @throws BKNotEnoughBookiesException - */ - public ArrayList getNewBookies(int numBookiesNeeded) throws BKNotEnoughBookiesException { - return getAdditionalBookies(EMPTY_SET, numBookiesNeeded); - } - - /** - * Wrapper over the {@link #getAdditionalBookies(Set, int)} method when you just need 1 extra bookie - * @param existingBookies - * @return - * @throws BKNotEnoughBookiesException - */ - public InetSocketAddress getAdditionalBookie(List existingBookies) - throws BKNotEnoughBookiesException { - return getAdditionalBookies(new HashSet(existingBookies), 1).get(0); - } - - /** - * Returns additional bookies given an exclusion list and how many are needed - * @param existingBookies - * @param numAdditionalBookiesNeeded - * @return - * @throws BKNotEnoughBookiesException - */ - public ArrayList getAdditionalBookies(Set existingBookies, - int numAdditionalBookiesNeeded) throws BKNotEnoughBookiesException { - - ArrayList newBookies = new ArrayList(); - - if (numAdditionalBookiesNeeded <= 0) { - return newBookies; - } - - List allBookies; - - synchronized (this) { - allBookies = new ArrayList(knownBookies); - } - - Collections.shuffle(allBookies); - - for (InetSocketAddress bookie : allBookies) { - if (existingBookies.contains(bookie)) { - continue; - } - - newBookies.add(bookie); - numAdditionalBookiesNeeded--; - - if (numAdditionalBookiesNeeded == 0) { - return newBookies; - } - } - - throw new BKNotEnoughBookiesException(); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DigestManager.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DigestManager.java deleted file mode 100644 index 273b265f5fe..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DigestManager.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.nio.ByteBuffer; -import java.security.GeneralSecurityException; - -import org.apache.bookkeeper.client.BKException.BKDigestMatchException; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBufferInputStream; -import org.jboss.netty.buffer.ChannelBuffers; - -/** - * This class takes an entry, attaches a digest to it and packages it with relevant - * data so that it can be shipped to the bookie. On the return side, it also - * gets a packet, checks that the digest matches, and extracts the original entry - * for the packet. Currently 2 types of digests are supported: MAC (based on SHA-1) and CRC32 - */ - -public abstract class DigestManager { - static final Logger logger = Logger.getLogger(DigestManager.class); - - long ledgerId; - - abstract int getMacCodeLength(); - - void update(byte[] data){ - update(data, 0, data.length); - } - - abstract void update(byte[] data, int offset, int length); - abstract byte[] getValueAndReset(); - - final int macCodeLength; - - public DigestManager(long ledgerId) { - this.ledgerId = ledgerId; - macCodeLength = getMacCodeLength(); - } - - static DigestManager instantiate(long ledgerId, byte[] passwd, DigestType digestType) throws GeneralSecurityException{ - switch(digestType){ - case MAC: - return new MacDigestManager(ledgerId, passwd); - case CRC32: - return new CRC32DigestManager(ledgerId); - default: - throw new GeneralSecurityException("Unknown checksum type: " + digestType); - } - } - - public ChannelBuffer computeDigestAndPackageForSending(long entryId, long lastAddConfirmed, byte[] data) { - - byte[] bufferArray = new byte[24+macCodeLength]; - ByteBuffer buffer = ByteBuffer.wrap(bufferArray); - buffer.putLong(ledgerId); - buffer.putLong(entryId); - buffer.putLong(lastAddConfirmed); - buffer.flip(); - - update(buffer.array(), 0, 24); - update(data); - byte[] digest = getValueAndReset(); - - buffer.limit(buffer.capacity()); - buffer.position(24); - buffer.put(digest); - buffer.flip(); - - return ChannelBuffers.wrappedBuffer(ChannelBuffers.wrappedBuffer(buffer), ChannelBuffers.wrappedBuffer(data)); - } - - private void verifyDigest(ChannelBuffer dataReceived) throws BKDigestMatchException { - verifyDigest(-1, dataReceived, true); - } - - private void verifyDigest(long entryId, ChannelBuffer dataReceived) throws BKDigestMatchException { - verifyDigest(entryId, dataReceived, false); - } - - private void verifyDigest(long entryId, ChannelBuffer dataReceived, boolean skipEntryIdCheck) - throws BKDigestMatchException { - - ByteBuffer dataReceivedBuffer = dataReceived.toByteBuffer(); - byte[] digest; - - update(dataReceivedBuffer.array(), dataReceivedBuffer.position(), 24); - - int offset = 24 + macCodeLength; - update(dataReceivedBuffer.array(), dataReceivedBuffer.position() + offset, dataReceived.readableBytes() - offset); - digest = getValueAndReset(); - - for (int i = 0; i < digest.length; i++) { - if (digest[i] != dataReceived.getByte(24 + i)) { - logger.error("Mac mismatch for ledger-id: " + ledgerId + ", entry-id: " + entryId); - throw new BKDigestMatchException(); - } - } - - long actualLedgerId = dataReceived.readLong(); - long actualEntryId = dataReceived.readLong(); - - if (actualLedgerId != ledgerId) { - logger.error("Ledger-id mismatch in authenticated message, expected: " + ledgerId + " , actual: " - + actualLedgerId); - throw new BKDigestMatchException(); - } - - if (!skipEntryIdCheck && actualEntryId != entryId) { - logger.error("Entry-id mismatch in authenticated message, expected: " + entryId + " , actual: " - + actualEntryId); - throw new BKDigestMatchException(); - } - - } - - ChannelBufferInputStream verifyDigestAndReturnData(long entryId, ChannelBuffer dataReceived) - throws BKDigestMatchException { - verifyDigest(entryId, dataReceived); - dataReceived.readerIndex(24 + macCodeLength); - return new ChannelBufferInputStream(dataReceived); - } - - static class RecoveryData { - long lastAddConfirmed; - long entryId; - - public RecoveryData(long lastAddConfirmed, long entryId) { - this.lastAddConfirmed = lastAddConfirmed; - this.entryId = entryId; - } - - } - - RecoveryData verifyDigestAndReturnLastConfirmed(ChannelBuffer dataReceived) throws BKDigestMatchException { - verifyDigest(dataReceived); - dataReceived.readerIndex(8); - - long entryId = dataReceived.readLong(); - long lastAddConfirmed = dataReceived.readLong(); - return new RecoveryData(lastAddConfirmed, entryId); - - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DistributionSchedule.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DistributionSchedule.java deleted file mode 100644 index 2f65adfde27..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/DistributionSchedule.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * This interface determins how entries are distributed among bookies. - * - * Every entry gets replicated to some number of replicas. The first replica for - * an entry is given a replicaIndex of 0, and so on. To distribute write load, - * not all entries go to all bookies. Given an entry-id and replica index, an - * {@link DistributionSchedule} determines which bookie that replica should go - * to. - */ - -public interface DistributionSchedule { - - /** - * - * @param entryId - * @param replicaIndex - * @return index of bookie that should get this replica - */ - public int getBookieIndex(long entryId, int replicaIndex); - - /** - * - * @param entryId - * @param bookieIndex - * @return -1 if the given bookie index is not a replica for the given - * entryId - */ - public int getReplicaIndex(long entryId, int bookieIndex); - - /** - * Specifies whether its ok to proceed with recovery given that we have - * heard back from the given bookie index. These calls will be a made in a - * sequence and an implementation of this interface should accumulate - * history about which bookie indexes we have heard from. Once this method - * has returned true, it wont be called again on the same instance - * - * @param bookieIndexHeardFrom - * @return true if its ok to proceed with recovery - */ - public boolean canProceedWithRecovery(int bookieIndexHeardFrom); -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerCreateOp.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerCreateOp.java deleted file mode 100644 index 6c0d11a680b..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerCreateOp.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.client; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.security.GeneralSecurityException; -import java.util.ArrayList; -import org.apache.bookkeeper.client.AsyncCallback.CreateCallback; -import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.util.StringUtils; -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.AsyncCallback.StatCallback; -import org.apache.zookeeper.AsyncCallback.StringCallback; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.data.Stat; - -/** - * Encapsulates asynchronous ledger create operation - * - */ -class LedgerCreateOp implements StringCallback, StatCallback { - - static final Logger LOG = Logger.getLogger(LedgerCreateOp.class); - - CreateCallback cb; - LedgerMetadata metadata; - LedgerHandle lh; - Object ctx; - byte[] passwd; - BookKeeper bk; - DigestType digestType; - - /** - * Constructor - * - * @param bk - * BookKeeper object - * @param ensembleSize - * ensemble size - * @param quorumSize - * quorum size - * @param digestType - * digest type, either MAC or CRC32 - * @param passwd - * passowrd - * @param cb - * callback implementation - * @param ctx - * optional control object - */ - - LedgerCreateOp(BookKeeper bk, int ensembleSize, int quorumSize, DigestType digestType, byte[] passwd, CreateCallback cb, Object ctx) { - this.bk = bk; - this.metadata = new LedgerMetadata(ensembleSize, quorumSize); - this.digestType = digestType; - this.passwd = passwd; - this.cb = cb; - this.ctx = ctx; - } - - /** - * Initiates the operation - */ - public void initiate() { - /* - * Create ledger node on ZK. We get the id from the sequence number on - * the node. - */ - - bk.getZkHandle().create(StringUtils.prefix, new byte[0], Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT_SEQUENTIAL, this, null); - - // calls the children callback method below - } - - - /** - * Implements ZooKeeper string callback. - * - * @see org.apache.zookeeper.AsyncCallback.StringCallback#processResult(int, java.lang.String, java.lang.Object, java.lang.String) - */ - public void processResult(int rc, String path, Object ctx, String name) { - - if (rc != KeeperException.Code.OK.intValue()) { - LOG.error("Could not create node for ledger", KeeperException.create(KeeperException.Code.get(rc), path)); - cb.createComplete(BKException.Code.ZKException, null, this.ctx); - return; - } - - /* - * Extract ledger id. - */ - long ledgerId; - try { - ledgerId = StringUtils.getLedgerId(name); - } catch (IOException e) { - LOG.error("Could not extract ledger-id from path:" + path, e); - cb.createComplete(BKException.Code.ZKException, null, this.ctx); - return; - } - - /* - * Adding bookies to ledger handle - */ - - ArrayList ensemble; - try { - ensemble = bk.bookieWatcher.getNewBookies(metadata.ensembleSize); - } catch (BKNotEnoughBookiesException e) { - LOG.error("Not enough bookies to create ledger" + ledgerId); - cb.createComplete(e.getCode(), null, this.ctx); - return; - } - - /* - * Add ensemble to the configuration - */ - metadata.addEnsemble(new Long(0), ensemble); - try { - lh = new LedgerHandle(bk, ledgerId, metadata, digestType, passwd); - } catch (GeneralSecurityException e) { - LOG.error("Security exception while creating ledger: " + ledgerId, e); - cb.createComplete(BKException.Code.DigestNotInitializedException, null, this.ctx); - return; - } catch (NumberFormatException e) { - LOG.error("Incorrectly entered parameter throttle: " + System.getProperty("throttle"), e); - cb.createComplete(BKException.Code.IncorrectParameterException, null, this.ctx); - return; - } - - lh.writeLedgerConfig(this, null); - - } - - /** - * Implements ZooKeeper stat callback. - * - * @see org.apache.zookeeper.AsyncCallback.StatCallback#processResult(int, String, Object, Stat) - */ - public void processResult(int rc, String path, Object ctx, Stat stat) { - cb.createComplete(rc, lh, this.ctx); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerDeleteOp.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerDeleteOp.java deleted file mode 100644 index eb847bf677b..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerDeleteOp.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.client; - -import org.apache.bookkeeper.client.AsyncCallback.DeleteCallback; -import org.apache.bookkeeper.util.StringUtils; -import org.apache.log4j.Logger; -import org.apache.zookeeper.AsyncCallback.VoidCallback; - -/** - * Encapsulates asynchronous ledger delete operation - * - */ -class LedgerDeleteOp implements VoidCallback { - - static final Logger LOG = Logger.getLogger(LedgerDeleteOp.class); - - BookKeeper bk; - long ledgerId; - DeleteCallback cb; - Object ctx; - - /** - * Constructor - * - * @param bk - * BookKeeper object - * @param ledgerId - * ledger Id - * @param cb - * callback implementation - * @param ctx - * optional control object - */ - LedgerDeleteOp(BookKeeper bk, long ledgerId, DeleteCallback cb, Object ctx) { - this.bk = bk; - this.ledgerId = ledgerId; - this.cb = cb; - this.ctx = ctx; - } - - /** - * Initiates the operation - */ - public void initiate() { - // Asynchronously delete the ledger node in ZK. - // When this completes, it will invoke the callback method below. - bk.getZkHandle().delete(StringUtils.getLedgerNodePath(ledgerId), -1, this, null); - } - - /** - * Implements ZooKeeper Void Callback. - * - * @see org.apache.zookeeper.AsyncCallback.VoidCallback#processResult(int, - * java.lang.String, java.lang.Object) - */ - public void processResult(int rc, String path, Object ctx) { - cb.deleteComplete(rc, this.ctx); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerEntry.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerEntry.java deleted file mode 100644 index 2c2369dd68b..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerEntry.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.apache.bookkeeper.client; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBufferInputStream; - -/** - * Ledger entry. Its a simple tuple containing the ledger id, the entry-id, and - * the entry content. - * - */ - -public class LedgerEntry { - Logger LOG = Logger.getLogger(LedgerEntry.class); - - long ledgerId; - long entryId; - ChannelBufferInputStream entryDataStream; - - int nextReplicaIndexToReadFrom = 0; - - LedgerEntry(long lId, long eId) { - this.ledgerId = lId; - this.entryId = eId; - } - - public long getLedgerId() { - return ledgerId; - } - - public long getEntryId() { - return entryId; - } - - public byte[] getEntry() { - try { - // In general, you can't rely on the available() method of an input - // stream, but ChannelBufferInputStream is backed by a byte[] so it - // accurately knows the # bytes available - byte[] ret = new byte[entryDataStream.available()]; - entryDataStream.readFully(ret); - return ret; - } catch (IOException e) { - // The channelbufferinput stream doesnt really throw the - // ioexceptions, it just has to be in the signature because - // InputStream says so. Hence this code, should never be reached. - LOG.fatal("Unexpected IOException while reading from channel buffer", e); - return new byte[0]; - } - } - - public InputStream getEntryInputStream() { - return entryDataStream; - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerHandle.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerHandle.java deleted file mode 100644 index e37806f9d33..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerHandle.java +++ /dev/null @@ -1,512 +0,0 @@ -package org.apache.bookkeeper.client; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.net.InetSocketAddress; -import java.security.GeneralSecurityException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Queue; -import java.util.concurrent.Semaphore; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.AsyncCallback.AddCallback; -import org.apache.bookkeeper.client.AsyncCallback.CloseCallback; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.client.LedgerMetadata; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback; -import org.apache.bookkeeper.util.SafeRunnable; -import org.apache.bookkeeper.util.StringUtils; - -import org.apache.log4j.Logger; - -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.AsyncCallback.StatCallback; -import org.apache.zookeeper.data.Stat; -import org.jboss.netty.buffer.ChannelBuffer; - -/** - * Ledger handle contains ledger metadata and is used to access the read and - * write operations to a ledger. - */ -public class LedgerHandle implements ReadCallback, AddCallback, CloseCallback { - final static Logger LOG = Logger.getLogger(LedgerHandle.class); - - final byte[] ledgerKey; - final LedgerMetadata metadata; - final BookKeeper bk; - final long ledgerId; - long lastAddPushed; - long lastAddConfirmed; - final DigestManager macManager; - final DistributionSchedule distributionSchedule; - - final Semaphore opCounterSem; - private Integer throttling = 5000; - - final Queue pendingAddOps = new ArrayDeque(); - - LedgerHandle(BookKeeper bk, long ledgerId, LedgerMetadata metadata, - DigestType digestType, byte[] password) - throws GeneralSecurityException, NumberFormatException { - this.bk = bk; - this.metadata = metadata; - if (metadata.isClosed()) { - lastAddConfirmed = lastAddPushed = metadata.close; - } else { - lastAddConfirmed = lastAddPushed = -1; - } - - this.ledgerId = ledgerId; - - String throttleValue = System.getProperty("throttle"); - if(throttleValue != null){ - this.throttling = new Integer(throttleValue); - } - this.opCounterSem = new Semaphore(throttling); - - macManager = DigestManager.instantiate(ledgerId, password, digestType); - this.ledgerKey = MacDigestManager.genDigest("ledger", password); - distributionSchedule = new RoundRobinDistributionSchedule( - metadata.quorumSize, metadata.ensembleSize); - } - - /** - * Get the id of the current ledger - * - * @return - */ - public long getId() { - return ledgerId; - } - - /** - * Get the last confirmed entry id on this ledger - * - * @return - */ - public long getLastAddConfirmed() { - return lastAddConfirmed; - } - - /** - * Get the entry id of the last entry that has been enqueued for addition (but - * may not have possibly been persited to the ledger) - * - * @return - */ - public long getLastAddPushed() { - return lastAddPushed; - } - - /** - * Get the Ledger's key/password. - * - * @return byte array for the ledger's key/password. - */ - public byte[] getLedgerKey() { - return ledgerKey; - } - - /** - * Get the LedgerMetadata - * - * @return LedgerMetadata for the LedgerHandle - */ - public LedgerMetadata getLedgerMetadata() { - return metadata; - } - - /** - * Get the DigestManager - * - * @return DigestManager for the LedgerHandle - */ - public DigestManager getDigestManager() { - return macManager; - } - - /** - * Return total number of available slots. - * - * @return int available slots - */ - Semaphore getAvailablePermits(){ - return this.opCounterSem; - } - - /** - * Get the Distribution Schedule - * - * @return DistributionSchedule for the LedgerHandle - */ - public DistributionSchedule getDistributionSchedule() { - return distributionSchedule; - } - - public void writeLedgerConfig(StatCallback callback, Object ctx) { - bk.getZkHandle().setData(StringUtils.getLedgerNodePath(ledgerId), - metadata.serialize(), -1, callback, ctx); - } - - /** - * Close this ledger synchronously. - * - */ - public void close() throws InterruptedException { - SyncCounter counter = new SyncCounter(); - counter.inc(); - - asyncClose(this, counter); - - counter.block(0); - } - - /** - * Asynchronous close, any adds in flight will return errors - * - * @param cb - * callback implementation - * @param ctx - * control object - * @throws InterruptedException - */ - public void asyncClose(CloseCallback cb, Object ctx) { - asyncClose(cb, ctx, BKException.Code.LedgerClosedException); - } - - /** - * Same as public version of asynClose except that this one takes an - * additional parameter which is the return code to hand to all the pending - * add ops - * - * @param cb - * @param ctx - * @param rc - */ - private void asyncClose(final CloseCallback cb, final Object ctx, final int rc) { - - bk.mainWorkerPool.submitOrdered(ledgerId, new SafeRunnable() { - - @Override - public void safeRun() { - // Close operation is idempotent, so no need to check if we are - // already closed - metadata.close(lastAddConfirmed); - errorOutPendingAdds(rc); - lastAddPushed = lastAddConfirmed; - - if (LOG.isDebugEnabled()) { - LOG.debug("Closing ledger: " + ledgerId + " at entryId: " - + metadata.close); - } - - writeLedgerConfig(new StatCallback() { - @Override - public void processResult(int rc, String path, Object subctx, - Stat stat) { - if (rc != KeeperException.Code.OK.intValue()) { - cb.closeComplete(BKException.Code.ZKException, LedgerHandle.this, - ctx); - } else { - cb.closeComplete(BKException.Code.OK, LedgerHandle.this, ctx); - } - } - }, null); - - } - }); - } - - /** - * Read a sequence of entries synchronously. - * - * @param firstEntry - * id of first entry of sequence (included) - * @param lastEntry - * id of last entry of sequence (included) - * - */ - public Enumeration readEntries(long firstEntry, long lastEntry) - throws InterruptedException, BKException { - SyncCounter counter = new SyncCounter(); - counter.inc(); - - asyncReadEntries(firstEntry, lastEntry, this, counter); - - counter.block(0); - if (counter.getrc() != BKException.Code.OK) { - throw BKException.create(counter.getrc()); - } - - return counter.getSequence(); - } - - /** - * Read a sequence of entries asynchronously. - * - * @param firstEntry - * id of first entry of sequence - * @param lastEntry - * id of last entry of sequence - * @param cb - * object implementing read callback interface - * @param ctx - * control object - */ - public void asyncReadEntries(long firstEntry, long lastEntry, - ReadCallback cb, Object ctx) throws InterruptedException { - // Little sanity check - if (firstEntry < 0 || lastEntry > lastAddConfirmed - || firstEntry > lastEntry) { - cb.readComplete(BKException.Code.ReadException, this, null, ctx); - return; - } - - new PendingReadOp(this, firstEntry, lastEntry, cb, ctx).initiate(); - } - - /** - * Add entry synchronously to an open ledger. - * - * @param data - * array of bytes to be written to the ledger - */ - - public long addEntry(byte[] data) throws InterruptedException, BKException { - LOG.debug("Adding entry " + data); - SyncCounter counter = new SyncCounter(); - counter.inc(); - - asyncAddEntry(data, this, counter); - counter.block(0); - - return counter.getrc(); - } - - /** - * Add entry asynchronously to an open ledger. - * - * @param data - * array of bytes to be written - * @param cb - * object implementing callbackinterface - * @param ctx - * some control object - */ - public void asyncAddEntry(final byte[] data, final AddCallback cb, - final Object ctx) throws InterruptedException { - opCounterSem.acquire(); - - try{ - bk.mainWorkerPool.submitOrdered(ledgerId, new SafeRunnable() { - @Override - public void safeRun() { - if (metadata.isClosed()) { - LOG.warn("Attempt to add to closed ledger: " + ledgerId); - LedgerHandle.this.opCounterSem.release(); - cb.addComplete(BKException.Code.LedgerClosedException, - LedgerHandle.this, -1, ctx); - return; - } - - long entryId = ++lastAddPushed; - PendingAddOp op = new PendingAddOp(LedgerHandle.this, cb, ctx, entryId); - pendingAddOps.add(op); - ChannelBuffer toSend = macManager.computeDigestAndPackageForSending( - entryId, lastAddConfirmed, data); - op.initiate(toSend); - } - }); - } catch (RuntimeException e) { - opCounterSem.release(); - throw e; - } - } - - // close the ledger and send fails to all the adds in the pipeline - void handleUnrecoverableErrorDuringAdd(int rc) { - asyncClose(NoopCloseCallback.instance, null, rc); - } - - void errorOutPendingAdds(int rc) { - PendingAddOp pendingAddOp; - while ((pendingAddOp = pendingAddOps.poll()) != null) { - pendingAddOp.submitCallback(rc); - } - } - - void sendAddSuccessCallbacks() { - // Start from the head of the queue and proceed while there are - // entries that have had all their responses come back - PendingAddOp pendingAddOp; - while ((pendingAddOp = pendingAddOps.peek()) != null) { - if (pendingAddOp.numResponsesPending != 0) { - return; - } - pendingAddOps.remove(); - lastAddConfirmed = pendingAddOp.entryId; - pendingAddOp.submitCallback(BKException.Code.OK); - } - - } - - void handleBookieFailure(InetSocketAddress addr, final int bookieIndex) { - InetSocketAddress newBookie; - - if (LOG.isDebugEnabled()) { - LOG.debug("Handling failure of bookie: " + addr + " index: " - + bookieIndex); - } - - try { - newBookie = bk.bookieWatcher - .getAdditionalBookie(metadata.currentEnsemble); - } catch (BKNotEnoughBookiesException e) { - LOG - .error("Could not get additional bookie to remake ensemble, closing ledger: " - + ledgerId); - handleUnrecoverableErrorDuringAdd(e.getCode()); - return; - } - - final ArrayList newEnsemble = new ArrayList( - metadata.currentEnsemble); - newEnsemble.set(bookieIndex, newBookie); - - if (LOG.isDebugEnabled()) { - LOG.debug("Changing ensemble from: " + metadata.currentEnsemble + " to: " - + newEnsemble + " for ledger: " + ledgerId + " starting at entry: " - + (lastAddConfirmed + 1)); - } - - metadata.addEnsemble(lastAddConfirmed + 1, newEnsemble); - - writeLedgerConfig(new StatCallback() { - @Override - public void processResult(final int rc, String path, Object ctx, Stat stat) { - - bk.mainWorkerPool.submitOrdered(ledgerId, new SafeRunnable() { - @Override - public void safeRun() { - if (rc != KeeperException.Code.OK.intValue()) { - LOG - .error("Could not persist ledger metadata while changing ensemble to: " - + newEnsemble + " , closing ledger"); - handleUnrecoverableErrorDuringAdd(BKException.Code.ZKException); - return; - } - - for (PendingAddOp pendingAddOp : pendingAddOps) { - pendingAddOp.unsetSuccessAndSendWriteRequest(bookieIndex); - } - } - }); - - } - }, null); - - } - - void recover(GenericCallback cb) { - if (metadata.isClosed()) { - // We are already closed, nothing to do - cb.operationComplete(BKException.Code.OK, null); - return; - } - - new LedgerRecoveryOp(this, cb).initiate(); - } - - static class NoopCloseCallback implements CloseCallback { - static NoopCloseCallback instance = new NoopCloseCallback(); - - @Override - public void closeComplete(int rc, LedgerHandle lh, Object ctx) { - // noop - } - } - - /** - * Implementation of callback interface for synchronous read method. - * - * @param rc - * return code - * @param leder - * ledger identifier - * @param seq - * sequence of entries - * @param ctx - * control object - */ - public void readComplete(int rc, LedgerHandle lh, - Enumeration seq, Object ctx) { - - SyncCounter counter = (SyncCounter) ctx; - synchronized (counter) { - counter.setSequence(seq); - counter.setrc(rc); - counter.dec(); - counter.notify(); - } - } - - /** - * Implementation of callback interface for synchronous read method. - * - * @param rc - * return code - * @param leder - * ledger identifier - * @param entry - * entry identifier - * @param ctx - * control object - */ - public void addComplete(int rc, LedgerHandle lh, long entry, Object ctx) { - SyncCounter counter = (SyncCounter) ctx; - - counter.setrc(rc); - counter.dec(); - } - - /** - * Close callback method - * - * @param rc - * @param lh - * @param ctx - */ - public void closeComplete(int rc, LedgerHandle lh, Object ctx) { - - SyncCounter counter = (SyncCounter) ctx; - counter.setrc(rc); - synchronized (counter) { - counter.dec(); - counter.notify(); - } - - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerMetadata.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerMetadata.java deleted file mode 100644 index eae3e6e33ca..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerMetadata.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -import org.apache.bookkeeper.util.StringUtils; -import org.apache.log4j.Logger; - -/** - * This class encapsulates all the ledger metadata that is persistently stored - * in zookeeper. It provides parsing and serialization methods of such metadata. - * - */ -public class LedgerMetadata { - static final Logger LOG = Logger.getLogger(LedgerMetadata.class); - - private static final String closed = "CLOSED"; - private static final String lSplitter = "\n"; - private static final String tSplitter = "\t"; - - // can't use -1 for NOTCLOSED because that is reserved for a closed, empty - // ledger - public static final int NOTCLOSED = -101; - int ensembleSize; - int quorumSize; - long close; - private SortedMap> ensembles = new TreeMap>(); - ArrayList currentEnsemble; - - public LedgerMetadata(int ensembleSize, int quorumSize) { - this.ensembleSize = ensembleSize; - this.quorumSize = quorumSize; - this.close = NOTCLOSED; - }; - - private LedgerMetadata() { - this(0, 0); - } - - /** - * Get the Map of bookie ensembles for the various ledger fragments - * that make up the ledger. - * - * @return SortedMap of Ledger Fragments and the corresponding - * bookie ensembles that store the entries. - */ - public SortedMap> getEnsembles() { - return ensembles; - } - - boolean isClosed() { - return close != NOTCLOSED; - } - - void close(long entryId) { - close = entryId; - } - - void addEnsemble(long startEntryId, ArrayList ensemble) { - assert ensembles.isEmpty() || startEntryId >= ensembles.lastKey(); - - ensembles.put(startEntryId, ensemble); - currentEnsemble = ensemble; - } - - ArrayList getEnsemble(long entryId) { - // the head map cannot be empty, since we insert an ensemble for - // entry-id 0, right when we start - return ensembles.get(ensembles.headMap(entryId + 1).lastKey()); - } - - /** - * the entry id > the given entry-id at which the next ensemble change takes - * place ( -1 if no further ensemble changes) - * - * @param entryId - * @return - */ - long getNextEnsembleChange(long entryId) { - SortedMap> tailMap = ensembles.tailMap(entryId + 1); - - if (tailMap.isEmpty()) { - return -1; - } else { - return tailMap.firstKey(); - } - } - - /** - * Generates a byte array based on a LedgerConfig object received. - * - * @param config - * LedgerConfig object - * @return byte[] - */ - public byte[] serialize() { - StringBuilder s = new StringBuilder(); - s.append(quorumSize).append(lSplitter).append(ensembleSize); - - for (Map.Entry> entry : ensembles.entrySet()) { - s.append(lSplitter).append(entry.getKey()); - for (InetSocketAddress addr : entry.getValue()) { - s.append(tSplitter); - StringUtils.addrToString(s, addr); - } - } - - if (close != NOTCLOSED) { - s.append(lSplitter).append(close).append(tSplitter).append(closed); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Serialized config: " + s.toString()); - } - - return s.toString().getBytes(); - } - - /** - * Parses a given byte array and transforms into a LedgerConfig object - * - * @param array - * byte array to parse - * @return LedgerConfig - * @throws IOException - * if the given byte[] cannot be parsed - */ - - static LedgerMetadata parseConfig(byte[] bytes) throws IOException { - - LedgerMetadata lc = new LedgerMetadata(); - String config = new String(bytes); - - if (LOG.isDebugEnabled()) { - LOG.debug("Parsing Config: " + config); - } - - String lines[] = config.split(lSplitter); - - if (lines.length < 2) { - throw new IOException("Quorum size or ensemble size absent from config: " + config); - } - - try { - lc.quorumSize = new Integer(lines[0]); - lc.ensembleSize = new Integer(lines[1]); - - for (int i = 2; i < lines.length; i++) { - String parts[] = lines[i].split(tSplitter); - - if (parts[1].equals(closed)) { - lc.close = new Long(parts[0]); - break; - } - - ArrayList addrs = new ArrayList(); - for (int j = 1; j < parts.length; j++) { - addrs.add(StringUtils.parseAddr(parts[j])); - } - lc.addEnsemble(new Long(parts[0]), addrs); - } - } catch (NumberFormatException e) { - throw new IOException(e); - } - return lc; - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerOpenOp.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerOpenOp.java deleted file mode 100644 index 1c08d9850fb..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerOpenOp.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.client; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import org.apache.bookkeeper.client.AsyncCallback.OpenCallback; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.util.StringUtils; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.AsyncCallback.DataCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback; -import org.apache.zookeeper.data.Stat; - -/** - * Encapsulates the ledger open operation - * - */ -class LedgerOpenOp implements DataCallback { - static final Logger LOG = Logger.getLogger(LedgerOpenOp.class); - - BookKeeper bk; - long ledgerId; - OpenCallback cb; - Object ctx; - LedgerHandle lh; - byte[] passwd; - DigestType digestType; - - /** - * Constructor. - * - * @param bk - * @param ledgerId - * @param digestType - * @param passwd - * @param cb - * @param ctx - */ - - public LedgerOpenOp(BookKeeper bk, long ledgerId, DigestType digestType, byte[] passwd, OpenCallback cb, Object ctx) { - this.bk = bk; - this.ledgerId = ledgerId; - this.passwd = passwd; - this.cb = cb; - this.ctx = ctx; - this.digestType = digestType; - } - - /** - * Inititates the ledger open operation - */ - public void initiate() { - /** - * Asynchronously read the ledger metadata node. - */ - - bk.getZkHandle().getData(StringUtils.getLedgerNodePath(ledgerId), false, this, ctx); - - } - - /** - * Implements ZooKeeper data callback. - * @see org.apache.zookeeper.AsyncCallback.DataCallback#processResult(int, String, Object, byte[], Stat) - */ - public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { - - if (rc == KeeperException.Code.NONODE.intValue()) { - if (LOG.isDebugEnabled()) { - LOG.debug("No such ledger: " + ledgerId, KeeperException.create(KeeperException.Code.get(rc), path)); - } - cb.openComplete(BKException.Code.NoSuchLedgerExistsException, null, this.ctx); - return; - } - if (rc != KeeperException.Code.OK.intValue()) { - LOG.error("Could not read metadata for ledger: " + ledgerId, KeeperException.create(KeeperException.Code - .get(rc), path)); - cb.openComplete(BKException.Code.ZKException, null, this.ctx); - return; - } - - LedgerMetadata metadata; - try { - metadata = LedgerMetadata.parseConfig(data); - } catch (IOException e) { - LOG.error("Could not parse ledger metadata for ledger: " + ledgerId, e); - cb.openComplete(BKException.Code.ZKException, null, this.ctx); - return; - } - - try { - lh = new LedgerHandle(bk, ledgerId, metadata, digestType, passwd); - } catch (GeneralSecurityException e) { - LOG.error("Security exception while opening ledger: " + ledgerId, e); - cb.openComplete(BKException.Code.DigestNotInitializedException, null, this.ctx); - return; - } catch (NumberFormatException e) { - LOG.error("Incorrectly entered parameter throttle: " + System.getProperty("throttle"), e); - cb.openComplete(BKException.Code.IncorrectParameterException, null, this.ctx); - return; - } - - if (metadata.close != LedgerMetadata.NOTCLOSED) { - // Ledger was closed properly - cb.openComplete(BKException.Code.OK, lh, this.ctx); - return; - } - - lh.recover(new GenericCallback() { - @Override - public void operationComplete(int rc, Void result) { - if (rc != BKException.Code.OK) { - cb.openComplete(BKException.Code.LedgerRecoveryException, null, LedgerOpenOp.this.ctx); - } else { - cb.openComplete(BKException.Code.OK, lh, LedgerOpenOp.this.ctx); - } - } - }); - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java deleted file mode 100644 index 039a2311b90..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/LedgerRecoveryOp.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.util.Enumeration; - -import org.apache.bookkeeper.client.AsyncCallback.AddCallback; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.BKException.BKDigestMatchException; -import org.apache.bookkeeper.client.LedgerHandle.NoopCloseCallback; -import org.apache.bookkeeper.client.DigestManager.RecoveryData; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback; -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBuffer; - -/** - * This class encapsulated the ledger recovery operation. It first does a read - * with entry-id of -1 to all bookies. Then starting from the last confirmed - * entry (from hints in the ledger entries), it reads forward until it is not - * able to find a particular entry. It closes the ledger at that entry. - * - */ -class LedgerRecoveryOp implements ReadEntryCallback, ReadCallback, AddCallback { - static final Logger LOG = Logger.getLogger(LedgerRecoveryOp.class); - LedgerHandle lh; - int numResponsesPending; - boolean proceedingWithRecovery = false; - long maxAddPushed = -1; - long maxAddConfirmed = -1; - - GenericCallback cb; - - public LedgerRecoveryOp(LedgerHandle lh, GenericCallback cb) { - this.cb = cb; - this.lh = lh; - numResponsesPending = lh.metadata.ensembleSize; - } - - public void initiate() { - for (int i = 0; i < lh.metadata.currentEnsemble.size(); i++) { - lh.bk.bookieClient.readEntry(lh.metadata.currentEnsemble.get(i), lh.ledgerId, -1, this, i); - } - } - - public synchronized void readEntryComplete(final int rc, final long ledgerId, final long entryId, - final ChannelBuffer buffer, final Object ctx) { - - // Already proceeding with recovery, nothing to do - if (proceedingWithRecovery) { - return; - } - - int bookieIndex = (Integer) ctx; - - numResponsesPending--; - - boolean heardValidResponse = false; - - if (rc == BKException.Code.OK) { - try { - RecoveryData recoveryData = lh.macManager.verifyDigestAndReturnLastConfirmed(buffer); - maxAddConfirmed = Math.max(maxAddConfirmed, recoveryData.lastAddConfirmed); - maxAddPushed = Math.max(maxAddPushed, recoveryData.entryId); - heardValidResponse = true; - } catch (BKDigestMatchException e) { - // Too bad, this bookie didnt give us a valid answer, we - // still - // might be able to recover though so continue - LOG.error("Mac mismatch while reading last entry from bookie: " - + lh.metadata.currentEnsemble.get(bookieIndex)); - } - } - - if (rc == BKException.Code.NoSuchLedgerExistsException || rc == BKException.Code.NoSuchEntryException) { - // this still counts as a valid response, e.g., if the - // client - // crashed without writing any entry - heardValidResponse = true; - } - - // other return codes dont count as valid responses - if (heardValidResponse && lh.distributionSchedule.canProceedWithRecovery(bookieIndex)) { - proceedingWithRecovery = true; - lh.lastAddPushed = lh.lastAddConfirmed = maxAddConfirmed; - doRecoveryRead(); - return; - } - - if (numResponsesPending == 0) { - // Have got all responses back but was still not enough to - // start - // recovery, just fail the operation - LOG.error("While recovering ledger: " + ledgerId + " did not hear success responses from all quorums"); - cb.operationComplete(BKException.Code.LedgerRecoveryException, null); - } - - } - - /** - * Try to read past the last confirmed. - */ - private void doRecoveryRead() { - try{ - lh.lastAddConfirmed++; - lh.asyncReadEntries(lh.lastAddConfirmed, lh.lastAddConfirmed, this, null); - } catch (InterruptedException e) { - LOG.error("Interrupted while trying to read entry.", e); - Thread.currentThread().interrupt(); - } - } - - @Override - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - // get back to prev value - lh.lastAddConfirmed--; - if (rc == BKException.Code.OK) { - try{ - lh.asyncAddEntry(seq.nextElement().getEntry(), this, null); - } catch (InterruptedException e) { - LOG.error("Interrupted while adding entry.", e); - Thread.currentThread().interrupt(); - } - return; - } - - if (rc == BKException.Code.NoSuchEntryException || rc == BKException.Code.NoSuchLedgerExistsException) { - lh.asyncClose(NoopCloseCallback.instance, null); - // we don't need to wait for the close to complete. Since we mark - // the - // ledger closed in memory, the application wont be able to add to - // it - - cb.operationComplete(BKException.Code.OK, null); - return; - } - - // otherwise, some other error, we can't handle - LOG.error("Failure " + BKException.getMessage(rc) + " while reading entry: " + lh.lastAddConfirmed + 1 - + " ledger: " + lh.ledgerId + " while recovering ledger"); - cb.operationComplete(rc, null); - return; - } - - @Override - public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) { - if (rc != BKException.Code.OK) { - // Give up, we can't recover from this error - - LOG.error("Failure " + BKException.getMessage(rc) + " while writing entry: " + lh.lastAddConfirmed + 1 - + " ledger: " + lh.ledgerId + " while recovering ledger"); - cb.operationComplete(rc, null); - return; - } - - doRecoveryRead(); - - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/MacDigestManager.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/MacDigestManager.java deleted file mode 100644 index 1dbc27f2ab3..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/MacDigestManager.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.apache.bookkeeper.client; - -/* -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -class MacDigestManager extends DigestManager { - public static String DIGEST_ALGORITHM = "SHA-1"; - public static String KEY_ALGORITHM = "HmacSHA1"; - Mac mac; - - public MacDigestManager(long ledgerId, byte[] passwd) throws GeneralSecurityException { - super(ledgerId); - byte[] macKey = genDigest("mac", passwd); - SecretKeySpec keySpec = new SecretKeySpec(macKey, KEY_ALGORITHM); - mac = Mac.getInstance(KEY_ALGORITHM); - mac.init(keySpec); - - - } - - static byte[] genDigest(String pad, byte[] passwd) throws NoSuchAlgorithmException { - MessageDigest digest = MessageDigest.getInstance(DIGEST_ALGORITHM); - digest.update(pad.getBytes()); - digest.update(passwd); - return digest.digest(); - } - - @Override - int getMacCodeLength() { - return 20; - } - - - @Override - byte[] getValueAndReset() { - return mac.doFinal(); - } - - @Override - void update(byte[] data, int offset, int length) { - mac.update(data, offset, length); - } - - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingAddOp.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingAddOp.java deleted file mode 100644 index 1b6d16791c1..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingAddOp.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.net.InetSocketAddress; -import org.apache.bookkeeper.client.AsyncCallback.AddCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBuffer; - -/** - * This represents a pending add operation. When it has got success from all - * bookies, it sees if its at the head of the pending adds queue, and if yes, - * sends ack back to the application. If a bookie fails, a replacement is made - * and placed at the same position in the ensemble. The pending adds are then - * rereplicated. - * - * - */ -class PendingAddOp implements WriteCallback { - final static Logger LOG = Logger.getLogger(PendingAddOp.class); - - ChannelBuffer toSend; - AddCallback cb; - Object ctx; - long entryId; - boolean[] successesSoFar; - int numResponsesPending; - LedgerHandle lh; - - PendingAddOp(LedgerHandle lh, AddCallback cb, Object ctx, long entryId) { - this.lh = lh; - this.cb = cb; - this.ctx = ctx; - this.entryId = entryId; - successesSoFar = new boolean[lh.metadata.quorumSize]; - numResponsesPending = successesSoFar.length; - } - - void sendWriteRequest(int bookieIndex, int arrayIndex) { - lh.bk.bookieClient.addEntry(lh.metadata.currentEnsemble.get(bookieIndex), lh.ledgerId, lh.ledgerKey, entryId, toSend, - this, arrayIndex); - } - - void unsetSuccessAndSendWriteRequest(int bookieIndex) { - if (toSend == null) { - // this addOp hasn't yet had its mac computed. When the mac is - // computed, its write requests will be sent, so no need to send it - // now - return; - } - - int replicaIndex = lh.distributionSchedule.getReplicaIndex(entryId, bookieIndex); - if (replicaIndex < 0) { - if (LOG.isDebugEnabled()) { - LOG.debug("Leaving unchanged, ledger: " + lh.ledgerId + " entry: " + entryId + " bookie index: " - + bookieIndex); - } - return; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Unsetting success for ledger: " + lh.ledgerId + " entry: " + entryId + " bookie index: " - + bookieIndex); - } - - // if we had already heard a success from this array index, need to - // increment our number of responses that are pending, since we are - // going to unset this success - if (successesSoFar[replicaIndex]) { - successesSoFar[replicaIndex] = false; - numResponsesPending++; - } - - sendWriteRequest(bookieIndex, replicaIndex); - } - - void initiate(ChannelBuffer toSend) { - this.toSend = toSend; - for (int i = 0; i < successesSoFar.length; i++) { - int bookieIndex = lh.distributionSchedule.getBookieIndex(entryId, i); - sendWriteRequest(bookieIndex, i); - } - } - - @Override - public void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, Object ctx) { - - Integer replicaIndex = (Integer) ctx; - int bookieIndex = lh.distributionSchedule.getBookieIndex(entryId, replicaIndex); - - if (!lh.metadata.currentEnsemble.get(bookieIndex).equals(addr)) { - // ensemble has already changed, failure of this addr is immaterial - LOG.warn("Write did not succeed: " + ledgerId + ", " + entryId + ". But we have already fixed it."); - return; - } - - if (rc != BKException.Code.OK) { - LOG.warn("Write did not succeed: " + ledgerId + ", " + entryId); - lh.handleBookieFailure(addr, bookieIndex); - return; - } - - - if (!successesSoFar[replicaIndex]) { - successesSoFar[replicaIndex] = true; - numResponsesPending--; - - // do some quick checks to see if some adds may have finished. All - // this will be checked under locks again - if (numResponsesPending == 0 && lh.pendingAddOps.peek() == this) { - lh.sendAddSuccessCallbacks(); - } - } - } - - void submitCallback(final int rc) { - cb.addComplete(rc, lh, entryId, ctx); - lh.opCounterSem.release(); - } - -} \ No newline at end of file diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingReadOp.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingReadOp.java deleted file mode 100644 index 81614196deb..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/PendingReadOp.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.apache.bookkeeper.client; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.net.InetSocketAddress; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.NoSuchElementException; -import java.util.Queue; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.BKException.BKDigestMatchException; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback; -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBufferInputStream; - -/** - * Sequence of entries of a ledger that represents a pending read operation. - * When all the data read has come back, the application callback is called. - * This class could be improved because we could start pushing data to the - * application as soon as it arrives rather than waiting for the whole thing. - * - */ - -class PendingReadOp implements Enumeration, ReadEntryCallback { - Logger LOG = Logger.getLogger(PendingReadOp.class); - - Queue seq; - ReadCallback cb; - Object ctx; - LedgerHandle lh; - long numPendingReads; - long startEntryId; - long endEntryId; - - PendingReadOp(LedgerHandle lh, long startEntryId, long endEntryId, ReadCallback cb, Object ctx) { - - seq = new ArrayDeque((int) (endEntryId - startEntryId)); - this.cb = cb; - this.ctx = ctx; - this.lh = lh; - this.startEntryId = startEntryId; - this.endEntryId = endEntryId; - numPendingReads = endEntryId - startEntryId + 1; - } - - public void initiate() throws InterruptedException { - long nextEnsembleChange = startEntryId, i = startEntryId; - - ArrayList ensemble = null; - do { - - if(LOG.isDebugEnabled()){ - LOG.debug("Acquiring lock: " + i); - } - - lh.opCounterSem.acquire(); - - if (i == nextEnsembleChange) { - ensemble = lh.metadata.getEnsemble(i); - nextEnsembleChange = lh.metadata.getNextEnsembleChange(i); - } - LedgerEntry entry = new LedgerEntry(lh.ledgerId, i); - seq.add(entry); - i++; - sendRead(ensemble, entry, BKException.Code.ReadException); - - } while (i <= endEntryId); - } - - void sendRead(ArrayList ensemble, LedgerEntry entry, int lastErrorCode) { - if (entry.nextReplicaIndexToReadFrom >= lh.metadata.quorumSize) { - // we are done, the read has failed from all replicas, just fail the - // read - submitCallback(lastErrorCode); - return; - } - - int bookieIndex = lh.distributionSchedule.getBookieIndex(entry.entryId, entry.nextReplicaIndexToReadFrom); - entry.nextReplicaIndexToReadFrom++; - lh.bk.bookieClient.readEntry(ensemble.get(bookieIndex), lh.ledgerId, entry.entryId, this, entry); - } - - void logErrorAndReattemptRead(LedgerEntry entry, String errMsg, int rc) { - ArrayList ensemble = lh.metadata.getEnsemble(entry.entryId); - int bookeIndex = lh.distributionSchedule.getBookieIndex(entry.entryId, entry.nextReplicaIndexToReadFrom - 1); - LOG.error(errMsg + " while reading entry: " + entry.entryId + " ledgerId: " + lh.ledgerId + " from bookie: " - + ensemble.get(bookeIndex)); - sendRead(ensemble, entry, rc); - return; - } - - @Override - public void readEntryComplete(int rc, long ledgerId, final long entryId, final ChannelBuffer buffer, Object ctx) { - final LedgerEntry entry = (LedgerEntry) ctx; - - if (rc != BKException.Code.OK) { - logErrorAndReattemptRead(entry, "Error: " + BKException.getMessage(rc), rc); - return; - } - - ChannelBufferInputStream is; - try { - is = lh.macManager.verifyDigestAndReturnData(entryId, buffer); - } catch (BKDigestMatchException e) { - logErrorAndReattemptRead(entry, "Mac mismatch", BKException.Code.DigestMatchException); - return; - } - - entry.entryDataStream = is; - - numPendingReads--; - if (numPendingReads == 0) { - submitCallback(BKException.Code.OK); - } - - if(LOG.isDebugEnabled()){ - LOG.debug("Releasing lock: " + entryId); - } - - lh.opCounterSem.release(); - - if(numPendingReads < 0) - LOG.error("Read too many values"); - } - - private void submitCallback(int code){ - cb.readComplete(code, lh, PendingReadOp.this, PendingReadOp.this.ctx); - } - public boolean hasMoreElements() { - return !seq.isEmpty(); - } - - public LedgerEntry nextElement() throws NoSuchElementException { - return seq.remove(); - } - - public int size() { - return seq.size(); - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/RoundRobinDistributionSchedule.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/RoundRobinDistributionSchedule.java deleted file mode 100644 index 4660ab16990..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/RoundRobinDistributionSchedule.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.apache.bookkeeper.client; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.apache.bookkeeper.util.MathUtils; - -/** - * A specific {@link DistributionSchedule} that places entries in round-robin - * fashion. For ensemble size 3, and quorum size 2, Entry 0 goes to bookie 0 and - * 1, entry 1 goes to bookie 1 and 2, and entry 2 goes to bookie 2 and 0, and so - * on. - * - */ -class RoundRobinDistributionSchedule implements DistributionSchedule { - int quorumSize; - int ensembleSize; - - // covered[i] is true if the quorum starting at bookie index i has been - // covered by a recovery reply - boolean[] covered = null; - int numQuorumsUncovered; - - public RoundRobinDistributionSchedule(int quorumSize, int ensembleSize) { - this.quorumSize = quorumSize; - this.ensembleSize = ensembleSize; - } - - @Override - public int getBookieIndex(long entryId, int replicaIndex) { - return (int) ((entryId + replicaIndex) % ensembleSize); - } - - @Override - public int getReplicaIndex(long entryId, int bookieIndex) { - // NOTE: Java's % operator returns the sign of the dividend and is hence - // not always positive - - int replicaIndex = MathUtils.signSafeMod(bookieIndex - entryId, ensembleSize); - - return replicaIndex < quorumSize ? replicaIndex : -1; - - } - - public synchronized boolean canProceedWithRecovery(int bookieIndexHeardFrom) { - if (covered == null) { - covered = new boolean[ensembleSize]; - numQuorumsUncovered = ensembleSize; - } - - if (numQuorumsUncovered == 0) { - return true; - } - - for (int i = 0; i < quorumSize; i++) { - int quorumStartIndex = MathUtils.signSafeMod(bookieIndexHeardFrom - i, ensembleSize); - if (!covered[quorumStartIndex]) { - covered[quorumStartIndex] = true; - numQuorumsUncovered--; - - if (numQuorumsUncovered == 0) { - return true; - } - } - - } - - return false; - - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/SyncCounter.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/SyncCounter.java deleted file mode 100644 index 1f20ff4e2b0..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/client/SyncCounter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.client; - -import java.util.Enumeration; - -/** - * Implements objects to help with the synchronization of asynchronous calls - * - */ - -class SyncCounter { - int i; - int rc; - int total; - Enumeration seq = null; - LedgerHandle lh = null; - - synchronized void inc() { - i++; - total++; - } - - synchronized void dec() { - i--; - notifyAll(); - } - - synchronized void block(int limit) throws InterruptedException { - while (i > limit) { - int prev = i; - wait(); - if (i == prev) { - break; - } - } - } - - synchronized int total() { - return total; - } - - void setrc(int rc) { - this.rc = rc; - } - - int getrc() { - return rc; - } - - void setSequence(Enumeration seq) { - this.seq = seq; - } - - Enumeration getSequence() { - return seq; - } - - void setLh(LedgerHandle lh) { - this.lh = lh; - } - - LedgerHandle getLh() { - return lh; - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieClient.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieClient.java deleted file mode 100644 index 4911be41407..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieClient.java +++ /dev/null @@ -1,178 +0,0 @@ -package org.apache.bookkeeper.proto; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicLong; -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.bookkeeper.util.OrderedSafeExecutor; -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; - -/** - * Implements the client-side part of the BookKeeper protocol. - * - */ -public class BookieClient { - static final Logger LOG = Logger.getLogger(BookieClient.class); - - // This is global state that should be across all BookieClients - AtomicLong totalBytesOutstanding = new AtomicLong(); - - OrderedSafeExecutor executor; - ClientSocketChannelFactory channelFactory; - ConcurrentHashMap channels = new ConcurrentHashMap(); - - public BookieClient(ClientSocketChannelFactory channelFactory, OrderedSafeExecutor executor) { - this.channelFactory = channelFactory; - this.executor = executor; - } - - public PerChannelBookieClient lookupClient(InetSocketAddress addr) { - PerChannelBookieClient channel = channels.get(addr); - - if (channel == null) { - channel = new PerChannelBookieClient(executor, channelFactory, addr, totalBytesOutstanding); - PerChannelBookieClient prevChannel = channels.putIfAbsent(addr, channel); - if (prevChannel != null) { - channel = prevChannel; - } - } - - return channel; - } - - public void addEntry(final InetSocketAddress addr, final long ledgerId, final byte[] masterKey, final long entryId, - final ChannelBuffer toSend, final WriteCallback cb, final Object ctx) { - - final PerChannelBookieClient client = lookupClient(addr); - - client.connectIfNeededAndDoOp(new GenericCallback() { - @Override - public void operationComplete(int rc, Void result) { - if (rc != BKException.Code.OK) { - cb.writeComplete(rc, ledgerId, entryId, addr, ctx); - return; - } - client.addEntry(ledgerId, masterKey, entryId, toSend, cb, ctx); - } - }); - } - - public void readEntry(final InetSocketAddress addr, final long ledgerId, final long entryId, - final ReadEntryCallback cb, final Object ctx) { - - final PerChannelBookieClient client = lookupClient(addr); - - client.connectIfNeededAndDoOp(new GenericCallback() { - @Override - public void operationComplete(int rc, Void result) { - - if (rc != BKException.Code.OK) { - cb.readEntryComplete(rc, ledgerId, entryId, null, ctx); - return; - } - client.readEntry(ledgerId, entryId, cb, ctx); - } - }); - } - - public void close(){ - for (PerChannelBookieClient channel: channels.values()){ - channel.close(); - } - } - - private static class Counter { - int i; - int total; - - synchronized void inc() { - i++; - total++; - } - - synchronized void dec() { - i--; - notifyAll(); - } - - synchronized void wait(int limit) throws InterruptedException { - while (i > limit) { - wait(); - } - } - - synchronized int total() { - return total; - } - } - - /** - * @param args - * @throws IOException - * @throws NumberFormatException - * @throws InterruptedException - */ - public static void main(String[] args) throws NumberFormatException, IOException, InterruptedException { - if (args.length != 3) { - System.err.println("USAGE: BookieClient bookieHost port ledger#"); - return; - } - WriteCallback cb = new WriteCallback() { - - public void writeComplete(int rc, long ledger, long entry, InetSocketAddress addr, Object ctx) { - Counter counter = (Counter) ctx; - counter.dec(); - if (rc != 0) { - System.out.println("rc = " + rc + " for " + entry + "@" + ledger); - } - } - }; - Counter counter = new Counter(); - byte hello[] = "hello".getBytes(); - long ledger = Long.parseLong(args[2]); - ClientSocketChannelFactory channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors - .newCachedThreadPool()); - OrderedSafeExecutor executor = new OrderedSafeExecutor(1); - BookieClient bc = new BookieClient(channelFactory, executor); - InetSocketAddress addr = new InetSocketAddress(args[0], Integer.parseInt(args[1])); - - for (int i = 0; i < 100000; i++) { - counter.inc(); - bc.addEntry(addr, ledger, new byte[0], i, ChannelBuffers.wrappedBuffer(hello), cb, counter); - } - counter.wait(0); - System.out.println("Total = " + counter.total()); - channelFactory.releaseExternalResources(); - executor.shutdown(); - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieProtocol.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieProtocol.java deleted file mode 100644 index c35685b9868..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieProtocol.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.apache.bookkeeper.proto; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -/** - * The packets of the Bookie protocol all have a 4-byte integer indicating the - * type of request or response at the very beginning of the packet followed by a - * payload. - * - */ -public interface BookieProtocol { - /** - * The Add entry request payload will be a ledger entry exactly as it should - * be logged. The response payload will be a 4-byte integer that has the - * error code followed by the 8-byte ledger number and 8-byte entry number - * of the entry written. - */ - public static final int ADDENTRY = 1; - /** - * The Read entry request payload will be the ledger number and entry number - * to read. (The ledger number is an 8-byte integer and the entry number is - * a 8-byte integer.) The response payload will be a 4-byte integer - * representing an error code and a ledger entry if the error code is EOK, - * otherwise it will be the 8-byte ledger number and the 4-byte entry number - * requested. (Note that the first sixteen bytes of the entry happen to be - * the ledger number and entry number as well.) - */ - public static final int READENTRY = 2; - - /** - * The error code that indicates success - */ - public static final int EOK = 0; - /** - * The error code that indicates that the ledger does not exist - */ - public static final int ENOLEDGER = 1; - /** - * The error code that indicates that the requested entry does not exist - */ - public static final int ENOENTRY = 2; - /** - * The error code that indicates an invalid request type - */ - public static final int EBADREQ = 100; - /** - * General error occurred at the server - */ - public static final int EIO = 101; - - /** - * Unauthorized access to ledger - */ - public static final int EUA = 102; - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieServer.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieServer.java deleted file mode 100644 index fd2bd1816e0..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookieServer.java +++ /dev/null @@ -1,208 +0,0 @@ -package org.apache.bookkeeper.proto; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; - -import org.apache.bookkeeper.bookie.Bookie; -import org.apache.bookkeeper.bookie.BookieException; -import org.apache.bookkeeper.proto.NIOServerFactory.Cnxn; -import org.apache.log4j.Logger; - -/** - * Implements the server-side part of the BookKeeper protocol. - * - */ -public class BookieServer implements NIOServerFactory.PacketProcessor, BookkeeperInternalCallbacks.WriteCallback { - int port; - NIOServerFactory nioServerFactory; - volatile boolean down = false; - Bookie bookie; - static Logger LOG = Logger.getLogger(BookieServer.class); - - public BookieServer(int port, String zkServers, File journalDirectory, File ledgerDirectories[]) throws IOException { - this.port = port; - this.bookie = new Bookie(port, zkServers, journalDirectory, ledgerDirectories); - } - - public void start() throws IOException { - nioServerFactory = new NIOServerFactory(port, this); - } - - public void shutdown() throws InterruptedException { - down = true; - nioServerFactory.shutdown(); - bookie.shutdown(); - } - - public boolean isDown() { - return down; - } - - public void join() throws InterruptedException { - nioServerFactory.join(); - } - - /** - * @param args - * @throws IOException - * @throws InterruptedException - */ - public static void main(String[] args) throws IOException, InterruptedException { - if (args.length < 4) { - System.err.println("USAGE: BookieServer port zkServers journalDirectory ledgerDirectory [ledgerDirectory]*"); - return; - } - int port = Integer.parseInt(args[0]); - String zkServers = args[1]; - File journalDirectory = new File(args[2]); - File ledgerDirectory[] = new File[args.length - 3]; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < ledgerDirectory.length; i++) { - ledgerDirectory[i] = new File(args[i + 3]); - if (i != 0) { - sb.append(','); - } - sb.append(ledgerDirectory[i]); - } - String hello = String.format( - "Hello, I'm your bookie, listening on port %1$s. ZKServers are on %2$s. Journals are in %3$s. Ledgers are stored in %4$s.", - port, zkServers, journalDirectory, sb); - LOG.info(hello); - BookieServer bs = new BookieServer(port, zkServers, journalDirectory, ledgerDirectory); - bs.start(); - bs.join(); - } - - public void processPacket(ByteBuffer packet, Cnxn src) { - int type = packet.getInt(); - switch (type) { - case BookieProtocol.ADDENTRY: - try { - byte[] masterKey = new byte[20]; - packet.get(masterKey, 0, 20); - // LOG.debug("Master key: " + new String(masterKey)); - bookie.addEntry(packet.slice(), this, src, masterKey); - } catch (IOException e) { - ByteBuffer bb = packet.duplicate(); - - long ledgerId = bb.getLong(); - long entryId = bb.getLong(); - LOG.error("Error writing " + entryId + "@" + ledgerId, e); - ByteBuffer eio = ByteBuffer.allocate(8 + 16); - eio.putInt(type); - eio.putInt(BookieProtocol.EIO); - eio.putLong(ledgerId); - eio.putLong(entryId); - eio.flip(); - src.sendResponse(new ByteBuffer[] { eio }); - } catch (BookieException e) { - ByteBuffer bb = packet.duplicate(); - long ledgerId = bb.getLong(); - long entryId = bb.getLong(); - - LOG.error("Unauthorized access to ledger " + ledgerId); - - ByteBuffer eio = ByteBuffer.allocate(8 + 16); - eio.putInt(type); - eio.putInt(BookieProtocol.EUA); - eio.putLong(ledgerId); - eio.putLong(entryId); - eio.flip(); - src.sendResponse(new ByteBuffer[] { eio }); - } - break; - case BookieProtocol.READENTRY: - ByteBuffer[] rsp = new ByteBuffer[2]; - ByteBuffer rc = ByteBuffer.allocate(8 + 8 + 8); - rsp[0] = rc; - rc.putInt(type); - - long ledgerId = packet.getLong(); - long entryId = packet.getLong(); - LOG.debug("Received new read request: " + ledgerId + ", " + entryId); - try { - rsp[1] = bookie.readEntry(ledgerId, entryId); - LOG.debug("##### Read entry ##### " + rsp[1].remaining()); - rc.putInt(BookieProtocol.EOK); - } catch (Bookie.NoLedgerException e) { - if (LOG.isTraceEnabled()) { - LOG.error("Error reading " + entryId + "@" + ledgerId, e); - } - rc.putInt(BookieProtocol.ENOLEDGER); - } catch (Bookie.NoEntryException e) { - if (LOG.isTraceEnabled()) { - LOG.error("Error reading " + entryId + "@" + ledgerId, e); - } - rc.putInt(BookieProtocol.ENOENTRY); - } catch (IOException e) { - if (LOG.isTraceEnabled()) { - LOG.error("Error reading " + entryId + "@" + ledgerId, e); - } - rc.putInt(BookieProtocol.EIO); - } - rc.putLong(ledgerId); - rc.putLong(entryId); - rc.flip(); - if (LOG.isTraceEnabled()) { - int rcCode = rc.getInt(); - rc.rewind(); - LOG.trace("Read entry rc = " + rcCode + " for " + entryId + "@" + ledgerId); - } - if (rsp[1] == null) { - // We haven't filled in entry data, so we have to send back - // the ledger and entry ids here - rsp[1] = ByteBuffer.allocate(16); - rsp[1].putLong(ledgerId); - rsp[1].putLong(entryId); - rsp[1].flip(); - } - LOG.debug("Sending response for: " + entryId + ", " + new String(rsp[1].array())); - src.sendResponse(rsp); - break; - default: - ByteBuffer badType = ByteBuffer.allocate(8); - badType.putInt(type); - badType.putInt(BookieProtocol.EBADREQ); - badType.flip(); - src.sendResponse(new ByteBuffer[] { packet }); - } - } - - public void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, Object ctx) { - Cnxn src = (Cnxn) ctx; - ByteBuffer bb = ByteBuffer.allocate(24); - bb.putInt(BookieProtocol.ADDENTRY); - bb.putInt(rc); - bb.putLong(ledgerId); - bb.putLong(entryId); - bb.flip(); - if (LOG.isTraceEnabled()) { - LOG.trace("Add entry rc = " + rc + " for " + entryId + "@" + ledgerId); - } - src.sendResponse(new ByteBuffer[] { bb }); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookkeeperInternalCallbacks.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookkeeperInternalCallbacks.java deleted file mode 100644 index a4801526a80..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/BookkeeperInternalCallbacks.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.proto; - -import java.net.InetSocketAddress; -import org.jboss.netty.buffer.ChannelBuffer; - -/** - * Declaration of a callback interfaces used in bookkeeper client library but - * not exposed to the client application. - */ - -public class BookkeeperInternalCallbacks { - /** - * Callback for calls from BookieClient objects. Such calls are for replies - * of write operations (operations to add an entry to a ledger). - * - */ - - public interface WriteCallback { - void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, Object ctx); - } - - public interface GenericCallback { - void operationComplete(int rc, T result); - } - - /** - * Declaration of a callback implementation for calls from BookieClient objects. - * Such calls are for replies of read operations (operations to read an entry - * from a ledger). - * - */ - - public interface ReadEntryCallback { - void readEntryComplete(int rc, long ledgerId, long entryId, ChannelBuffer buffer, Object ctx); - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/NIOServerFactory.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/NIOServerFactory.java deleted file mode 100644 index 46cb5b7f32b..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/NIOServerFactory.java +++ /dev/null @@ -1,517 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.bookkeeper.proto; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.Channel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.LinkedBlockingQueue; - -import org.apache.log4j.Logger; - -/** - * This class handles communication with clients using NIO. There is one Cnxn - * per client, but only one thread doing the communication. - */ -public class NIOServerFactory extends Thread { - - public interface PacketProcessor { - public void processPacket(ByteBuffer packet, Cnxn src); - } - - ServerStats stats = new ServerStats(); - - Logger LOG = Logger.getLogger(NIOServerFactory.class); - - ServerSocketChannel ss; - - Selector selector = Selector.open(); - - /** - * We use this buffer to do efficient socket I/O. Since there is a single - * sender thread per NIOServerCnxn instance, we can use a member variable to - * only allocate it once. - */ - ByteBuffer directBuffer = ByteBuffer.allocateDirect(64 * 1024); - - HashSet cnxns = new HashSet(); - - int outstandingLimit = 2000; - - PacketProcessor processor; - - long minLatency = 99999999; - - public NIOServerFactory(int port, PacketProcessor processor) throws IOException { - super("NIOServerFactory"); - setDaemon(true); - this.processor = processor; - this.ss = ServerSocketChannel.open(); - ss.socket().bind(new InetSocketAddress(port)); - ss.configureBlocking(false); - ss.register(selector, SelectionKey.OP_ACCEPT); - start(); - } - - public InetSocketAddress getLocalAddress() { - return (InetSocketAddress) ss.socket().getLocalSocketAddress(); - } - - private void addCnxn(Cnxn cnxn) { - synchronized (cnxns) { - cnxns.add(cnxn); - } - } - - @Override - public void run() { - while (!ss.socket().isClosed()) { - try { - selector.select(1000); - Set selected; - synchronized (this) { - selected = selector.selectedKeys(); - } - ArrayList selectedList = new ArrayList(selected); - Collections.shuffle(selectedList); - for (SelectionKey k : selectedList) { - if ((k.readyOps() & SelectionKey.OP_ACCEPT) != 0) { - SocketChannel sc = ((ServerSocketChannel) k.channel()).accept(); - sc.configureBlocking(false); - SelectionKey sk = sc.register(selector, SelectionKey.OP_READ); - Cnxn cnxn = new Cnxn(sc, sk); - sk.attach(cnxn); - addCnxn(cnxn); - } else if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_WRITE)) != 0) { - Cnxn c = (Cnxn) k.attachment(); - c.doIO(k); - } - } - selected.clear(); - } catch (Exception e) { - LOG.warn(e); - } - } - LOG.debug("NIOServerCnxn factory exitedloop."); - clear(); - // System.exit(0); - } - - /** - * clear all the connections in the selector - * - */ - synchronized public void clear() { - selector.wakeup(); - synchronized (cnxns) { - // got to clear all the connections that we have in the selector - for (Iterator it = cnxns.iterator(); it.hasNext();) { - Cnxn cnxn = it.next(); - it.remove(); - try { - cnxn.close(); - } catch (Exception e) { - // Do nothing. - } - } - } - - } - - public void shutdown() { - try { - ss.close(); - clear(); - this.interrupt(); - this.join(); - } catch (InterruptedException e) { - LOG.warn("Interrupted", e); - } catch (Exception e) { - LOG.error("Unexpected exception", e); - } - } - - /** - * The buffer will cause the connection to be close when we do a send. - */ - static final ByteBuffer closeConn = ByteBuffer.allocate(0); - - public class Cnxn { - - private SocketChannel sock; - - private SelectionKey sk; - - boolean initialized; - - ByteBuffer lenBuffer = ByteBuffer.allocate(4); - - ByteBuffer incomingBuffer = lenBuffer; - - LinkedBlockingQueue outgoingBuffers = new LinkedBlockingQueue(); - - int sessionTimeout; - - int packetsSent; - - int packetsReceived; - - void doIO(SelectionKey k) throws InterruptedException { - try { - if (sock == null) { - return; - } - if (k.isReadable()) { - int rc = sock.read(incomingBuffer); - if (rc < 0) { - throw new IOException("Read error"); - } - if (incomingBuffer.remaining() == 0) { - incomingBuffer.flip(); - if (incomingBuffer == lenBuffer) { - readLength(k); - } else { - cnxnStats.packetsReceived++; - stats.incrementPacketsReceived(); - try { - readRequest(); - } finally { - lenBuffer.clear(); - incomingBuffer = lenBuffer; - } - } - } - } - if (k.isWritable()) { - if (outgoingBuffers.size() > 0) { - // ZooLog.logTraceMessage(LOG, - // ZooLog.CLIENT_DATA_PACKET_TRACE_MASK, - // "sk " + k + " is valid: " + - // k.isValid()); - - /* - * This is going to reset the buffer position to 0 and - * the limit to the size of the buffer, so that we can - * fill it with data from the non-direct buffers that we - * need to send. - */ - directBuffer.clear(); - - for (ByteBuffer b : outgoingBuffers) { - if (directBuffer.remaining() < b.remaining()) { - /* - * When we call put later, if the directBuffer - * is to small to hold everything, nothing will - * be copied, so we've got to slice the buffer - * if it's too big. - */ - b = (ByteBuffer) b.slice().limit(directBuffer.remaining()); - } - /* - * put() is going to modify the positions of both - * buffers, put we don't want to change the position - * of the source buffers (we'll do that after the - * send, if needed), so we save and reset the - * position after the copy - */ - int p = b.position(); - directBuffer.put(b); - b.position(p); - if (directBuffer.remaining() == 0) { - break; - } - } - /* - * Do the flip: limit becomes position, position gets - * set to 0. This sets us up for the write. - */ - directBuffer.flip(); - - int sent = sock.write(directBuffer); - ByteBuffer bb; - - // Remove the buffers that we have sent - while (outgoingBuffers.size() > 0) { - bb = outgoingBuffers.peek(); - if (bb == closeConn) { - throw new IOException("closing"); - } - int left = bb.remaining() - sent; - if (left > 0) { - /* - * We only partially sent this buffer, so we - * update the position and exit the loop. - */ - bb.position(bb.position() + sent); - break; - } - cnxnStats.packetsSent++; - /* We've sent the whole buffer, so drop the buffer */ - sent -= bb.remaining(); - ServerStats.getInstance().incrementPacketsSent(); - outgoingBuffers.remove(); - } - // ZooLog.logTraceMessage(LOG, - // ZooLog.CLIENT_DATA_PACKET_TRACE_MASK, "after send, - // outgoingBuffers.size() = " + outgoingBuffers.size()); - } - synchronized (this) { - if (outgoingBuffers.size() == 0) { - if (!initialized && (sk.interestOps() & SelectionKey.OP_READ) == 0) { - throw new IOException("Responded to info probe"); - } - sk.interestOps(sk.interestOps() & (~SelectionKey.OP_WRITE)); - } else { - sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE); - } - } - } - } catch (CancelledKeyException e) { - close(); - } catch (IOException e) { - // LOG.error("FIXMSG",e); - close(); - } - } - - private void readRequest() throws IOException { - incomingBuffer = incomingBuffer.slice(); - processor.processPacket(incomingBuffer, this); - } - - public void disableRecv() { - sk.interestOps(sk.interestOps() & (~SelectionKey.OP_READ)); - } - - public void enableRecv() { - if (sk.isValid()) { - int interest = sk.interestOps(); - if ((interest & SelectionKey.OP_READ) == 0) { - sk.interestOps(interest | SelectionKey.OP_READ); - } - } - } - - private void readLength(SelectionKey k) throws IOException { - // Read the length, now get the buffer - int len = lenBuffer.getInt(); - if (len < 0 || len > 0xfffff) { - throw new IOException("Len error " + len); - } - incomingBuffer = ByteBuffer.allocate(len); - } - - /** - * The number of requests that have been submitted but not yet responded - * to. - */ - int outstandingRequests; - - /* - * (non-Javadoc) - * - * @see org.apache.zookeeper.server.ServerCnxnIface#getSessionTimeout() - */ - public int getSessionTimeout() { - return sessionTimeout; - } - - String peerName; - - public Cnxn(SocketChannel sock, SelectionKey sk) throws IOException { - this.sock = sock; - this.sk = sk; - sock.socket().setTcpNoDelay(true); - sock.socket().setSoLinger(true, 2); - sk.interestOps(SelectionKey.OP_READ); - if (LOG.isTraceEnabled()) { - peerName = sock.socket().toString(); - } - - lenBuffer.clear(); - incomingBuffer = lenBuffer; - } - - @Override - public String toString() { - return "NIOServerCnxn object with sock = " + sock + " and sk = " + sk; - } - - boolean closed; - - /* - * (non-Javadoc) - * - * @see org.apache.zookeeper.server.ServerCnxnIface#close() - */ - public void close() { - if (closed) { - return; - } - closed = true; - synchronized (cnxns) { - cnxns.remove(this); - } - LOG.debug("close NIOServerCnxn: " + sock); - try { - /* - * The following sequence of code is stupid! You would think - * that only sock.close() is needed, but alas, it doesn't work - * that way. If you just do sock.close() there are cases where - * the socket doesn't actually close... - */ - sock.socket().shutdownOutput(); - } catch (IOException e) { - // This is a relatively common exception that we can't avoid - } - try { - sock.socket().shutdownInput(); - } catch (IOException e) { - } - try { - sock.socket().close(); - } catch (IOException e) { - LOG.error("FIXMSG", e); - } - try { - sock.close(); - // XXX The next line doesn't seem to be needed, but some posts - // to forums suggest that it is needed. Keep in mind if errors - // in - // this section arise. - // factory.selector.wakeup(); - } catch (IOException e) { - LOG.error("FIXMSG", e); - } - sock = null; - if (sk != null) { - try { - // need to cancel this selection key from the selector - sk.cancel(); - } catch (Exception e) { - } - } - } - - private void makeWritable(SelectionKey sk) { - try { - selector.wakeup(); - if (sk.isValid()) { - sk.interestOps(sk.interestOps() | SelectionKey.OP_WRITE); - } - } catch (RuntimeException e) { - LOG.error("Problem setting writable", e); - throw e; - } - } - - private void sendBuffers(ByteBuffer bb[]) { - ByteBuffer len = ByteBuffer.allocate(4); - int total = 0; - for (int i = 0; i < bb.length; i++) { - if (bb[i] != null) { - total += bb[i].remaining(); - } - } - if (LOG.isTraceEnabled()) { - LOG.debug("Sending response of size " + total + " to " + peerName); - } - len.putInt(total); - len.flip(); - outgoingBuffers.add(len); - for (int i = 0; i < bb.length; i++) { - if (bb[i] != null) { - outgoingBuffers.add(bb[i]); - } - } - makeWritable(sk); - } - - synchronized public void sendResponse(ByteBuffer bb[]) { - if (closed) { - return; - } - sendBuffers(bb); - synchronized (NIOServerFactory.this) { - outstandingRequests--; - // check throttling - if (outstandingRequests < outstandingLimit) { - sk.selector().wakeup(); - enableRecv(); - } - } - } - - public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) sock.socket().getRemoteSocketAddress(); - } - - private class CnxnStats { - long packetsReceived; - - long packetsSent; - - /** - * The number of requests that have been submitted but not yet - * responded to. - */ - public long getOutstandingRequests() { - return outstandingRequests; - } - - public long getPacketsReceived() { - return packetsReceived; - } - - public long getPacketsSent() { - return packetsSent; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - Channel channel = sk.channel(); - if (channel instanceof SocketChannel) { - sb.append(" ").append(((SocketChannel) channel).socket().getRemoteSocketAddress()).append("[") - .append(Integer.toHexString(sk.interestOps())).append("](queued=").append( - getOutstandingRequests()).append(",recved=").append(getPacketsReceived()).append( - ",sent=").append(getPacketsSent()).append(")\n"); - } - return sb.toString(); - } - } - - private CnxnStats cnxnStats = new CnxnStats(); - - public CnxnStats getStats() { - return cnxnStats; - } - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java deleted file mode 100644 index 14879b67f62..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java +++ /dev/null @@ -1,573 +0,0 @@ -package org.apache.bookkeeper.proto; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayDeque; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback; -import org.apache.bookkeeper.util.OrderedSafeExecutor; -import org.apache.bookkeeper.util.SafeRunnable; -import org.apache.log4j.Logger; -import org.jboss.netty.bootstrap.ClientBootstrap; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFactory; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.ChannelPipelineCoverage; -import org.jboss.netty.channel.ChannelPipelineFactory; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.Channels; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.jboss.netty.channel.SimpleChannelHandler; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.handler.codec.frame.CorruptedFrameException; -import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; -import org.jboss.netty.handler.codec.frame.TooLongFrameException; - -/** - * This class manages all details of connection to a particular bookie. It also - * has reconnect logic if a connection to a bookie fails. - * - */ - -@ChannelPipelineCoverage("one") -public class PerChannelBookieClient extends SimpleChannelHandler implements ChannelPipelineFactory { - - static final Logger LOG = Logger.getLogger(PerChannelBookieClient.class); - - static final long maxMemory = Runtime.getRuntime().maxMemory() / 5; - public static int MAX_FRAME_LENGTH = 2 * 1024 * 1024; // 2M - - InetSocketAddress addr; - boolean connected = false; - Semaphore opCounterSem = new Semaphore(2000); - AtomicLong totalBytesOutstanding; - ClientSocketChannelFactory channelFactory; - OrderedSafeExecutor executor; - - ConcurrentHashMap addCompletions = new ConcurrentHashMap(); - ConcurrentHashMap readCompletions = new ConcurrentHashMap(); - - /** - * The following member variables do not need to be concurrent, or volatile - * because they are always updated under a lock - */ - Queue> pendingOps = new ArrayDeque>(); - boolean connectionAttemptInProgress; - Channel channel = null; - - public PerChannelBookieClient(OrderedSafeExecutor executor, ClientSocketChannelFactory channelFactory, - InetSocketAddress addr, AtomicLong totalBytesOutstanding) { - this.addr = addr; - this.executor = executor; - this.totalBytesOutstanding = totalBytesOutstanding; - this.channelFactory = channelFactory; - connect(channelFactory); - } - - void connect(ChannelFactory channelFactory) { - - if (LOG.isDebugEnabled()) - LOG.debug("Connecting to bookie: " + addr); - - // Set up the ClientBootStrap so we can create a new Channel connection - // to the bookie. - ClientBootstrap bootstrap = new ClientBootstrap(channelFactory); - bootstrap.setPipelineFactory(this); - bootstrap.setOption("tcpNoDelay", true); - bootstrap.setOption("keepAlive", true); - - // Start the connection attempt to the input server host. - connectionAttemptInProgress = true; - - ChannelFuture future = bootstrap.connect(addr); - - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - int rc; - Queue> oldPendingOps; - - synchronized (PerChannelBookieClient.this) { - - if (future.isSuccess()) { - LOG.info("Successfully connected to bookie: " + addr); - rc = BKException.Code.OK; - channel = future.getChannel(); - connected = true; - } else { - LOG.error("Could not connect to bookie: " + addr); - rc = BKException.Code.BookieHandleNotAvailableException; - channel = null; - connected = false; - } - - connectionAttemptInProgress = false; - PerChannelBookieClient.this.channel = channel; - - // trick to not do operations under the lock, take the list - // of pending ops and assign it to a new variable, while - // emptying the pending ops by just assigning it to a new - // list - oldPendingOps = pendingOps; - pendingOps = new ArrayDeque>(); - } - - for (GenericCallback pendingOp : oldPendingOps) { - pendingOp.operationComplete(rc, null); - } - - } - }); - } - - void connectIfNeededAndDoOp(GenericCallback op) { - boolean doOpNow; - - // common case without lock first - if (channel != null && connected) { - doOpNow = true; - } else { - - synchronized (this) { - // check again under lock - if (channel != null && connected) { - doOpNow = true; - } else { - - // if reached here, channel is either null (first connection - // attempt), - // or the channel is disconnected - doOpNow = false; - - // connection attempt is still in progress, queue up this - // op. Op will be executed when connection attempt either - // fails - // or - // succeeds - pendingOps.add(op); - - if (!connectionAttemptInProgress) { - connect(channelFactory); - } - - } - } - } - - if (doOpNow) { - op.operationComplete(BKException.Code.OK, null); - } - - } - - /** - * This method should be called only after connection has been checked for - * {@link #connectIfNeededAndDoOp(GenericCallback)} - * - * @param ledgerId - * @param masterKey - * @param entryId - * @param lastConfirmed - * @param macCode - * @param data - * @param cb - * @param ctx - */ - void addEntry(final long ledgerId, byte[] masterKey, final long entryId, ChannelBuffer toSend, WriteCallback cb, - Object ctx) { - - final int entrySize = toSend.readableBytes(); - - // if (totalBytesOutstanding.get() > maxMemory) { - // // TODO: how to throttle, throw an exception, or call the callback? - // // Maybe this should be done at the layer above? - // } - - final CompletionKey completionKey = new CompletionKey(ledgerId, entryId); - - addCompletions.put(completionKey, new AddCompletion(cb, entrySize, ctx)); - - int totalHeaderSize = 4 // for the length of the packet - + 4 // for the type of request - + masterKey.length; // for the master key - - ChannelBuffer header = channel.getConfig().getBufferFactory().getBuffer(totalHeaderSize); - header.writeInt(totalHeaderSize - 4 + entrySize); - header.writeInt(BookieProtocol.ADDENTRY); - header.writeBytes(masterKey); - - ChannelBuffer wrappedBuffer = ChannelBuffers.wrappedBuffer(header, toSend); - - ChannelFuture future = channel.write(wrappedBuffer); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (future.isSuccess()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Successfully wrote request for adding entry: " + entryId + " ledger-id: " + ledgerId - + " bookie: " + channel.getRemoteAddress() + " entry length: " + entrySize); - } - // totalBytesOutstanding.addAndGet(entrySize); - } else { - errorOutAddKey(completionKey); - } - } - }); - - } - - public void readEntry(final long ledgerId, final long entryId, ReadEntryCallback cb, Object ctx) { - - final CompletionKey key = new CompletionKey(ledgerId, entryId); - readCompletions.put(key, new ReadCompletion(cb, ctx)); - - int totalHeaderSize = 4 // for the length of the packet - + 4 // for request type - + 8 // for ledgerId - + 8; // for entryId - - ChannelBuffer tmpEntry = channel.getConfig().getBufferFactory().getBuffer(totalHeaderSize); - tmpEntry.writeInt(totalHeaderSize - 4); - tmpEntry.writeInt(BookieProtocol.READENTRY); - tmpEntry.writeLong(ledgerId); - tmpEntry.writeLong(entryId); - - ChannelFuture future = channel.write(tmpEntry); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (future.isSuccess()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Successfully wrote request for reading entry: " + entryId + " ledger-id: " - + ledgerId + " bookie: " + channel.getRemoteAddress()); - } - } else { - errorOutReadKey(key); - } - } - }); - - } - - public void close() { - if (channel != null) { - channel.close(); - } - } - - void errorOutReadKey(final CompletionKey key) { - executor.submitOrdered(key.ledgerId, new SafeRunnable() { - @Override - public void safeRun() { - - ReadCompletion readCompletion = readCompletions.remove(key); - - if (readCompletion != null) { - LOG.error("Could not write request for reading entry: " + key.entryId + " ledger-id: " - + key.ledgerId + " bookie: " + channel.getRemoteAddress()); - - readCompletion.cb.readEntryComplete(BKException.Code.BookieHandleNotAvailableException, - key.ledgerId, key.entryId, null, readCompletion.ctx); - } - } - - }); - } - - void errorOutAddKey(final CompletionKey key) { - executor.submitOrdered(key.ledgerId, new SafeRunnable() { - @Override - public void safeRun() { - - AddCompletion addCompletion = addCompletions.remove(key); - - if (addCompletion != null) { - String bAddress = "null"; - if(channel != null) - bAddress = channel.getRemoteAddress().toString(); - LOG.error("Could not write request for adding entry: " + key.entryId + " ledger-id: " - + key.ledgerId + " bookie: " + bAddress); - - addCompletion.cb.writeComplete(BKException.Code.BookieHandleNotAvailableException, key.ledgerId, - key.entryId, addr, addCompletion.ctx); - LOG.error("Invoked callback method: " + key.entryId); - } - } - - }); - - } - - /** - * Errors out pending entries. We call this method from one thread to avoid - * concurrent executions to QuorumOpMonitor (implements callbacks). It seems - * simpler to call it from BookieHandle instead of calling directly from - * here. - */ - - void errorOutOutstandingEntries() { - - // DO NOT rewrite these using Map.Entry iterations. We want to iterate - // on keys and see if we are successfully able to remove the key from - // the map. Because the add and the read methods also do the same thing - // in case they get a write failure on the socket. The one who - // successfully removes the key from the map is the one responsible for - // calling the application callback. - - for (CompletionKey key : addCompletions.keySet()) { - errorOutAddKey(key); - } - - for (CompletionKey key : readCompletions.keySet()) { - errorOutReadKey(key); - } - } - - /** - * In the netty pipeline, we need to split packets based on length, so we - * use the {@link LengthFieldBasedFrameDecoder}. Other than that all actions - * are carried out in this class, e.g., making sense of received messages, - * prepending the length to outgoing packets etc. - */ - @Override - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - pipeline.addLast("lengthbasedframedecoder", new LengthFieldBasedFrameDecoder(MAX_FRAME_LENGTH, 0, 4, 0, 4)); - pipeline.addLast("mainhandler", this); - return pipeline; - } - - /** - * If our channel has disconnected, we just error out the pending entries - */ - @Override - public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - LOG.info("Disconnected from bookie: " + addr); - errorOutOutstandingEntries(); - channel.close(); - - connected = false; - - // we don't want to reconnect right away. If someone sends a request to - // this address, we will reconnect. - } - - /** - * Called by netty when an exception happens in one of the netty threads - * (mostly due to what we do in the netty threads) - */ - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { - Throwable t = e.getCause(); - if (t instanceof CorruptedFrameException || t instanceof TooLongFrameException) { - LOG.error("Corrupted fram recieved from bookie: " + e.getChannel().getRemoteAddress()); - return; - } - if (t instanceof IOException) { - // these are thrown when a bookie fails, logging them just pollutes - // the logs (the failure is logged from the listeners on the write - // operation), so I'll just ignore it here. - return; - } - - LOG.fatal("Unexpected exception caught by bookie client channel handler", t); - // Since we are a library, cant terminate App here, can we? - } - - /** - * Called by netty when a message is received on a channel - */ - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - if (!(e.getMessage() instanceof ChannelBuffer)) { - ctx.sendUpstream(e); - return; - } - - final ChannelBuffer buffer = (ChannelBuffer) e.getMessage(); - final int type, rc; - final long ledgerId, entryId; - - try { - type = buffer.readInt(); - rc = buffer.readInt(); - ledgerId = buffer.readLong(); - entryId = buffer.readLong(); - } catch (IndexOutOfBoundsException ex) { - LOG.error("Unparseable response from bookie: " + addr, ex); - return; - } - - executor.submitOrdered(ledgerId, new SafeRunnable() { - @Override - public void safeRun() { - switch (type) { - case BookieProtocol.ADDENTRY: - handleAddResponse(ledgerId, entryId, rc); - break; - case BookieProtocol.READENTRY: - handleReadResponse(ledgerId, entryId, rc, buffer); - break; - default: - LOG.error("Unexpected response, type: " + type + " recieved from bookie: " + addr + " , ignoring"); - } - } - - }); - } - - void handleAddResponse(long ledgerId, long entryId, int rc) { - if (LOG.isDebugEnabled()) { - LOG.debug("Got response for add request from bookie: " + addr + " for ledger: " + ledgerId + " entry: " - + entryId + " rc: " + rc); - } - - // convert to BKException code because thats what the uppper - // layers expect. This is UGLY, there should just be one set of - // error codes. - if (rc != BookieProtocol.EOK) { - LOG.error("Add for ledger: " + ledgerId + ", entry: " + entryId + " failed on bookie: " + addr - + " with code: " + rc); - rc = BKException.Code.WriteException; - } else { - rc = BKException.Code.OK; - } - - AddCompletion ac; - ac = addCompletions.remove(new CompletionKey(ledgerId, entryId)); - if (ac == null) { - LOG.error("Unexpected add response received from bookie: " + addr + " for ledger: " + ledgerId - + ", entry: " + entryId + " , ignoring"); - return; - } - - // totalBytesOutstanding.addAndGet(-ac.size); - - ac.cb.writeComplete(rc, ledgerId, entryId, addr, ac.ctx); - - } - - void handleReadResponse(long ledgerId, long entryId, int rc, ChannelBuffer buffer) { - if (LOG.isDebugEnabled()) { - LOG.debug("Got response for read request from bookie: " + addr + " for ledger: " + ledgerId + " entry: " - + entryId + " rc: " + rc + "entry length: " + buffer.readableBytes()); - } - - // convert to BKException code because thats what the uppper - // layers expect. This is UGLY, there should just be one set of - // error codes. - if (rc == BookieProtocol.EOK) { - rc = BKException.Code.OK; - } else if (rc == BookieProtocol.ENOENTRY || rc == BookieProtocol.ENOLEDGER) { - rc = BKException.Code.NoSuchEntryException; - } else { - LOG.error("Read for ledger: " + ledgerId + ", entry: " + entryId + " failed on bookie: " + addr - + " with code: " + rc); - rc = BKException.Code.ReadException; - } - - CompletionKey key = new CompletionKey(ledgerId, entryId); - ReadCompletion readCompletion = readCompletions.remove(key); - - if (readCompletion == null) { - /* - * This is a special case. When recovering a ledger, a client - * submits a read request with id -1, and receives a response with a - * different entry id. - */ - readCompletion = readCompletions.remove(new CompletionKey(ledgerId, -1)); - } - - if (readCompletion == null) { - LOG.error("Unexpected read response recieved from bookie: " + addr + " for ledger: " + ledgerId - + ", entry: " + entryId + " , ignoring"); - return; - } - - readCompletion.cb.readEntryComplete(rc, ledgerId, entryId, buffer.slice(), readCompletion.ctx); - } - - /** - * Boiler-plate wrapper classes follow - * - */ - - private static class ReadCompletion { - final ReadEntryCallback cb; - final Object ctx; - - public ReadCompletion(ReadEntryCallback cb, Object ctx) { - this.cb = cb; - this.ctx = ctx; - } - } - - private static class AddCompletion { - final WriteCallback cb; - //final long size; - final Object ctx; - - public AddCompletion(WriteCallback cb, long size, Object ctx) { - this.cb = cb; - //this.size = size; - this.ctx = ctx; - } - } - - private static class CompletionKey { - long ledgerId; - long entryId; - - CompletionKey(long ledgerId, long entryId) { - this.ledgerId = ledgerId; - this.entryId = entryId; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CompletionKey) || obj == null) { - return false; - } - CompletionKey that = (CompletionKey) obj; - return this.ledgerId == that.ledgerId && this.entryId == that.entryId; - } - - @Override - public int hashCode() { - return ((int) ledgerId << 16) ^ ((int) entryId); - } - - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/ServerStats.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/ServerStats.java deleted file mode 100644 index 65c051fc038..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/proto/ServerStats.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.bookkeeper.proto; - -public class ServerStats { - private static ServerStats instance = new ServerStats(); - private long packetsSent; - private long packetsReceived; - private long maxLatency; - private long minLatency = Long.MAX_VALUE; - private long totalLatency = 0; - private long count = 0; - - public interface Provider { - public long getOutstandingRequests(); - - public long getLastProcessedZxid(); - } - - private Provider provider = null; - private Object mutex = new Object(); - - static public ServerStats getInstance() { - return instance; - } - - static public void registerAsConcrete() { - setInstance(new ServerStats()); - } - - static synchronized public void unregister() { - instance = null; - } - - static synchronized protected void setInstance(ServerStats newInstance) { - assert instance == null; - instance = newInstance; - } - - protected ServerStats() { - } - - // getters - synchronized public long getMinLatency() { - return (minLatency == Long.MAX_VALUE) ? 0 : minLatency; - } - - synchronized public long getAvgLatency() { - if (count != 0) - return totalLatency / count; - return 0; - } - - synchronized public long getMaxLatency() { - return maxLatency; - } - - public long getOutstandingRequests() { - synchronized (mutex) { - return (provider != null) ? provider.getOutstandingRequests() : -1; - } - } - - public long getLastProcessedZxid() { - synchronized (mutex) { - return (provider != null) ? provider.getLastProcessedZxid() : -1; - } - } - - synchronized public long getPacketsReceived() { - return packetsReceived; - } - - synchronized public long getPacketsSent() { - return packetsSent; - } - - public String getServerState() { - return "standalone"; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Latency min/avg/max: " + getMinLatency() + "/" + getAvgLatency() + "/" + getMaxLatency() + "\n"); - sb.append("Received: " + getPacketsReceived() + "\n"); - sb.append("Sent: " + getPacketsSent() + "\n"); - if (provider != null) { - sb.append("Outstanding: " + getOutstandingRequests() + "\n"); - sb.append("Zxid: 0x" + Long.toHexString(getLastProcessedZxid()) + "\n"); - } - sb.append("Mode: " + getServerState() + "\n"); - return sb.toString(); - } - - // mutators - public void setStatsProvider(Provider zk) { - synchronized (mutex) { - provider = zk; - } - } - - synchronized void updateLatency(long requestCreateTime) { - long latency = System.currentTimeMillis() - requestCreateTime; - totalLatency += latency; - count++; - if (latency < minLatency) { - minLatency = latency; - } - if (latency > maxLatency) { - maxLatency = latency; - } - } - - synchronized public void resetLatency() { - totalLatency = count = maxLatency = 0; - minLatency = Long.MAX_VALUE; - } - - synchronized public void resetMaxLatency() { - maxLatency = getMinLatency(); - } - - synchronized public void incrementPacketsReceived() { - packetsReceived++; - } - - synchronized public void incrementPacketsSent() { - packetsSent++; - } - - synchronized public void resetRequestCounters() { - packetsReceived = packetsSent = 0; - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerInputStream.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerInputStream.java deleted file mode 100644 index 5566aa29afc..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerInputStream.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.bookkeeper.streaming; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.util.Enumeration; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.log4j.Logger; - -public class LedgerInputStream extends InputStream { - Logger LOG = Logger.getLogger(LedgerInputStream.class); - private LedgerHandle lh; - private ByteBuffer bytebuff; - byte[] bbytes; - long lastEntry = 0; - int increment = 50; - int defaultSize = 1024 * 1024; // 1MB default size - Enumeration ledgerSeq = null; - - /** - * construct a outputstream from a ledger handle - * - * @param lh - * ledger handle - * @throws {@link BKException}, {@link InterruptedException} - */ - public LedgerInputStream(LedgerHandle lh) throws BKException, InterruptedException { - this.lh = lh; - bbytes = new byte[defaultSize]; - this.bytebuff = ByteBuffer.wrap(bbytes); - this.bytebuff.position(this.bytebuff.limit()); - lastEntry = Math.min(lh.getLastAddConfirmed(), increment); - ledgerSeq = lh.readEntries(0, lastEntry); - } - - /** - * construct a outputstream from a ledger handle - * - * @param lh - * the ledger handle - * @param size - * the size of the buffer - * @throws {@link BKException}, {@link InterruptedException} - */ - public LedgerInputStream(LedgerHandle lh, int size) throws BKException, InterruptedException { - this.lh = lh; - bbytes = new byte[size]; - this.bytebuff = ByteBuffer.wrap(bbytes); - this.bytebuff.position(this.bytebuff.limit()); - lastEntry = Math.min(lh.getLastAddConfirmed(), increment); - ledgerSeq = lh.readEntries(0, lastEntry); - } - - /** - * Method close currently doesn't do anything. The application - * is supposed to open and close the ledger handle backing up - * a stream ({@link LedgerHandle}). - */ - @Override - public void close() { - // do nothing - // let the application - // close the ledger - } - - /** - * refill the buffer, we need to read more bytes - * - * @return if we can refill or not - */ - private synchronized boolean refill() throws IOException { - bytebuff.clear(); - if (!ledgerSeq.hasMoreElements() && lastEntry >= lh.getLastAddConfirmed()) { - return false; - } - if (!ledgerSeq.hasMoreElements()) { - // do refill - long last = Math.min(lastEntry + increment, lh.getLastAddConfirmed()); - try { - ledgerSeq = lh.readEntries(lastEntry + 1, last); - } catch (BKException bk) { - IOException ie = new IOException(bk.getMessage()); - ie.initCause(bk); - throw ie; - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - lastEntry = last; - } - LedgerEntry le = ledgerSeq.nextElement(); - bbytes = le.getEntry(); - bytebuff = ByteBuffer.wrap(bbytes); - return true; - } - - @Override - public synchronized int read() throws IOException { - boolean toread = true; - if (bytebuff.remaining() == 0) { - // their are no remaining bytes - toread = refill(); - } - if (toread) { - int ret = 0xFF & bytebuff.get(); - return ret; - } - return -1; - } - - @Override - public synchronized int read(byte[] b) throws IOException { - // be smart ... just copy the bytes - // once and return the size - // user will call it again - boolean toread = true; - if (bytebuff.remaining() == 0) { - toread = refill(); - } - if (toread) { - int bcopied = bytebuff.remaining(); - int tocopy = Math.min(bcopied, b.length); - // cannot used gets because of - // the underflow/overflow exceptions - System.arraycopy(bbytes, bytebuff.position(), b, 0, tocopy); - bytebuff.position(bytebuff.position() + tocopy); - return tocopy; - } - return -1; - } - - @Override - public synchronized int read(byte[] b, int off, int len) throws IOException { - // again dont need ot fully - // fill b, just return - // what we have and let the application call read - // again - boolean toread = true; - if (bytebuff.remaining() == 0) { - toread = refill(); - } - if (toread) { - int bcopied = bytebuff.remaining(); - int tocopy = Math.min(bcopied, len); - System.arraycopy(bbytes, bytebuff.position(), b, off, tocopy); - bytebuff.position(bytebuff.position() + tocopy); - return tocopy; - } - return -1; - } -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerOutputStream.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerOutputStream.java deleted file mode 100644 index 8938d2abc75..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/streaming/LedgerOutputStream.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ -package org.apache.bookkeeper.streaming; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.log4j.Logger; - -/** - * this class provides a streaming api to get an output stream from a ledger - * handle and write to it as a stream of bytes. This is built on top of - * ledgerhandle api and uses a buffer to cache the data written to it and writes - * out the entry to the ledger. - */ -public class LedgerOutputStream extends OutputStream { - Logger LOG = Logger.getLogger(LedgerOutputStream.class); - private LedgerHandle lh; - private ByteBuffer bytebuff; - byte[] bbytes; - int defaultSize = 1024 * 1024; // 1MB default size - - /** - * construct a outputstream from a ledger handle - * - * @param lh - * ledger handle - */ - public LedgerOutputStream(LedgerHandle lh) { - this.lh = lh; - bbytes = new byte[defaultSize]; - this.bytebuff = ByteBuffer.wrap(bbytes); - } - - /** - * construct a outputstream from a ledger handle - * - * @param lh - * the ledger handle - * @param size - * the size of the buffer - */ - public LedgerOutputStream(LedgerHandle lh, int size) { - this.lh = lh; - bbytes = new byte[size]; - this.bytebuff = ByteBuffer.wrap(bbytes); - } - - @Override - public void close() { - // flush everything - // we have - flush(); - } - - @Override - public synchronized void flush() { - // lets flush all the data - // into the ledger entry - if (bytebuff.position() > 0) { - // copy the bytes into - // a new byte buffer and send it out - byte[] b = new byte[bytebuff.position()]; - LOG.info("Comment: flushing with params " + " " + bytebuff.position()); - System.arraycopy(bbytes, 0, b, 0, bytebuff.position()); - try { - lh.addEntry(b); - } catch (InterruptedException ie) { - LOG.warn("Interrupted while flusing " + ie); - Thread.currentThread().interrupt(); - } catch (BKException bke) { - LOG.warn("BookKeeper exception ", bke); - } - } - } - - /** - * make space for len bytes to be written to the buffer. - * - * @param len - * @return if true then we can make space for len if false we cannot - */ - private boolean makeSpace(int len) { - if (bytebuff.remaining() < len) { - flush(); - bytebuff.clear(); - if (bytebuff.capacity() < len) { - return false; - } - } - return true; - } - - @Override - public synchronized void write(byte[] b) { - if (makeSpace(b.length)) { - bytebuff.put(b); - } else { - try { - lh.addEntry(b); - } catch (InterruptedException ie) { - LOG.warn("Interrupted while writing", ie); - Thread.currentThread().interrupt(); - } catch (BKException bke) { - LOG.warn("BookKeeper exception", bke); - } - } - } - - @Override - public synchronized void write(byte[] b, int off, int len) { - if (!makeSpace(len)) { - // lets try making the buffer bigger - bbytes = new byte[len]; - bytebuff = ByteBuffer.wrap(bbytes); - } - bytebuff.put(b, off, len); - } - - @Override - public synchronized void write(int b) throws IOException { - makeSpace(1); - byte oneB = (byte) (b & 0xFF); - bytebuff.put(oneB); - } -} \ No newline at end of file diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/tools/BookKeeperTools.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/tools/BookKeeperTools.java deleted file mode 100644 index a24d2aed76a..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/tools/BookKeeperTools.java +++ /dev/null @@ -1,762 +0,0 @@ -package org.apache.bookkeeper.tools; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.AsyncCallback.OpenCallback; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.AsyncCallback.RecoverCallback; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.log4j.Logger; -import org.apache.zookeeper.AsyncCallback; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.data.Stat; -import org.jboss.netty.buffer.ChannelBuffer; - -/** - * Provides Admin Tools to manage the BookKeeper cluster. - * - */ -public class BookKeeperTools { - - private static Logger LOG = Logger.getLogger(BookKeeperTools.class); - - // ZK client instance - private ZooKeeper zk; - // ZK ledgers related String constants - static final String LEDGERS_PATH = "/ledgers"; - static final String LEDGER_NODE_PREFIX = "L"; - static final String AVAILABLE_NODE = "available"; - static final String BOOKIES_PATH = LEDGERS_PATH + "/" + AVAILABLE_NODE; - static final String COLON = ":"; - - // BookKeeper client instance - private BookKeeper bkc; - - /* - * Random number generator used to choose an available bookie server to - * replicate data from a dead bookie. - */ - private Random rand = new Random(); - - /* - * For now, assume that all ledgers were created with the same DigestType - * and password. In the future, this admin tool will need to know for each - * ledger, what was the DigestType and password used to create it before it - * can open it. These values will come from System properties, though hard - * coded defaults are defined here. - */ - private DigestType DIGEST_TYPE = DigestType.valueOf(System.getProperty("digestType", DigestType.CRC32.toString())); - private byte[] PASSWD = System.getProperty("passwd", "").getBytes(); - - /** - * Constructor that takes in a ZooKeeper servers connect string so we know - * how to connect to ZooKeeper to retrieve information about the BookKeeper - * cluster. We need this before we can do any type of admin operations on - * the BookKeeper cluster. - * - * @param zkServers - * Comma separated list of hostname:port pairs for the ZooKeeper - * servers cluster. - * @throws IOException - * Throws this exception if there is an error instantiating the - * ZooKeeper client. - * @throws InterruptedException - * Throws this exception if there is an error instantiating the - * BookKeeper client. - * @throws KeeperException - * Throws this exception if there is an error instantiating the - * BookKeeper client. - */ - public BookKeeperTools(String zkServers) throws IOException, InterruptedException, KeeperException { - // Create the ZooKeeper client instance - zk = new ZooKeeper(zkServers, 10000, new Watcher() { - @Override - public void process(WatchedEvent event) { - if (LOG.isDebugEnabled()) { - LOG.debug("Process: " + event.getType() + " " + event.getPath()); - } - } - }); - // Create the BookKeeper client instance - bkc = new BookKeeper(zk); - } - - /** - * Shutdown method to gracefully release resources that this class uses. - * - * @throws InterruptedException - * if there is an error shutting down the clients that this - * class uses. - */ - public void shutdown() throws InterruptedException { - bkc.halt(); - zk.close(); - } - - /** - * This is a multi callback object for bookie recovery that waits for all of - * the multiple async operations to complete. If any fail, then we invoke - * the final callback with a BK LedgerRecoveryException. - */ - class MultiCallback implements AsyncCallback.VoidCallback { - // Number of expected callbacks - final int expected; - // Final callback and the corresponding context to invoke - final AsyncCallback.VoidCallback cb; - final Object context; - // This keeps track of how many operations have completed - final AtomicInteger done = new AtomicInteger(); - // List of the exceptions from operations that completed unsuccessfully - final LinkedBlockingQueue exceptions = new LinkedBlockingQueue(); - - MultiCallback(int expected, AsyncCallback.VoidCallback cb, Object context) { - this.expected = expected; - this.cb = cb; - this.context = context; - if (expected == 0) { - cb.processResult(Code.OK.intValue(), null, context); - } - } - - private void tick() { - if (done.incrementAndGet() == expected) { - if (exceptions.isEmpty()) { - cb.processResult(Code.OK.intValue(), null, context); - } else { - cb.processResult(BKException.Code.LedgerRecoveryException, null, context); - } - } - } - - @Override - public void processResult(int rc, String path, Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("BK error recovering ledger data", BKException.create(rc)); - exceptions.add(rc); - } - tick(); - } - - } - - /** - * Method to get the input ledger's digest type. For now, this is just a - * placeholder function since there is no way we can get this information - * easily. In the future, BookKeeper should store this ledger metadata - * somewhere such that an admin tool can access it. - * - * @param ledgerId - * LedgerId we are retrieving the digestType for. - * @return DigestType for the input ledger - */ - private DigestType getLedgerDigestType(long ledgerId) { - return DIGEST_TYPE; - } - - /** - * Method to get the input ledger's password. For now, this is just a - * placeholder function since there is no way we can get this information - * easily. In the future, BookKeeper should store this ledger metadata - * somewhere such that an admin tool can access it. - * - * @param ledgerId - * LedgerId we are retrieving the password for. - * @return Password for the input ledger - */ - private byte[] getLedgerPasswd(long ledgerId) { - return PASSWD; - } - - // Object used for calling async methods and waiting for them to complete. - class SyncObject { - boolean value; - - public SyncObject() { - value = false; - } - } - - /** - * Synchronous method to rebuild and recover the ledger fragments data that - * was stored on the source bookie. That bookie could have failed completely - * and now the ledger data that was stored on it is under replicated. An - * optional destination bookie server could be given if we want to copy all - * of the ledger fragments data on the failed source bookie to it. - * Otherwise, we will just randomly distribute the ledger fragments to the - * active set of bookies, perhaps based on load. All ZooKeeper ledger - * metadata will be updated to point to the new bookie(s) that contain the - * replicated ledger fragments. - * - * @param bookieSrc - * Source bookie that had a failure. We want to replicate the - * ledger fragments that were stored there. - * @param bookieDest - * Optional destination bookie that if passed, we will copy all - * of the ledger fragments from the source bookie over to it. - */ - public void recoverBookieData(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest) - throws InterruptedException { - SyncObject sync = new SyncObject(); - // Call the async method to recover bookie data. - asyncRecoverBookieData(bookieSrc, bookieDest, new RecoverCallback() { - @Override - public void recoverComplete(int rc, Object ctx) { - LOG.info("Recover bookie operation completed with rc: " + rc); - SyncObject syncObj = (SyncObject) ctx; - synchronized (syncObj) { - syncObj.value = true; - syncObj.notify(); - } - } - }, sync); - - // Wait for the async method to complete. - synchronized (sync) { - while (sync.value == false) { - sync.wait(); - } - } - } - - /** - * Async method to rebuild and recover the ledger fragments data that was - * stored on the source bookie. That bookie could have failed completely and - * now the ledger data that was stored on it is under replicated. An - * optional destination bookie server could be given if we want to copy all - * of the ledger fragments data on the failed source bookie to it. - * Otherwise, we will just randomly distribute the ledger fragments to the - * active set of bookies, perhaps based on load. All ZooKeeper ledger - * metadata will be updated to point to the new bookie(s) that contain the - * replicated ledger fragments. - * - * @param bookieSrc - * Source bookie that had a failure. We want to replicate the - * ledger fragments that were stored there. - * @param bookieDest - * Optional destination bookie that if passed, we will copy all - * of the ledger fragments from the source bookie over to it. - * @param cb - * RecoverCallback to invoke once all of the data on the dead - * bookie has been recovered and replicated. - * @param context - * Context for the RecoverCallback to call. - */ - public void asyncRecoverBookieData(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest, - final RecoverCallback cb, final Object context) { - // Sync ZK to make sure we're reading the latest bookie/ledger data. - zk.sync(LEDGERS_PATH, new AsyncCallback.VoidCallback() { - @Override - public void processResult(int rc, String path, Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("ZK error syncing: ", KeeperException.create(KeeperException.Code.get(rc), path)); - cb.recoverComplete(BKException.Code.ZKException, context); - return; - } - getAvailableBookies(bookieSrc, bookieDest, cb, context); - }; - }, null); - } - - /** - * This method asynchronously gets the set of available Bookies that the - * dead input bookie's data will be copied over into. If the user passed in - * a specific destination bookie, then just use that one. Otherwise, we'll - * randomly pick one of the other available bookies to use for each ledger - * fragment we are replicating. - * - * @param bookieSrc - * Source bookie that had a failure. We want to replicate the - * ledger fragments that were stored there. - * @param bookieDest - * Optional destination bookie that if passed, we will copy all - * of the ledger fragments from the source bookie over to it. - * @param cb - * RecoverCallback to invoke once all of the data on the dead - * bookie has been recovered and replicated. - * @param context - * Context for the RecoverCallback to call. - */ - private void getAvailableBookies(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest, - final RecoverCallback cb, final Object context) { - final List availableBookies = new LinkedList(); - if (bookieDest != null) { - availableBookies.add(bookieDest); - // Now poll ZK to get the active ledgers - getActiveLedgers(bookieSrc, bookieDest, cb, context, availableBookies); - } else { - zk.getChildren(BOOKIES_PATH, null, new AsyncCallback.ChildrenCallback() { - @Override - public void processResult(int rc, String path, Object ctx, List children) { - if (rc != Code.OK.intValue()) { - LOG.error("ZK error getting bookie nodes: ", KeeperException.create(KeeperException.Code - .get(rc), path)); - cb.recoverComplete(BKException.Code.ZKException, context); - return; - } - for (String bookieNode : children) { - String parts[] = bookieNode.split(COLON); - if (parts.length < 2) { - LOG.error("Bookie Node retrieved from ZK has invalid name format: " + bookieNode); - cb.recoverComplete(BKException.Code.ZKException, context); - return; - } - availableBookies.add(new InetSocketAddress(parts[0], Integer.parseInt(parts[1]))); - } - // Now poll ZK to get the active ledgers - getActiveLedgers(bookieSrc, bookieDest, cb, context, availableBookies); - } - }, null); - } - } - - /** - * This method asynchronously polls ZK to get the current set of active - * ledgers. From this, we can open each ledger and look at the metadata to - * determine if any of the ledger fragments for it were stored at the dead - * input bookie. - * - * @param bookieSrc - * Source bookie that had a failure. We want to replicate the - * ledger fragments that were stored there. - * @param bookieDest - * Optional destination bookie that if passed, we will copy all - * of the ledger fragments from the source bookie over to it. - * @param cb - * RecoverCallback to invoke once all of the data on the dead - * bookie has been recovered and replicated. - * @param context - * Context for the RecoverCallback to call. - * @param availableBookies - * List of Bookie Servers that are available to use for - * replicating data on the failed bookie. This could contain a - * single bookie server if the user explicitly chose a bookie - * server to replicate data to. - */ - private void getActiveLedgers(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest, - final RecoverCallback cb, final Object context, final List availableBookies) { - zk.getChildren(LEDGERS_PATH, null, new AsyncCallback.ChildrenCallback() { - @Override - public void processResult(int rc, String path, Object ctx, List children) { - if (rc != Code.OK.intValue()) { - LOG.error("ZK error getting ledger nodes: ", KeeperException.create(KeeperException.Code.get(rc), - path)); - cb.recoverComplete(BKException.Code.ZKException, context); - return; - } - // Wrapper class around the RecoverCallback so it can be used - // as the final VoidCallback to invoke within the MultiCallback. - class RecoverCallbackWrapper implements AsyncCallback.VoidCallback { - final RecoverCallback cb; - - RecoverCallbackWrapper(RecoverCallback cb) { - this.cb = cb; - } - - @Override - public void processResult(int rc, String path, Object ctx) { - cb.recoverComplete(rc, ctx); - } - } - // Recover each of the ledgers asynchronously - MultiCallback ledgerMcb = new MultiCallback(children.size(), new RecoverCallbackWrapper(cb), context); - for (final String ledgerNode : children) { - recoverLedger(bookieSrc, ledgerNode, ledgerMcb, availableBookies); - } - } - }, null); - } - - /** - * This method asynchronously recovers a given ledger if any of the ledger - * entries were stored on the failed bookie. - * - * @param bookieSrc - * Source bookie that had a failure. We want to replicate the - * ledger fragments that were stored there. - * @param ledgerNode - * Ledger Node name as retrieved from ZooKeeper we want to - * recover. - * @param ledgerMcb - * MultiCallback to invoke once we've recovered the current - * ledger. - * @param availableBookies - * List of Bookie Servers that are available to use for - * replicating data on the failed bookie. This could contain a - * single bookie server if the user explicitly chose a bookie - * server to replicate data to. - */ - private void recoverLedger(final InetSocketAddress bookieSrc, final String ledgerNode, - final MultiCallback ledgerMcb, final List availableBookies) { - /* - * The available node is also stored in this path so ignore that. That - * node is the path for the set of available Bookie Servers. - */ - if (ledgerNode.equals(AVAILABLE_NODE)) { - ledgerMcb.processResult(BKException.Code.OK, null, null); - return; - } - // Parse out the ledgerId from the ZK ledger node. - String parts[] = ledgerNode.split(LEDGER_NODE_PREFIX); - if (parts.length < 2) { - LOG.error("Ledger Node retrieved from ZK has invalid name format: " + ledgerNode); - ledgerMcb.processResult(BKException.Code.ZKException, null, null); - return; - } - final long lId; - try { - lId = Long.parseLong(parts[parts.length - 1]); - } catch (NumberFormatException e) { - LOG.error("Error retrieving ledgerId from ledgerNode: " + ledgerNode, e); - ledgerMcb.processResult(BKException.Code.ZKException, null, null); - return; - } - /* - * For the current ledger, open it to retrieve the LedgerHandle. This - * will contain the LedgerMetadata indicating which bookie servers the - * ledger fragments are stored on. Check if any of the ledger fragments - * for the current ledger are stored on the input dead bookie. - */ - DigestType digestType = getLedgerDigestType(lId); - byte[] passwd = getLedgerPasswd(lId); - bkc.asyncOpenLedger(lId, digestType, passwd, new OpenCallback() { - @Override - public void openComplete(int rc, final LedgerHandle lh, Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("BK error opening ledger: " + lId, BKException.create(rc)); - ledgerMcb.processResult(rc, null, null); - return; - } - /* - * This List stores the ledger fragments to recover indexed by - * the start entry ID for the range. The ensembles TreeMap is - * keyed off this. - */ - final List ledgerFragmentsToRecover = new LinkedList(); - /* - * This Map will store the start and end entry ID values for - * each of the ledger fragment ranges. The only exception is the - * current active fragment since it has no end yet. In the event - * of a bookie failure, a new ensemble is created so the current - * ensemble should not contain the dead bookie we are trying to - * recover. - */ - Map ledgerFragmentsRange = new HashMap(); - Long curEntryId = null; - for (Map.Entry> entry : lh.getLedgerMetadata().getEnsembles() - .entrySet()) { - if (curEntryId != null) - ledgerFragmentsRange.put(curEntryId, entry.getKey() - 1); - curEntryId = entry.getKey(); - if (entry.getValue().contains(bookieSrc)) { - /* - * Current ledger fragment has entries stored on the - * dead bookie so we'll need to recover them. - */ - ledgerFragmentsToRecover.add(entry.getKey()); - } - } - /* - * See if this current ledger contains any ledger fragment that - * needs to be re-replicated. If not, then just invoke the - * multiCallback and return. - */ - if (ledgerFragmentsToRecover.size() == 0) { - ledgerMcb.processResult(BKException.Code.OK, null, null); - return; - } - /* - * We have ledger fragments that need to be re-replicated to a - * new bookie. Choose one randomly from the available set of - * bookies. - */ - final InetSocketAddress newBookie = availableBookies.get(rand.nextInt(availableBookies.size())); - - /* - * Wrapper class around the ledger MultiCallback. Once all - * ledger fragments for the ledger have been replicated to a new - * bookie, we need to update ZK with this new metadata to point - * to the new bookie instead of the old dead one. That should be - * done at the end prior to invoking the ledger MultiCallback. - */ - class LedgerMultiCallbackWrapper implements AsyncCallback.VoidCallback { - final MultiCallback ledgerMcb; - - LedgerMultiCallbackWrapper(MultiCallback ledgerMcb) { - this.ledgerMcb = ledgerMcb; - } - - @Override - public void processResult(int rc, String path, Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("BK error replicating ledger fragments for ledger: " + lId, BKException - .create(rc)); - ledgerMcb.processResult(rc, null, null); - return; - } - /* - * Update the ledger metadata's ensemble info to point - * to the new bookie. - */ - for (final Long startEntryId : ledgerFragmentsToRecover) { - ArrayList ensemble = lh.getLedgerMetadata().getEnsembles().get( - startEntryId); - int deadBookieIndex = ensemble.indexOf(bookieSrc); - ensemble.remove(deadBookieIndex); - ensemble.add(deadBookieIndex, newBookie); - } - lh.writeLedgerConfig(new AsyncCallback.StatCallback() { - @Override - public void processResult(int rc, String path, Object ctx, Stat stat) { - if (rc != Code.OK.intValue()) { - LOG.error("ZK error updating ledger config metadata for ledgerId: " + lh.getId(), - KeeperException.create(KeeperException.Code.get(rc), path)); - } else { - LOG.info("Updated ZK for ledgerId: (" + lh.getId() - + ") to point ledger fragments from old dead bookie: (" + bookieSrc - + ") to new bookie: (" + newBookie + ")"); - } - /* - * Pass the return code result up the chain with - * the parent callback. - */ - ledgerMcb.processResult(rc, null, null); - } - }, null); - } - } - - /* - * Now recover all of the necessary ledger fragments - * asynchronously using a MultiCallback for every fragment. - */ - MultiCallback ledgerFragmentMcb = new MultiCallback(ledgerFragmentsToRecover.size(), - new LedgerMultiCallbackWrapper(ledgerMcb), null); - for (final Long startEntryId : ledgerFragmentsToRecover) { - Long endEntryId = ledgerFragmentsRange.get(startEntryId); - try { - recoverLedgerFragment(bookieSrc, lh, startEntryId, endEntryId, ledgerFragmentMcb, newBookie); - } catch(InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - } - } - }, null); - } - - /** - * This method asynchronously recovers a ledger fragment which is a - * contiguous portion of a ledger that was stored in an ensemble that - * included the failed bookie. - * - * @param bookieSrc - * Source bookie that had a failure. We want to replicate the - * ledger fragments that were stored there. - * @param lh - * LedgerHandle for the ledger - * @param startEntryId - * Start entry Id for the ledger fragment - * @param endEntryId - * End entry Id for the ledger fragment - * @param ledgerFragmentMcb - * MultiCallback to invoke once we've recovered the current - * ledger fragment. - * @param newBookie - * New bookie we want to use to recover and replicate the ledger - * entries that were stored on the failed bookie. - */ - private void recoverLedgerFragment(final InetSocketAddress bookieSrc, final LedgerHandle lh, - final Long startEntryId, final Long endEntryId, final MultiCallback ledgerFragmentMcb, - final InetSocketAddress newBookie) throws InterruptedException { - if (endEntryId == null) { - /* - * Ideally this should never happen if bookie failure is taken care - * of properly. Nothing we can do though in this case. - */ - LOG.warn("Dead bookie (" + bookieSrc + ") is still part of the current active ensemble for ledgerId: " - + lh.getId()); - ledgerFragmentMcb.processResult(BKException.Code.OK, null, null); - return; - } - - ArrayList curEnsemble = lh.getLedgerMetadata().getEnsembles().get(startEntryId); - int bookieIndex = 0; - for (int i = 0; i < curEnsemble.size(); i++) { - if (curEnsemble.get(i).equals(bookieSrc)) { - bookieIndex = i; - break; - } - } - /* - * Loop through all entries in the current ledger fragment range and - * find the ones that were stored on the dead bookie. - */ - List entriesToReplicate = new LinkedList(); - for (long i = startEntryId; i <= endEntryId; i++) { - if (lh.getDistributionSchedule().getReplicaIndex(i, bookieIndex) >= 0) { - /* - * Current entry is stored on the dead bookie so we'll need to - * read it and replicate it to a new bookie. - */ - entriesToReplicate.add(i); - } - } - /* - * Now asynchronously replicate all of the entries for the ledger - * fragment that were on the dead bookie. - */ - MultiCallback ledgerFragmentEntryMcb = new MultiCallback(entriesToReplicate.size(), ledgerFragmentMcb, null); - for (final Long entryId : entriesToReplicate) { - recoverLedgerFragmentEntry(entryId, lh, ledgerFragmentEntryMcb, newBookie); - } - } - - /** - * This method asynchronously recovers a specific ledger entry by reading - * the values via the BookKeeper Client (which would read it from the other - * replicas) and then writing it to the chosen new bookie. - * - * @param entryId - * Ledger Entry ID to recover. - * @param lh - * LedgerHandle for the ledger - * @param ledgerFragmentEntryMcb - * MultiCallback to invoke once we've recovered the current - * ledger entry. - * @param newBookie - * New bookie we want to use to recover and replicate the ledger - * entries that were stored on the failed bookie. - */ - private void recoverLedgerFragmentEntry(final Long entryId, final LedgerHandle lh, - final MultiCallback ledgerFragmentEntryMcb, final InetSocketAddress newBookie) throws InterruptedException { - /* - * Read the ledger entry using the LedgerHandle. This will allow us to - * read the entry from one of the other replicated bookies other than - * the dead one. - */ - lh.asyncReadEntries(entryId, entryId, new ReadCallback() { - @Override - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("BK error reading ledger entry: " + entryId, BKException.create(rc)); - ledgerFragmentEntryMcb.processResult(rc, null, null); - return; - } - /* - * Now that we've read the ledger entry, write it to the new - * bookie we've selected. - */ - ChannelBuffer toSend = lh.getDigestManager().computeDigestAndPackageForSending(entryId, - lh.getLastAddConfirmed(), seq.nextElement().getEntry()); - bkc.getBookieClient().addEntry(newBookie, lh.getId(), lh.getLedgerKey(), entryId, toSend, - new WriteCallback() { - @Override - public void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, - Object ctx) { - if (rc != Code.OK.intValue()) { - LOG.error("BK error writing entry for ledgerId: " + ledgerId + ", entryId: " - + entryId + ", bookie: " + addr, BKException.create(rc)); - } else { - LOG.debug("Success writing ledger entry to a new bookie!"); - } - /* - * Pass the return code result up the chain with - * the parent callback. - */ - ledgerFragmentEntryMcb.processResult(rc, null, null); - } - }, null); - } - }, null); - } - - /** - * Main method so we can invoke the bookie recovery via command line. - * - * @param args - * Arguments to BookKeeperTools. 2 are required and the third is - * optional. The first is a comma separated list of ZK server - * host:port pairs. The second is the host:port socket address - * for the bookie we are trying to recover. The third is the - * host:port socket address of the optional destination bookie - * server we want to replicate the data over to. - * @throws InterruptedException - * @throws IOException - * @throws KeeperException - */ - public static void main(String[] args) throws InterruptedException, IOException, KeeperException { - // Validate the inputs - if (args.length < 2) { - System.err.println("USAGE: BookKeeperTools zkServers bookieSrc [bookieDest]"); - return; - } - // Parse out the input arguments - String zkServers = args[0]; - String bookieSrcString[] = args[1].split(COLON); - if (bookieSrcString.length < 2) { - System.err.println("BookieSrc inputted has invalid name format (host:port expected): " + bookieSrcString); - return; - } - final InetSocketAddress bookieSrc = new InetSocketAddress(bookieSrcString[0], Integer - .parseInt(bookieSrcString[1])); - InetSocketAddress bookieDest = null; - if (args.length < 3) { - String bookieDestString[] = args[2].split(COLON); - if (bookieDestString.length < 2) { - System.err.println("BookieDest inputted has invalid name format (host:port expected): " - + bookieDestString); - return; - } - bookieDest = new InetSocketAddress(bookieDestString[0], Integer.parseInt(bookieDestString[1])); - } - - // Create the BookKeeperTools instance and perform the bookie recovery - // synchronously. - BookKeeperTools bkTools = new BookKeeperTools(zkServers); - bkTools.recoverBookieData(bookieSrc, bookieDest); - - // Shutdown the resources used in the BookKeeperTools instance. - bkTools.shutdown(); - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/LocalBookKeeper.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/LocalBookKeeper.java deleted file mode 100644 index 5d8ecfc0997..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/LocalBookKeeper.java +++ /dev/null @@ -1,209 +0,0 @@ -package org.apache.bookkeeper.util; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; - -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.log4j.PatternLayout; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.server.NIOServerCnxnFactory; -import org.apache.zookeeper.server.ZooKeeperServer; - -public class LocalBookKeeper { - protected static final Logger LOG = Logger.getLogger(LocalBookKeeper.class); - public static final int CONNECTION_TIMEOUT = 30000; - - ConsoleAppender ca; - int numberOfBookies; - - public LocalBookKeeper() { - ca = new ConsoleAppender(new PatternLayout()); - LOG.addAppender(ca); - LOG.setLevel(Level.INFO); - numberOfBookies = 3; - } - - public LocalBookKeeper(int numberOfBookies){ - this(); - this.numberOfBookies = numberOfBookies; - LOG.info("Running " + this.numberOfBookies + " bookie(s)."); - } - - private final String HOSTPORT = "127.0.0.1:2181"; - NIOServerCnxnFactory serverFactory; - ZooKeeperServer zks; - ZooKeeper zkc; - int ZooKeeperDefaultPort = 2181; - File ZkTmpDir; - - //BookKeeper variables - File tmpDirs[]; - BookieServer bs[]; - Integer initialPort = 5000; - - /** - * @param args - */ - - private void runZookeeper(int maxCC) throws IOException{ - // create a ZooKeeper server(dataDir, dataLogDir, port) - LOG.info("Starting ZK server"); - //ServerStats.registerAsConcrete(); - //ClientBase.setupTestEnv(); - ZkTmpDir = File.createTempFile("zookeeper", "test"); - ZkTmpDir.delete(); - ZkTmpDir.mkdir(); - - try { - zks = new ZooKeeperServer(ZkTmpDir, ZkTmpDir, ZooKeeperDefaultPort); - serverFactory = new NIOServerCnxnFactory(); - serverFactory.configure(new InetSocketAddress(ZooKeeperDefaultPort), maxCC); - serverFactory.startup(zks); - } catch (Exception e) { - // TODO Auto-generated catch block - LOG.fatal("Exception while instantiating ZooKeeper", e); - } - - boolean b = waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT); - LOG.debug("ZooKeeper server up: " + b); - } - - private void initializeZookeper(){ - LOG.info("Instantiate ZK Client"); - //initialize the zk client with values - try { - zkc = new ZooKeeper("127.0.0.1", ZooKeeperDefaultPort, new emptyWatcher()); - zkc.create("/ledgers", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - zkc.create("/ledgers/available", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - // No need to create an entry for each requested bookie anymore as the - // BookieServers will register themselves with ZooKeeper on startup. - } catch (KeeperException e) { - // TODO Auto-generated catch block - LOG.fatal("Exception while creating znodes", e); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - LOG.fatal("Interrupted while creating znodes", e); - } catch (IOException e) { - // TODO Auto-generated catch block - LOG.fatal("Exception while creating znodes", e); - } - } - private void runBookies() throws IOException{ - LOG.info("Starting Bookie(s)"); - // Create Bookie Servers (B1, B2, B3) - - tmpDirs = new File[numberOfBookies]; - bs = new BookieServer[numberOfBookies]; - - for(int i = 0; i < numberOfBookies; i++){ - tmpDirs[i] = File.createTempFile("bookie" + Integer.toString(i), "test"); - tmpDirs[i].delete(); - tmpDirs[i].mkdir(); - - bs[i] = new BookieServer(initialPort + i, InetAddress.getLocalHost().getHostAddress() + ":" - + ZooKeeperDefaultPort, tmpDirs[i], new File[]{tmpDirs[i]}); - bs[i].start(); - } - } - - public static void main(String[] args) throws IOException, InterruptedException { - if(args.length < 1){ - usage(); - System.exit(-1); - } - LocalBookKeeper lb = new LocalBookKeeper(Integer.parseInt(args[0])); - lb.runZookeeper(1000); - lb.initializeZookeper(); - lb.runBookies(); - while (true){ - Thread.sleep(5000); - } - } - - private static void usage() { - System.err.println("Usage: LocalBookKeeper number-of-bookies"); - } - - /* User for testing purposes, void */ - class emptyWatcher implements Watcher{ - public void process(WatchedEvent event) {} - } - - public static boolean waitForServerUp(String hp, long timeout) { - long start = System.currentTimeMillis(); - String split[] = hp.split(":"); - String host = split[0]; - int port = Integer.parseInt(split[1]); - while (true) { - try { - Socket sock = new Socket(host, port); - BufferedReader reader = null; - try { - OutputStream outstream = sock.getOutputStream(); - outstream.write("stat".getBytes()); - outstream.flush(); - - reader = - new BufferedReader( - new InputStreamReader(sock.getInputStream())); - String line = reader.readLine(); - if (line != null && line.startsWith("Zookeeper version:")) { - LOG.info("Server UP"); - return true; - } - } finally { - sock.close(); - if (reader != null) { - reader.close(); - } - } - } catch (IOException e) { - // ignore as this is expected - LOG.info("server " + hp + " not up " + e); - } - - if (System.currentTimeMillis() > start + timeout) { - break; - } - try { - Thread.sleep(250); - } catch (InterruptedException e) { - // ignore - } - } - return false; - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/Main.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/Main.java deleted file mode 100644 index 27335411f4d..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/Main.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.apache.bookkeeper.util; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.IOException; - -import org.apache.bookkeeper.proto.BookieClient; -import org.apache.bookkeeper.proto.BookieServer; - -public class Main { - - static void usage() { - System.err.println("USAGE: bookeeper client|bookie"); - } - - /** - * @param args - * @throws InterruptedException - * @throws IOException - */ - public static void main(String[] args) throws IOException, InterruptedException { - if (args.length < 1 || !(args[0].equals("client") || args[0].equals("bookie"))) { - usage(); - return; - } - String newArgs[] = new String[args.length - 1]; - System.arraycopy(args, 1, newArgs, 0, newArgs.length); - if (args[0].equals("bookie")) { - BookieServer.main(newArgs); - } else { - BookieClient.main(newArgs); - } - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/MathUtils.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/MathUtils.java deleted file mode 100644 index 69fad814e46..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/MathUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.apache.bookkeeper.util; - - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Provides misc math functions that dont come standard - */ -public class MathUtils { - - public static int signSafeMod(long dividend, int divisor){ - int mod = (int) (dividend % divisor); - - if (mod < 0){ - mod += divisor; - } - - return mod; - - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java deleted file mode 100644 index 349dc8a8555..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/OrderedSafeExecutor.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.apache.bookkeeper.util; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -/** - * This class provides 2 things over the java {@link ScheduledExecutorService}. - * - * 1. It takes {@link SafeRunnable objects} instead of plain Runnable objects. - * This means that exceptions in scheduled tasks wont go unnoticed and will be - * logged. - * - * 2. It supports submitting tasks with an ordering key, so that tasks submitted - * with the same key will always be executed in order, but tasks across - * different keys can be unordered. This retains parallelism while retaining the - * basic amount of ordering we want (e.g. , per ledger handle). Ordering is - * achieved by hashing the key objects to threads by their {@link #hashCode()} - * method. - * - */ -public class OrderedSafeExecutor { - ExecutorService threads[]; - Random rand = new Random(); - - public OrderedSafeExecutor(int numThreads) { - if (numThreads <= 0) { - throw new IllegalArgumentException(); - } - - threads = new ExecutorService[numThreads]; - for (int i = 0; i < numThreads; i++) { - threads[i] = Executors.newSingleThreadExecutor(); - } - } - - ExecutorService chooseThread() { - // skip random # generation in this special case - if (threads.length == 1) { - return threads[0]; - } - - return threads[rand.nextInt(threads.length)]; - - } - - ExecutorService chooseThread(Object orderingKey) { - // skip hashcode generation in this special case - if (threads.length == 1) { - return threads[0]; - } - - return threads[MathUtils.signSafeMod(orderingKey.hashCode(), threads.length)]; - - } - - /** - * schedules a one time action to execute - */ - public void submit(SafeRunnable r) { - chooseThread().submit(r); - } - - /** - * schedules a one time action to execute with an ordering guarantee on the key - * @param orderingKey - * @param r - */ - public void submitOrdered(Object orderingKey, SafeRunnable r) { - chooseThread(orderingKey).submit(r); - } - - public void shutdown() { - for (int i = 0; i < threads.length; i++) { - threads[i].shutdown(); - } - } - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/SafeRunnable.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/SafeRunnable.java deleted file mode 100644 index 5a7648505d1..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/SafeRunnable.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.apache.bookkeeper.util; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.apache.log4j.Logger; - -public abstract class SafeRunnable implements Runnable{ - - static final Logger logger = Logger.getLogger(SafeRunnable.class); - -@Override - public void run() { - try{ - safeRun(); - }catch(Throwable t){ - logger.fatal("Unexpected throwable caught ", t); - } - } - - public abstract void safeRun(); - -} diff --git a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/StringUtils.java b/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/StringUtils.java deleted file mode 100644 index f457cc88109..00000000000 --- a/src/contrib/bookkeeper/src/java/org/apache/bookkeeper/util/StringUtils.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.apache.bookkeeper.util; - -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.IOException; -import java.net.InetSocketAddress; - -/** - * Provided utilites for parsing network addresses, ledger-id from node paths - * etc. - * - */ -public class StringUtils { - - /* - * Path to ledger metadata. ZooKeeper appends a sequence number to L. - */ - static public final String prefix = "/ledgers/L"; - - /** - * Parses address into IP and port. - * - * @param addr - * String - */ - - public static InetSocketAddress parseAddr(String s) throws IOException { - - String parts[] = s.split(":"); - if (parts.length != 2) { - throw new IOException(s + " does not have the form host:port"); - } - int port; - try { - port = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - throw new IOException(s + " does not have the form host:port"); - } - - InetSocketAddress addr = new InetSocketAddress(parts[0], port); - return addr; - } - - public static StringBuilder addrToString(StringBuilder sb, InetSocketAddress addr) { - return sb.append(addr.getAddress().getHostAddress()).append(":").append(addr.getPort()); - } - - /** - * Formats ledger ID according to ZooKeeper rules - * - * @param id - * znode id - */ - public static String getZKStringId(long id) { - return String.format("%010d", id); - } - - /** - * Get the path for the ledger metadata node - * - * @return - */ - public static String getLedgerNodePath(long ledgerId) { - return prefix + StringUtils.getZKStringId(ledgerId); - } - - public static long getLedgerId(String nodeName) throws IOException { - long ledgerId; - try { - String parts[] = nodeName.split(prefix); - ledgerId = Long.parseLong(parts[parts.length - 1]); - } catch (NumberFormatException e) { - throw new IOException(e); - } - return ledgerId; - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/AsyncLedgerOpsTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/AsyncLedgerOpsTest.java deleted file mode 100644 index 49bf4fb534b..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/AsyncLedgerOpsTest.java +++ /dev/null @@ -1,256 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Random; -import java.util.Set; - -import org.apache.bookkeeper.client.AsyncCallback.AddCallback; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.AsyncCallback.CloseCallback; -import org.apache.bookkeeper.client.AsyncCallback.CreateCallback; -import org.apache.bookkeeper.client.AsyncCallback.OpenCallback; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.log4j.Logger; -import org.junit.Before; -import org.junit.Test; -import org.junit.runners.Parameterized.Parameters; - -/** - * This test tests read and write, synchronous and asynchronous, strings and - * integers for a BookKeeper client. The test deployment uses a ZooKeeper server - * and three BookKeepers. - * - */ -public class AsyncLedgerOpsTest extends BaseTestCase implements AddCallback, ReadCallback, CreateCallback, - CloseCallback, OpenCallback { - static Logger LOG = Logger.getLogger(BookieClientTest.class); - - DigestType digestType; - - public AsyncLedgerOpsTest(DigestType digestType) { - super(3); - this.digestType = digestType; - } - - @Parameters - public static Collection configs(){ - return Arrays.asList(new Object[][]{ {DigestType.MAC }, {DigestType.CRC32}}); - } - - - byte[] ledgerPassword = "aaa".getBytes(); - LedgerHandle lh, lh2; - long ledgerId; - Enumeration ls; - - // test related variables - int numEntriesToWrite = 20; - int maxInt = 2147483647; - Random rng; // Random Number Generator - ArrayList entries; // generated entries - ArrayList entriesSize; - - // Synchronization - SyncObj sync; - Set syncObjs; - - class SyncObj { - int counter; - boolean value; - - public SyncObj() { - counter = 0; - value = false; - } - } - - class ControlObj { - LedgerHandle lh; - - void setLh(LedgerHandle lh) { - this.lh = lh; - } - - LedgerHandle getLh() { - return lh; - } - } - - @Test - public void testAsyncCreateClose() throws IOException { - try { - - ControlObj ctx = new ControlObj(); - - synchronized (ctx) { - LOG.info("Going to create ledger asynchronously"); - bkc.asyncCreateLedger(3, 2, digestType, ledgerPassword, this, ctx); - - ctx.wait(); - } - - // bkc.initMessageDigest("SHA1"); - LedgerHandle lh = ctx.getLh(); - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - - entries.add(entry.array()); - entriesSize.add(entry.array().length); - lh.asyncAddEntry(entry.array(), this, sync); - } - - // wait for all entries to be acknowledged - synchronized (sync) { - while (sync.counter < numEntriesToWrite) { - LOG.debug("Entries counter = " + sync.counter); - sync.wait(); - } - } - - LOG.debug("*** WRITE COMPLETE ***"); - // close ledger - synchronized (ctx) { - lh.asyncClose(this, ctx); - ctx.wait(); - } - - // *** WRITING PART COMPLETE // READ PART BEGINS *** - - // open ledger - synchronized (ctx) { - bkc.asyncOpenLedger(ledgerId, digestType, ledgerPassword, this, ctx); - ctx.wait(); - } - lh = ctx.getLh(); - - LOG.debug("Number of entries written: " + lh.getLastAddConfirmed()); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == (numEntriesToWrite - 1)); - - // read entries - lh.asyncReadEntries(0, numEntriesToWrite - 1, this, sync); - - synchronized (sync) { - while (sync.value == false) { - sync.wait(); - } - } - - LOG.debug("*** READ COMPLETE ***"); - - // at this point, LedgerSequence ls is filled with the returned - // values - int i = 0; - while (ls.hasMoreElements()) { - ByteBuffer origbb = ByteBuffer.wrap(entries.get(i)); - Integer origEntry = origbb.getInt(); - byte[] entry = ls.nextElement().getEntry(); - ByteBuffer result = ByteBuffer.wrap(entry); - LOG.debug("Length of result: " + result.capacity()); - LOG.debug("Original entry: " + origEntry); - - Integer retrEntry = result.getInt(); - LOG.debug("Retrieved entry: " + retrEntry); - assertTrue("Checking entry " + i + " for equality", origEntry.equals(retrEntry)); - assertTrue("Checking entry " + i + " for size", entry.length == entriesSize.get(i).intValue()); - i++; - } - assertTrue("Checking number of read entries", i == numEntriesToWrite); - lh.close(); - } catch (InterruptedException e) { - LOG.error(e); - fail("InterruptedException"); - } // catch (NoSuchAlgorithmException e) { - // e.printStackTrace(); - // } - - } - - public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) { - SyncObj x = (SyncObj) ctx; - synchronized (x) { - x.counter++; - x.notify(); - } - } - - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - ls = seq; - synchronized (sync) { - sync.value = true; - sync.notify(); - } - - } - - public void createComplete(int rc, LedgerHandle lh, Object ctx) { - synchronized (ctx) { - ControlObj cobj = (ControlObj) ctx; - cobj.setLh(lh); - cobj.notify(); - } - } - - public void openComplete(int rc, LedgerHandle lh, Object ctx) { - synchronized (ctx) { - ControlObj cobj = (ControlObj) ctx; - cobj.setLh(lh); - cobj.notify(); - } - } - - public void closeComplete(int rc, LedgerHandle lh, Object ctx) { - synchronized (ctx) { - ControlObj cobj = (ControlObj) ctx; - cobj.notify(); - } - } - - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - rng = new Random(System.currentTimeMillis()); // Initialize the Random - // Number Generator - entries = new ArrayList(); // initialize the entries list - entriesSize = new ArrayList(); - sync = new SyncObj(); // initialize the synchronization data structure - } - - - - - -} \ No newline at end of file diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BaseTestCase.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BaseTestCase.java deleted file mode 100644 index f9f18178319..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BaseTestCase.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.test; - -import java.io.File; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.server.NIOServerCnxnFactory; -import org.apache.zookeeper.server.ZooKeeperServer; -import org.apache.zookeeper.test.ClientBase; -import org.junit.After; -import org.junit.Before; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import junit.framework.TestCase; - -@RunWith(Parameterized.class) -public abstract class BaseTestCase extends TestCase { - static final Logger LOG = Logger.getLogger(BaseTestCase.class); - // ZooKeeper related variables - static final String HOSTPORT = "127.0.0.1:2181"; - static Integer ZooKeeperDefaultPort = 2181; - ZooKeeperServer zks; - ZooKeeper zkc; // zookeeper client - NIOServerCnxnFactory serverFactory; - File ZkTmpDir; - - // BookKeeper - List tmpDirs = new ArrayList(); - List bs = new ArrayList(); - Integer initialPort = 5000; - int numBookies; - BookKeeper bkc; - - public BaseTestCase(int numBookies) { - this.numBookies = numBookies; - } - - @Parameters - public static Collection configs(){ - return Arrays.asList(new Object[][]{ {DigestType.MAC }, {DigestType.CRC32}}); - } - - - @Before - @Override - public void setUp() throws Exception { - try { - // create a ZooKeeper server(dataDir, dataLogDir, port) - LOG.debug("Running ZK server"); - // ServerStats.registerAsConcrete(); - ClientBase.setupTestEnv(); - ZkTmpDir = File.createTempFile("zookeeper", "test"); - ZkTmpDir.delete(); - ZkTmpDir.mkdir(); - - zks = new ZooKeeperServer(ZkTmpDir, ZkTmpDir, ZooKeeperDefaultPort); - serverFactory = new NIOServerCnxnFactory(); - serverFactory.configure(new InetSocketAddress(ZooKeeperDefaultPort), 100); - serverFactory.startup(zks); - - boolean b = ClientBase.waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT); - - LOG.debug("Server up: " + b); - - // create a zookeeper client - LOG.debug("Instantiate ZK Client"); - zkc = new ZooKeeper("127.0.0.1", ZooKeeperDefaultPort, new emptyWatcher()); - - // initialize the zk client with values - zkc.create("/ledgers", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - zkc.create("/ledgers/available", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - - // Create Bookie Servers (B1, B2, B3) - for (int i = 0; i < numBookies; i++) { - File f = File.createTempFile("bookie", "test"); - tmpDirs.add(f); - f.delete(); - f.mkdir(); - - BookieServer server = new BookieServer(initialPort + i, HOSTPORT, f, new File[] { f }); - server.start(); - bs.add(server); - } - zkc.close(); - bkc = new BookKeeper("127.0.0.1"); - } catch(Exception e) { - e.printStackTrace(); - throw e; - } - } - - @After - @Override - public void tearDown() throws Exception { - LOG.info("TearDown"); - - if (bkc != null) { - bkc.halt();; - } - - for (BookieServer server : bs) { - server.shutdown(); - } - - for (File f : tmpDirs) { - cleanUpDir(f); - } - - // shutdown ZK server - if (serverFactory != null) { - serverFactory.shutdown(); - assertTrue("waiting for server down", ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT)); - } - // ServerStats.unregister(); - cleanUpDir(ZkTmpDir); - - - } - - /* Clean up a directory recursively */ - protected boolean cleanUpDir(File dir) { - if (dir.isDirectory()) { - LOG.info("Cleaning up " + dir.getName()); - String[] children = dir.list(); - for (String string : children) { - boolean success = cleanUpDir(new File(dir, string)); - if (!success) - return false; - } - } - // The directory is now empty so delete it - return dir.delete(); - } - - /* User for testing purposes, void */ - class emptyWatcher implements Watcher { - public void process(WatchedEvent event) { - } - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieClientTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieClientTest.java deleted file mode 100644 index e626a4ec7d9..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieClientTest.java +++ /dev/null @@ -1,232 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.File; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.concurrent.Executors; - -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; -import org.junit.Test; -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.proto.BookieClient; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.bookkeeper.util.OrderedSafeExecutor; -import org.apache.log4j.Logger; - -import junit.framework.TestCase; - -public class BookieClientTest extends TestCase { - static Logger LOG = Logger.getLogger(BookieClientTest.class); - BookieServer bs; - File tmpDir; - int port = 13645; - ClientSocketChannelFactory channelFactory; - OrderedSafeExecutor executor; - - @Override - protected void setUp() throws Exception { - tmpDir = File.createTempFile("bookie", "test"); - tmpDir.delete(); - tmpDir.mkdir(); - // Since this test does not rely on the BookKeeper client needing to - // know via ZooKeeper which Bookies are available, okay, so pass in null - // for the zkServers input parameter when constructing the BookieServer. - bs = new BookieServer(port, null, tmpDir, new File[] { tmpDir }); - bs.start(); - channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors - .newCachedThreadPool()); - executor = new OrderedSafeExecutor(2); - } - - @Override - protected void tearDown() throws Exception { - bs.shutdown(); - recursiveDelete(tmpDir); - channelFactory.releaseExternalResources(); - executor.shutdown(); - } - - private static void recursiveDelete(File dir) { - File children[] = dir.listFiles(); - if (children != null) { - for (File child : children) { - recursiveDelete(child); - } - } - dir.delete(); - } - - static class ResultStruct { - int rc; - ByteBuffer entry; - } - - ReadEntryCallback recb = new ReadEntryCallback() { - - public void readEntryComplete(int rc, long ledgerId, long entryId, ChannelBuffer bb, Object ctx) { - ResultStruct rs = (ResultStruct) ctx; - synchronized (rs) { - rs.rc = rc; - if (bb != null) { - bb.readerIndex(16); - rs.entry = bb.toByteBuffer(); - rs.notifyAll(); - } - } - } - - }; - - WriteCallback wrcb = new WriteCallback() { - public void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, Object ctx) { - if (ctx != null) { - synchronized (ctx) { - ctx.notifyAll(); - } - } - } - }; - - @Test - public void testWriteGaps() throws Exception { - final Object notifyObject = new Object(); - byte[] passwd = new byte[20]; - Arrays.fill(passwd, (byte) 'a'); - InetSocketAddress addr = new InetSocketAddress("127.0.0.1", port); - ResultStruct arc = new ResultStruct(); - - BookieClient bc = new BookieClient(channelFactory, executor); - ChannelBuffer bb; - bb = createByteBuffer(1, 1, 1); - bc.addEntry(addr, 1, passwd, 1, bb, wrcb, null); - synchronized (arc) { - bc.readEntry(addr, 1, 1, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(1, arc.entry.getInt()); - } - bb = createByteBuffer(2, 1, 2); - bc.addEntry(addr, 1, passwd, 2, bb, wrcb, null); - bb = createByteBuffer(3, 1, 3); - bc.addEntry(addr, 1, passwd, 3, bb, wrcb, null); - bb = createByteBuffer(5, 1, 5); - bc.addEntry(addr, 1, passwd, 5, bb, wrcb, null); - bb = createByteBuffer(7, 1, 7); - bc.addEntry(addr, 1, passwd, 7, bb, wrcb, null); - synchronized (notifyObject) { - bb = createByteBuffer(11, 1, 11); - bc.addEntry(addr, 1, passwd, 11, bb, wrcb, notifyObject); - notifyObject.wait(); - } - synchronized (arc) { - bc.readEntry(addr, 1, 6, recb, arc); - arc.wait(1000); - assertEquals(BKException.Code.NoSuchEntryException, arc.rc); - } - synchronized (arc) { - bc.readEntry(addr, 1, 7, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(7, arc.entry.getInt()); - } - synchronized (arc) { - bc.readEntry(addr, 1, 1, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(1, arc.entry.getInt()); - } - synchronized (arc) { - bc.readEntry(addr, 1, 2, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(2, arc.entry.getInt()); - } - synchronized (arc) { - bc.readEntry(addr, 1, 3, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(3, arc.entry.getInt()); - } - synchronized (arc) { - bc.readEntry(addr, 1, 4, recb, arc); - arc.wait(1000); - assertEquals(BKException.Code.NoSuchEntryException, arc.rc); - } - synchronized (arc) { - bc.readEntry(addr, 1, 11, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(11, arc.entry.getInt()); - } - synchronized (arc) { - bc.readEntry(addr, 1, 5, recb, arc); - arc.wait(1000); - assertEquals(0, arc.rc); - assertEquals(5, arc.entry.getInt()); - } - synchronized (arc) { - bc.readEntry(addr, 1, 10, recb, arc); - arc.wait(1000); - assertEquals(BKException.Code.NoSuchEntryException, arc.rc); - } - synchronized (arc) { - bc.readEntry(addr, 1, 12, recb, arc); - arc.wait(1000); - assertEquals(BKException.Code.NoSuchEntryException, arc.rc); - } - synchronized (arc) { - bc.readEntry(addr, 1, 13, recb, arc); - arc.wait(1000); - assertEquals(BKException.Code.NoSuchEntryException, arc.rc); - } - } - - private ChannelBuffer createByteBuffer(int i, long lid, long eid) { - ByteBuffer bb; - bb = ByteBuffer.allocate(4 + 16); - bb.putLong(lid); - bb.putLong(eid); - bb.putInt(i); - bb.flip(); - return ChannelBuffers.wrappedBuffer(bb); - } - - @Test - public void testNoLedger() throws Exception { - ResultStruct arc = new ResultStruct(); - InetSocketAddress addr = new InetSocketAddress("127.0.0.1", port); - BookieClient bc = new BookieClient(channelFactory, executor); - synchronized (arc) { - bc.readEntry(addr, 2, 13, recb, arc); - arc.wait(1000); - assertEquals(BKException.Code.NoSuchEntryException, arc.rc); - } - } -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieFailureTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieFailureTest.java deleted file mode 100644 index df61bc84f11..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieFailureTest.java +++ /dev/null @@ -1,305 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Random; -import java.util.Set; - -import org.apache.bookkeeper.client.AsyncCallback.AddCallback; -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.junit.Before; -import org.junit.Test; - -/** - * This test tests read and write, synchronous and asynchronous, strings and - * integers for a BookKeeper client. The test deployment uses a ZooKeeper server - * and three BookKeepers. - * - */ - -public class BookieFailureTest extends BaseTestCase implements AddCallback, ReadCallback { - - // Depending on the taste, select the amount of logging - // by decommenting one of the two lines below - // static Logger LOG = Logger.getRootLogger(); - static Logger LOG = Logger.getLogger(BookieFailureTest.class); - - byte[] ledgerPassword = "aaa".getBytes(); - LedgerHandle lh, lh2; - long ledgerId; - Enumeration ls; - - // test related variables - int numEntriesToWrite = 200; - int maxInt = 2147483647; - Random rng; // Random Number Generator - ArrayList entries; // generated entries - ArrayList entriesSize; - DigestType digestType; - - // Synchronization - SyncObj sync; - Set syncObjs; - - class SyncObj { - int counter; - boolean value; - - public SyncObj() { - counter = 0; - value = false; - } - } - - public BookieFailureTest(DigestType digestType) { - super(4); - this.digestType = digestType; - } - - /** - * Tests writes and reads when a bookie fails. - * - * @throws {@link IOException} - */ - @Test - public void testAsyncBK1() throws IOException { - LOG.info("#### BK1 ####"); - auxTestReadWriteAsyncSingleClient(bs.get(0)); - } - - @Test - public void testAsyncBK2() throws IOException { - LOG.info("#### BK2 ####"); - auxTestReadWriteAsyncSingleClient(bs.get(1)); - } - - @Test - public void testAsyncBK3() throws IOException { - LOG.info("#### BK3 ####"); - auxTestReadWriteAsyncSingleClient(bs.get(2)); - } - - @Test - public void testAsyncBK4() throws IOException { - LOG.info("#### BK4 ####"); - auxTestReadWriteAsyncSingleClient(bs.get(3)); - } - - @Test - public void testBookieRecovery() throws Exception{ - bkc = new BookKeeper("127.0.0.1"); - - //Shutdown all but 1 bookie - bs.get(0).shutdown(); - bs.get(1).shutdown(); - bs.get(2).shutdown(); - - byte[] passwd = "blah".getBytes(); - LedgerHandle lh = bkc.createLedger(1, 1,digestType, passwd); - - int numEntries = 100; - for (int i=0; i< numEntries; i++){ - byte[] data = (""+i).getBytes(); - lh.addEntry(data); - } - - bs.get(3).shutdown(); - BookieServer server = new BookieServer(initialPort + 3, HOSTPORT, tmpDirs.get(3), new File[] { tmpDirs.get(3)}); - server.start(); - bs.set(3, server); - - assertEquals(numEntries - 1 , lh.getLastAddConfirmed()); - Enumeration entries = lh.readEntries(0, lh.getLastAddConfirmed()); - - int numScanned = 0; - while (entries.hasMoreElements()){ - assertEquals((""+numScanned), new String(entries.nextElement().getEntry())); - numScanned++; - } - assertEquals(numEntries, numScanned); - - - } - - void auxTestReadWriteAsyncSingleClient(BookieServer bs) throws IOException { - try { - // Create a BookKeeper client and a ledger - lh = bkc.createLedger(3, 2, digestType, ledgerPassword); - - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - - entries.add(entry.array()); - entriesSize.add(entry.array().length); - lh.asyncAddEntry(entry.array(), this, sync); - - } - - LOG.info("Wrote " + numEntriesToWrite + " and now going to fail bookie."); - // Bookie fail - bs.shutdown(); - - // wait for all entries to be acknowledged - synchronized (sync) { - while (sync.counter < numEntriesToWrite) { - LOG.debug("Entries counter = " + sync.counter); - sync.wait(); - } - } - - LOG.debug("*** WRITE COMPLETE ***"); - // close ledger - lh.close(); - - // *** WRITING PART COMPLETE // READ PART BEGINS *** - - // open ledger - bkc.halt(); - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - LOG.debug("Number of entries written: " + (lh.getLastAddConfirmed() + 1)); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == (numEntriesToWrite - 1)); - - // read entries - - lh.asyncReadEntries(0, numEntriesToWrite - 1, this, sync); - - synchronized (sync) { - while (sync.value == false) { - sync.wait(10000); - assertTrue("Haven't received entries", sync.value); - } - } - - LOG.debug("*** READ COMPLETE ***"); - - // at this point, LedgerSequence ls is filled with the returned - // values - int i = 0; - while (ls.hasMoreElements()) { - ByteBuffer origbb = ByteBuffer.wrap(entries.get(i)); - Integer origEntry = origbb.getInt(); - byte[] entry = ls.nextElement().getEntry(); - ByteBuffer result = ByteBuffer.wrap(entry); - - Integer retrEntry = result.getInt(); - LOG.debug("Retrieved entry: " + i); - assertTrue("Checking entry " + i + " for equality", origEntry.equals(retrEntry)); - assertTrue("Checking entry " + i + " for size", entry.length == entriesSize.get(i).intValue()); - i++; - } - - assertTrue("Checking number of read entries", i == numEntriesToWrite); - - LOG.info("Verified that entries are ok, and now closing ledger"); - lh.close(); - } catch (KeeperException e) { - LOG.error("Caught KeeperException", e); - fail(e.toString()); - } catch (BKException e) { - LOG.error("Caught BKException", e); - fail(e.toString()); - } catch (InterruptedException e) { - LOG.error("Caught InterruptedException", e); - fail(e.toString()); - } - - } - - public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) { - if (rc != 0) - fail("Failed to write entry: " + entryId); - SyncObj x = (SyncObj) ctx; - synchronized (x) { - x.counter++; - x.notify(); - } - } - - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - if (rc != 0) - fail("Failed to write entry"); - ls = seq; - synchronized (sync) { - sync.value = true; - sync.notify(); - } - - } - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - - rng = new Random(System.currentTimeMillis()); // Initialize the Random - // Number Generator - entries = new ArrayList(); // initialize the entries list - entriesSize = new ArrayList(); - sync = new SyncObj(); // initialize the synchronization data structure - - zkc.close(); - } - - - /* Clean up a directory recursively */ - @Override - protected boolean cleanUpDir(File dir) { - if (dir.isDirectory()) { - LOG.info("Cleaning up " + dir.getName()); - String[] children = dir.list(); - for (String string : children) { - boolean success = cleanUpDir(new File(dir, string)); - if (!success) - return false; - } - } - // The directory is now empty so delete it - return dir.delete(); - } - - /* User for testing purposes, void */ - class emptyWatcher implements Watcher { - public void process(WatchedEvent event) { - } - } - -} \ No newline at end of file diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieReadWriteTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieReadWriteTest.java deleted file mode 100644 index 6d4aee808bf..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieReadWriteTest.java +++ /dev/null @@ -1,666 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.File; -import java.io.IOException; -import java.lang.NoSuchFieldException; -import java.lang.IllegalAccessException; -import java.lang.reflect.Field; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.Semaphore; - - -import org.apache.bookkeeper.client.AsyncCallback.AddCallback; -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.AsyncCallback.ReadCallback; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.streaming.LedgerInputStream; -import org.apache.bookkeeper.streaming.LedgerOutputStream; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.junit.Before; -import org.junit.Test; - -/** - * This test tests read and write, synchronous and asynchronous, strings and - * integers for a BookKeeper client. The test deployment uses a ZooKeeper server - * and three BookKeepers. - * - */ - -public class BookieReadWriteTest extends BaseTestCase implements AddCallback, ReadCallback { - - // Depending on the taste, select the amount of logging - // by decommenting one of the two lines below - // static Logger LOG = Logger.getRootLogger(); - static Logger LOG = Logger.getLogger(BookieReadWriteTest.class); - - byte[] ledgerPassword = "aaa".getBytes(); - LedgerHandle lh, lh2; - long ledgerId; - Enumeration ls; - - // test related variables - int numEntriesToWrite = 200; - int maxInt = 2147483647; - Random rng; // Random Number Generator - ArrayList entries; // generated entries - ArrayList entriesSize; - - DigestType digestType; - - public BookieReadWriteTest(DigestType digestType){ - super(3); - this.digestType = digestType; - } - // Synchronization - SyncObj sync; - Set syncObjs; - - class SyncObj { - volatile int counter; - boolean value; - - public SyncObj() { - counter = 0; - value = false; - } - } - - @Test - public void testOpenException() throws KeeperException, IOException, InterruptedException { - try { - lh = bkc.openLedger(0, digestType, ledgerPassword); - fail("Haven't thrown exception"); - } catch (BKException e) { - LOG.warn("Successfully thrown and caught exception:", e); - } - } - - /** - * test the streaming api for reading and writing - * - * @throws {@link IOException}, {@link KeeperException} - */ - @Test - public void testStreamingClients() throws IOException, KeeperException, BKException, InterruptedException { - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - // write a string so that we cna - // create a buffer of a single bytes - // and check for corner cases - String toWrite = "we need to check for this string to match " + "and for the record mahadev is the best"; - LedgerOutputStream lout = new LedgerOutputStream(lh, 1); - byte[] b = toWrite.getBytes(); - lout.write(b); - lout.close(); - long lId = lh.getId(); - lh.close(); - // check for sanity - lh = bkc.openLedger(lId, digestType, ledgerPassword); - LedgerInputStream lin = new LedgerInputStream(lh, 1); - byte[] bread = new byte[b.length]; - int read = 0; - while (read < b.length) { - read = read + lin.read(bread, read, b.length); - } - - String newString = new String(bread); - assertTrue("these two should same", toWrite.equals(newString)); - lin.close(); - lh.close(); - // create another ledger to write one byte at a time - lh = bkc.createLedger(digestType, ledgerPassword); - lout = new LedgerOutputStream(lh); - for (int i = 0; i < b.length; i++) { - lout.write(b[i]); - } - lout.close(); - lId = lh.getId(); - lh.close(); - lh = bkc.openLedger(lId, digestType, ledgerPassword); - lin = new LedgerInputStream(lh); - bread = new byte[b.length]; - read = 0; - while (read < b.length) { - read = read + lin.read(bread, read, b.length); - } - newString = new String(bread); - assertTrue("these two should be same ", toWrite.equals(newString)); - lin.close(); - lh.close(); - } - - @Test - public void testReadWriteAsyncSingleClient() throws IOException { - try { - // Create a BookKeeper client and a ledger - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - // bkc.initMessageDigest("SHA1"); - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - - entries.add(entry.array()); - entriesSize.add(entry.array().length); - lh.asyncAddEntry(entry.array(), this, sync); - } - - // wait for all entries to be acknowledged - synchronized (sync) { - while (sync.counter < numEntriesToWrite) { - LOG.debug("Entries counter = " + sync.counter); - sync.wait(); - } - } - - LOG.debug("*** WRITE COMPLETE ***"); - // close ledger - lh.close(); - - // *** WRITING PART COMPLETE // READ PART BEGINS *** - - // open ledger - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - LOG.debug("Number of entries written: " + (lh.getLastAddConfirmed() + 1)); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == (numEntriesToWrite - 1)); - - // read entries - lh.asyncReadEntries(0, numEntriesToWrite - 1, this, (Object) sync); - - synchronized (sync) { - while (sync.value == false) { - sync.wait(); - } - } - - LOG.debug("*** READ COMPLETE ***"); - - // at this point, LedgerSequence ls is filled with the returned - // values - int i = 0; - while (ls.hasMoreElements()) { - ByteBuffer origbb = ByteBuffer.wrap(entries.get(i)); - Integer origEntry = origbb.getInt(); - byte[] entry = ls.nextElement().getEntry(); - ByteBuffer result = ByteBuffer.wrap(entry); - LOG.debug("Length of result: " + result.capacity()); - LOG.debug("Original entry: " + origEntry); - - Integer retrEntry = result.getInt(); - LOG.debug("Retrieved entry: " + retrEntry); - assertTrue("Checking entry " + i + " for equality", origEntry.equals(retrEntry)); - assertTrue("Checking entry " + i + " for size", entry.length == entriesSize.get(i).intValue()); - i++; - } - assertTrue("Checking number of read entries", i == numEntriesToWrite); - - lh.close(); - } catch (KeeperException e) { - LOG.error("Test failed", e); - fail("Test failed due to ZooKeeper exception"); - } catch (BKException e) { - LOG.error("Test failed", e); - fail("Test failed due to BookKeeper exception"); - } catch (InterruptedException e) { - LOG.error("Test failed", e); - fail("Test failed due to interruption"); - } - } - - class ThrottleTestCallback implements ReadCallback { - int throttle; - - ThrottleTestCallback(int threshold){ - this.throttle = threshold; - } - - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx){ - if(rc != BKException.Code.OK){ - fail("Return code is not OK: " + rc); - } - - ls = seq; - synchronized(sync){ - sync.counter += throttle; - sync.notify(); - } - LOG.info("Current counter: " + sync.counter); - } - } - - /** - * Method for obtaining the available permits of a ledger handle - * using reflection to avoid adding a new public method to the - * class. - * - * @param lh - * @return - */ - @SuppressWarnings("unchecked") - int getAvailablePermits(LedgerHandle lh) throws - NoSuchFieldException, IllegalAccessException - { - Field field = LedgerHandle.class.getDeclaredField("opCounterSem"); - field.setAccessible(true); - return ((Semaphore)field.get(lh)).availablePermits(); - } - - @Test - public void testReadWriteAsyncSingleClientThrottle() throws - IOException, NoSuchFieldException, IllegalAccessException { - try { - - Integer throttle = 100; - ThrottleTestCallback tcb = new ThrottleTestCallback(throttle); - // Create a BookKeeper client and a ledger - System.setProperty("throttle", throttle.toString()); - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - // bkc.initMessageDigest("SHA1"); - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - - numEntriesToWrite = 8000; - for (int i = 0; i < (numEntriesToWrite - 2000); i++) { - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - - entries.add(entry.array()); - entriesSize.add(entry.array().length); - lh.asyncAddEntry(entry.array(), this, sync); - /* - * Check that the difference is no larger than the throttling threshold - */ - int testValue = getAvailablePermits(lh); - assertTrue("Difference is incorrect : " + i + ", " + sync.counter + ", " + testValue, testValue <= throttle); - } - - - for (int i = 0; i < 2000; i++) { - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - - entries.add(entry.array()); - entriesSize.add(entry.array().length); - lh.asyncAddEntry(entry.array(), this, sync); - - /* - * Check that the difference is no larger than the throttling threshold - */ - int testValue = getAvailablePermits(lh); - assertTrue("Difference is incorrect : " + i + ", " + sync.counter + ", " + testValue, testValue <= throttle); - } - - // wait for all entries to be acknowledged - synchronized (sync) { - while (sync.counter < numEntriesToWrite) { - LOG.debug("Entries counter = " + sync.counter); - sync.wait(); - } - } - - LOG.debug("*** WRITE COMPLETE ***"); - // close ledger - lh.close(); - - // *** WRITING PART COMPLETE // READ PART BEGINS *** - - // open ledger - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - LOG.debug("Number of entries written: " + (lh.getLastAddConfirmed() + 1)); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == (numEntriesToWrite - 1)); - - // read entries - sync.counter = 0; - for (int i = 0; i < numEntriesToWrite; i+=throttle) { - lh.asyncReadEntries(i, i + throttle - 1, tcb, (Object) sync); - int testValue = getAvailablePermits(lh); - assertTrue("Difference is incorrect : " + i + ", " + sync.counter + ", " + testValue, testValue <= throttle); - } - - synchronized (sync) { - while (sync.counter < numEntriesToWrite) { - LOG.info("Entries counter = " + sync.counter); - sync.wait(); - } - } - - LOG.debug("*** READ COMPLETE ***"); - - lh.close(); - } catch (KeeperException e) { - LOG.error("Test failed", e); - fail("Test failed due to ZooKeeper exception"); - } catch (BKException e) { - LOG.error("Test failed", e); - fail("Test failed due to BookKeeper exception"); - } catch (InterruptedException e) { - LOG.error("Test failed", e); - fail("Test failed due to interruption"); - } - } - - @Test - public void testSyncReadAsyncWriteStringsSingleClient() throws IOException { - LOG.info("TEST READ WRITE STRINGS MIXED SINGLE CLIENT"); - String charset = "utf-8"; - LOG.debug("Default charset: " + Charset.defaultCharset()); - try { - // Create a BookKeeper client and a ledger - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - // bkc.initMessageDigest("SHA1"); - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - int randomInt = rng.nextInt(maxInt); - byte[] entry = new String(Integer.toString(randomInt)).getBytes(charset); - entries.add(entry); - lh.asyncAddEntry(entry, this, sync); - } - - // wait for all entries to be acknowledged - synchronized (sync) { - while (sync.counter < numEntriesToWrite) { - LOG.debug("Entries counter = " + sync.counter); - sync.wait(); - } - } - - LOG.debug("*** ASYNC WRITE COMPLETE ***"); - // close ledger - lh.close(); - - // *** WRITING PART COMPLETED // READ PART BEGINS *** - - // open ledger - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - LOG.debug("Number of entries written: " + (lh.getLastAddConfirmed() + 1)); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == (numEntriesToWrite - 1)); - - // read entries - ls = lh.readEntries(0, numEntriesToWrite - 1); - - LOG.debug("*** SYNC READ COMPLETE ***"); - - // at this point, LedgerSequence ls is filled with the returned - // values - int i = 0; - while (ls.hasMoreElements()) { - byte[] origEntryBytes = entries.get(i++); - byte[] retrEntryBytes = ls.nextElement().getEntry(); - - LOG.debug("Original byte entry size: " + origEntryBytes.length); - LOG.debug("Saved byte entry size: " + retrEntryBytes.length); - - String origEntry = new String(origEntryBytes, charset); - String retrEntry = new String(retrEntryBytes, charset); - - LOG.debug("Original entry: " + origEntry); - LOG.debug("Retrieved entry: " + retrEntry); - - assertTrue("Checking entry " + i + " for equality", origEntry.equals(retrEntry)); - } - assertTrue("Checking number of read entries", i == numEntriesToWrite); - - lh.close(); - } catch (KeeperException e) { - LOG.error("Test failed", e); - fail("Test failed due to ZooKeeper exception"); - } catch (BKException e) { - LOG.error("Test failed", e); - fail("Test failed due to BookKeeper exception"); - } catch (InterruptedException e) { - LOG.error("Test failed", e); - fail("Test failed due to interruption"); - } - - } - - @Test - public void testReadWriteSyncSingleClient() throws IOException { - try { - // Create a BookKeeper client and a ledger - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - // bkc.initMessageDigest("SHA1"); - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - entries.add(entry.array()); - lh.addEntry(entry.array()); - } - lh.close(); - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - LOG.debug("Number of entries written: " + lh.getLastAddConfirmed()); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == (numEntriesToWrite - 1)); - - ls = lh.readEntries(0, numEntriesToWrite - 1); - int i = 0; - while (ls.hasMoreElements()) { - ByteBuffer origbb = ByteBuffer.wrap(entries.get(i++)); - Integer origEntry = origbb.getInt(); - ByteBuffer result = ByteBuffer.wrap(ls.nextElement().getEntry()); - LOG.debug("Length of result: " + result.capacity()); - LOG.debug("Original entry: " + origEntry); - - Integer retrEntry = result.getInt(); - LOG.debug("Retrieved entry: " + retrEntry); - assertTrue("Checking entry " + i + " for equality", origEntry.equals(retrEntry)); - } - lh.close(); - } catch (KeeperException e) { - LOG.error("Test failed", e); - fail("Test failed due to ZooKeeper exception"); - } catch (BKException e) { - LOG.error("Test failed", e); - fail("Test failed due to BookKeeper exception"); - } catch (InterruptedException e) { - LOG.error("Test failed", e); - fail("Test failed due to interruption"); - } - } - - @Test - public void testReadWriteZero() throws IOException { - try { - // Create a BookKeeper client and a ledger - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - // bkc.initMessageDigest("SHA1"); - ledgerId = lh.getId(); - LOG.info("Ledger ID: " + lh.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - lh.addEntry(new byte[0]); - } - - /* - * Write a non-zero entry - */ - ByteBuffer entry = ByteBuffer.allocate(4); - entry.putInt(rng.nextInt(maxInt)); - entry.position(0); - entries.add(entry.array()); - lh.addEntry(entry.array()); - - lh.close(); - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - LOG.debug("Number of entries written: " + lh.getLastAddConfirmed()); - assertTrue("Verifying number of entries written", lh.getLastAddConfirmed() == numEntriesToWrite); - - ls = lh.readEntries(0, numEntriesToWrite - 1); - int i = 0; - while (ls.hasMoreElements()) { - ByteBuffer result = ByteBuffer.wrap(ls.nextElement().getEntry()); - LOG.debug("Length of result: " + result.capacity()); - - assertTrue("Checking if entry " + i + " has zero bytes", result.capacity() == 0); - } - lh.close(); - } catch (KeeperException e) { - LOG.error("Test failed", e); - fail("Test failed due to ZooKeeper exception"); - } catch (BKException e) { - LOG.error("Test failed", e); - fail("Test failed due to BookKeeper exception"); - } catch (InterruptedException e) { - LOG.error("Test failed", e); - fail("Test failed due to interruption"); - } - } - - @Test - public void testMultiLedger() throws IOException { - try { - // Create a BookKeeper client and a ledger - bkc = new BookKeeper("127.0.0.1"); - lh = bkc.createLedger(digestType, ledgerPassword); - lh2 = bkc.createLedger(digestType, ledgerPassword); - - long ledgerId = lh.getId(); - long ledgerId2 = lh2.getId(); - - // bkc.initMessageDigest("SHA1"); - LOG.info("Ledger ID 1: " + lh.getId() + ", Ledger ID 2: " + lh2.getId()); - for (int i = 0; i < numEntriesToWrite; i++) { - lh.addEntry(new byte[0]); - lh2.addEntry(new byte[0]); - } - - lh.close(); - lh2.close(); - - lh = bkc.openLedger(ledgerId, digestType, ledgerPassword); - lh2 = bkc.openLedger(ledgerId2, digestType, ledgerPassword); - - LOG.debug("Number of entries written: " + lh.getLastAddConfirmed() + ", " + lh2.getLastAddConfirmed()); - assertTrue("Verifying number of entries written lh (" + lh.getLastAddConfirmed() + ")", lh - .getLastAddConfirmed() == (numEntriesToWrite - 1)); - assertTrue("Verifying number of entries written lh2 (" + lh2.getLastAddConfirmed() + ")", lh2 - .getLastAddConfirmed() == (numEntriesToWrite - 1)); - - ls = lh.readEntries(0, numEntriesToWrite - 1); - int i = 0; - while (ls.hasMoreElements()) { - ByteBuffer result = ByteBuffer.wrap(ls.nextElement().getEntry()); - LOG.debug("Length of result: " + result.capacity()); - - assertTrue("Checking if entry " + i + " has zero bytes", result.capacity() == 0); - } - lh.close(); - ls = lh2.readEntries(0, numEntriesToWrite - 1); - i = 0; - while (ls.hasMoreElements()) { - ByteBuffer result = ByteBuffer.wrap(ls.nextElement().getEntry()); - LOG.debug("Length of result: " + result.capacity()); - - assertTrue("Checking if entry " + i + " has zero bytes", result.capacity() == 0); - } - lh2.close(); - } catch (KeeperException e) { - LOG.error("Test failed", e); - fail("Test failed due to ZooKeeper exception"); - } catch (BKException e) { - LOG.error("Test failed", e); - fail("Test failed due to BookKeeper exception"); - } catch (InterruptedException e) { - LOG.error("Test failed", e); - fail("Test failed due to interruption"); - } - } - - public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) { - if(rc != BKException.Code.OK) fail("Return code is not OK: " + rc); - - SyncObj x = (SyncObj) ctx; - - synchronized (x) { - x.counter++; - x.notify(); - } - } - - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - if(rc != BKException.Code.OK) fail("Return code is not OK: " + rc); - - ls = seq; - - synchronized (sync) { - sync.value = true; - sync.notify(); - } - } - - @Before - public void setUp() throws Exception{ - super.setUp(); - rng = new Random(System.currentTimeMillis()); // Initialize the Random - // Number Generator - entries = new ArrayList(); // initialize the entries list - entriesSize = new ArrayList(); - sync = new SyncObj(); // initialize the synchronization data structure - - } - - /* Clean up a directory recursively */ - protected boolean cleanUpDir(File dir) { - if (dir.isDirectory()) { - LOG.info("Cleaning up " + dir.getName()); - String[] children = dir.list(); - for (String string : children) { - boolean success = cleanUpDir(new File(dir, string)); - if (!success) - return false; - } - } - // The directory is now empty so delete it - return dir.delete(); - } - - /* User for testing purposes, void */ - class emptyWatcher implements Watcher { - public void process(WatchedEvent event) { - } - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieRecoveryTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieRecoveryTest.java deleted file mode 100644 index 96177dc613b..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/BookieRecoveryTest.java +++ /dev/null @@ -1,396 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.AsyncCallback.RecoverCallback; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.bookkeeper.tools.BookKeeperTools; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.KeeperException.Code; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * This class tests the bookie recovery admin functionality. - */ -public class BookieRecoveryTest extends BaseTestCase { - static Logger LOG = Logger.getLogger(BookieRecoveryTest.class); - - // Object used for synchronizing async method calls - class SyncObject { - boolean value; - - public SyncObject() { - value = false; - } - } - - // Object used for implementing the Bookie RecoverCallback for this jUnit - // test. This verifies that the operation completed successfully. - class BookieRecoverCallback implements RecoverCallback { - @Override - public void recoverComplete(int rc, Object ctx) { - LOG.info("Recovered bookie operation completed with rc: " + rc); - assertTrue(rc == Code.OK.intValue()); - SyncObject sync = (SyncObject) ctx; - synchronized (sync) { - sync.value = true; - sync.notify(); - } - } - } - - // Objects to use for this jUnit test. - DigestType digestType; - SyncObject sync; - BookieRecoverCallback bookieRecoverCb; - BookKeeperTools bkTools; - - // Constructor - public BookieRecoveryTest(DigestType digestType) { - super(3); - this.digestType = digestType; - } - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - // Set up the configuration properties needed. - System.setProperty("digestType", digestType.toString()); - System.setProperty("passwd", ""); - sync = new SyncObject(); - bookieRecoverCb = new BookieRecoverCallback(); - bkTools = new BookKeeperTools(HOSTPORT); - } - - @After - @Override - public void tearDown() throws Exception { - // Release any resources used by the BookKeeperTools instance. - bkTools.shutdown(); - super.tearDown(); - } - - /** - * Helper method to create a number of ledgers - * - * @param numLedgers - * Number of ledgers to create - * @return List of LedgerHandles for each of the ledgers created - * @throws BKException - * @throws KeeperException - * @throws IOException - * @throws InterruptedException - */ - private List createLedgers(int numLedgers) throws BKException, KeeperException, IOException, - InterruptedException { - List lhs = new ArrayList(); - for (int i = 0; i < numLedgers; i++) { - lhs.add(bkc.createLedger(digestType, System.getProperty("passwd").getBytes())); - } - return lhs; - } - - /** - * Helper method to write dummy ledger entries to all of the ledgers passed. - * - * @param numEntries - * Number of ledger entries to write for each ledger - * @param startEntryId - * The first entry Id we're expecting to write for each ledger - * @param lhs - * List of LedgerHandles for all ledgers to write entries to - * @throws BKException - * @throws InterruptedException - */ - private void writeEntriestoLedgers(int numEntries, long startEntryId, List lhs) throws BKException, - InterruptedException { - for (LedgerHandle lh : lhs) { - for (int i = 0; i < numEntries; i++) { - lh.addEntry(("LedgerId: " + lh.getId() + ", EntryId: " + (startEntryId + i)).getBytes()); - } - } - } - - /** - * Helper method to startup a new bookie server with the indicated port - * number - * - * @param port - * Port to start the new bookie server on - * @throws IOException - */ - private void startNewBookie(int port) throws IOException { - File f = File.createTempFile("bookie", "test"); - tmpDirs.add(f); - f.delete(); - f.mkdir(); - BookieServer server = new BookieServer(port, HOSTPORT, f, new File[] { f }); - server.start(); - bs.add(server); - LOG.info("New bookie on port " + port + " has been created."); - } - - /** - * Helper method to verify that we can read the recovered ledger entries. - * - * @param numLedgers - * Number of ledgers to verify - * @param startEntryId - * Start Entry Id to read - * @param endEntryId - * End Entry Id to read - * @throws BKException - * @throws InterruptedException - */ - private void verifyRecoveredLedgers(int numLedgers, long startEntryId, long endEntryId) throws BKException, - InterruptedException { - // Get a set of LedgerHandles for all of the ledgers to verify - List lhs = new ArrayList(); - for (int i = 0; i < numLedgers; i++) { - lhs.add(bkc.openLedger(i + 1, digestType, System.getProperty("passwd").getBytes())); - } - // Read the ledger entries to verify that they are all present and - // correct in the new bookie. - for (LedgerHandle lh : lhs) { - Enumeration entries = lh.readEntries(startEntryId, endEntryId); - while (entries.hasMoreElements()) { - LedgerEntry entry = entries.nextElement(); - assertTrue(new String(entry.getEntry()).equals("LedgerId: " + entry.getLedgerId() + ", EntryId: " - + entry.getEntryId())); - } - } - - } - - /** - * This tests the asynchronous bookie recovery functionality by writing - * entries into 3 bookies, killing one bookie, starting up a new one to - * replace it, and then recovering the ledger entries from the killed bookie - * onto the new one. We'll verify that the entries stored on the killed - * bookie are properly copied over and restored onto the new one. - * - * @throws Exception - */ - @Test - public void testAsyncBookieRecoveryToSpecificBookie() throws Exception { - // Create the ledgers - int numLedgers = 3; - List lhs = createLedgers(numLedgers); - - // Write the entries for the ledgers with dummy values. - int numMsgs = 10; - writeEntriestoLedgers(numMsgs, 0, lhs); - - // Shutdown the first bookie server - LOG.info("Finished writing all ledger entries so shutdown one of the bookies."); - bs.get(0).shutdown(); - bs.remove(0); - - // Startup a new bookie server - int newBookiePort = initialPort + numBookies; - startNewBookie(newBookiePort); - - // Write some more entries for the ledgers so a new ensemble will be - // created for them. - writeEntriestoLedgers(numMsgs, 10, lhs); - - // Call the async recover bookie method. - InetSocketAddress bookieSrc = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), initialPort); - InetSocketAddress bookieDest = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), newBookiePort); - LOG.info("Now recover the data on the killed bookie (" + bookieSrc + ") and replicate it to the new one (" - + bookieDest + ")"); - // Initiate the sync object - sync.value = false; - bkTools.asyncRecoverBookieData(bookieSrc, bookieDest, bookieRecoverCb, sync); - - // Wait for the async method to complete. - synchronized (sync) { - while (sync.value == false) { - sync.wait(); - } - } - - // Verify the recovered ledger entries are okay. - verifyRecoveredLedgers(numLedgers, 0, 2 * numMsgs - 1); - } - - /** - * This tests the asynchronous bookie recovery functionality by writing - * entries into 3 bookies, killing one bookie, starting up a few new - * bookies, and then recovering the ledger entries from the killed bookie - * onto random available bookie servers. We'll verify that the entries - * stored on the killed bookie are properly copied over and restored onto - * the other bookies. - * - * @throws Exception - */ - @Test - public void testAsyncBookieRecoveryToRandomBookies() throws Exception { - // Create the ledgers - int numLedgers = 3; - List lhs = createLedgers(numLedgers); - - // Write the entries for the ledgers with dummy values. - int numMsgs = 10; - writeEntriestoLedgers(numMsgs, 0, lhs); - - // Shutdown the first bookie server - LOG.info("Finished writing all ledger entries so shutdown one of the bookies."); - bs.get(0).shutdown(); - bs.remove(0); - - // Startup three new bookie servers - for (int i = 0; i < 3; i++) { - int newBookiePort = initialPort + numBookies + i; - startNewBookie(newBookiePort); - } - - // Write some more entries for the ledgers so a new ensemble will be - // created for them. - writeEntriestoLedgers(numMsgs, 10, lhs); - - // Call the async recover bookie method. - InetSocketAddress bookieSrc = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), initialPort); - InetSocketAddress bookieDest = null; - LOG.info("Now recover the data on the killed bookie (" + bookieSrc - + ") and replicate it to a random available one"); - // Initiate the sync object - sync.value = false; - bkTools.asyncRecoverBookieData(bookieSrc, bookieDest, bookieRecoverCb, sync); - - // Wait for the async method to complete. - synchronized (sync) { - while (sync.value == false) { - sync.wait(); - } - } - - // Verify the recovered ledger entries are okay. - verifyRecoveredLedgers(numLedgers, 0, 2 * numMsgs - 1); - } - - /** - * This tests the synchronous bookie recovery functionality by writing - * entries into 3 bookies, killing one bookie, starting up a new one to - * replace it, and then recovering the ledger entries from the killed bookie - * onto the new one. We'll verify that the entries stored on the killed - * bookie are properly copied over and restored onto the new one. - * - * @throws Exception - */ - @Test - public void testSyncBookieRecoveryToSpecificBookie() throws Exception { - // Create the ledgers - int numLedgers = 3; - List lhs = createLedgers(numLedgers); - - // Write the entries for the ledgers with dummy values. - int numMsgs = 10; - writeEntriestoLedgers(numMsgs, 0, lhs); - - // Shutdown the first bookie server - LOG.info("Finished writing all ledger entries so shutdown one of the bookies."); - bs.get(0).shutdown(); - bs.remove(0); - - // Startup a new bookie server - int newBookiePort = initialPort + numBookies; - startNewBookie(newBookiePort); - - // Write some more entries for the ledgers so a new ensemble will be - // created for them. - writeEntriestoLedgers(numMsgs, 10, lhs); - - // Call the sync recover bookie method. - InetSocketAddress bookieSrc = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), initialPort); - InetSocketAddress bookieDest = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), newBookiePort); - LOG.info("Now recover the data on the killed bookie (" + bookieSrc + ") and replicate it to the new one (" - + bookieDest + ")"); - bkTools.recoverBookieData(bookieSrc, bookieDest); - - // Verify the recovered ledger entries are okay. - verifyRecoveredLedgers(numLedgers, 0, 2 * numMsgs - 1); - } - - /** - * This tests the synchronous bookie recovery functionality by writing - * entries into 3 bookies, killing one bookie, starting up a few new - * bookies, and then recovering the ledger entries from the killed bookie - * onto random available bookie servers. We'll verify that the entries - * stored on the killed bookie are properly copied over and restored onto - * the other bookies. - * - * @throws Exception - */ - @Test - public void testSyncBookieRecoveryToRandomBookies() throws Exception { - // Create the ledgers - int numLedgers = 3; - List lhs = createLedgers(numLedgers); - - // Write the entries for the ledgers with dummy values. - int numMsgs = 10; - writeEntriestoLedgers(numMsgs, 0, lhs); - - // Shutdown the first bookie server - LOG.info("Finished writing all ledger entries so shutdown one of the bookies."); - bs.get(0).shutdown(); - bs.remove(0); - - // Startup three new bookie servers - for (int i = 0; i < 3; i++) { - int newBookiePort = initialPort + numBookies + i; - startNewBookie(newBookiePort); - } - - // Write some more entries for the ledgers so a new ensemble will be - // created for them. - writeEntriestoLedgers(numMsgs, 10, lhs); - - // Call the sync recover bookie method. - InetSocketAddress bookieSrc = new InetSocketAddress(InetAddress.getLocalHost().getHostAddress(), initialPort); - InetSocketAddress bookieDest = null; - LOG.info("Now recover the data on the killed bookie (" + bookieSrc - + ") and replicate it to a random available one"); - bkTools.recoverBookieData(bookieSrc, bookieDest); - - // Verify the recovered ledger entries are okay. - verifyRecoveredLedgers(numLedgers, 0, 2 * numMsgs - 1); - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/CloseTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/CloseTest.java deleted file mode 100644 index af43f66916e..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/CloseTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import org.junit.*; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.log4j.Logger; - -/** - * This unit test tests closing ledgers sequentially. It creates 4 ledgers, then - * write 1000 entries to each ledger and close it. - * - */ - -public class CloseTest extends BaseTestCase{ - static Logger LOG = Logger.getLogger(CloseTest.class); - DigestType digestType; - - public CloseTest(DigestType digestType) { - super(3); - this.digestType = digestType; - } - - @Test - public void testClose() throws Exception { - - /* - * Create 4 ledgers. - */ - int numLedgers = 4; - int numMsgs = 100; - - LedgerHandle[] lh = new LedgerHandle[numLedgers]; - for (int i = 0; i < numLedgers; i++) { - lh[i] = bkc.createLedger(digestType, "".getBytes()); - } - - String tmp = "BookKeeper is cool!"; - - /* - * Write 1000 entries to lh1. - */ - for (int i = 0; i < numMsgs; i++) { - for (int j = 0; j < numLedgers; j++) { - lh[j].addEntry(tmp.getBytes()); - } - } - - for (int i = 0; i < numLedgers; i++) { - - lh[i].close(); - } - } -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/ConcurrentLedgerTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/ConcurrentLedgerTest.java deleted file mode 100644 index 7888e563db0..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/ConcurrentLedgerTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -package org.apache.bookkeeper.test; - -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.bookkeeper.bookie.Bookie; -import org.apache.bookkeeper.bookie.BookieException; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import junit.framework.TestCase; - -/** - * Tests writing to concurrent ledgers - */ -public class ConcurrentLedgerTest extends TestCase { - Bookie bookie; - File txnDir, ledgerDir; - int recvTimeout = 10000; - Semaphore throttle; - - @Override - @Before - public void setUp() throws IOException { - String txnDirName = System.getProperty("txnDir"); - if (txnDirName != null) { - txnDir = new File(txnDirName); - } - String ledgerDirName = System.getProperty("ledgerDir"); - if (ledgerDirName != null) { - ledgerDir = new File(ledgerDirName); - } - File tmpFile = File.createTempFile("book", ".txn", txnDir); - tmpFile.delete(); - txnDir = new File(tmpFile.getParent(), tmpFile.getName()+".dir"); - txnDir.mkdirs(); - tmpFile = File.createTempFile("book", ".ledger", ledgerDir); - ledgerDir = new File(tmpFile.getParent(), tmpFile.getName()+".dir"); - ledgerDir.mkdirs(); - - bookie = new Bookie(5000, null, txnDir, new File[] {ledgerDir}); - } - - static void recursiveDelete(File f) { - if (f.isFile()) { - f.delete(); - } else { - for(File i: f.listFiles()) { - recursiveDelete(i); - } - f.delete(); - } - } - - @Override - @After - public void tearDown() { - try { - bookie.shutdown(); - recursiveDelete(txnDir); - recursiveDelete(ledgerDir); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - byte zeros[] = new byte[16]; - - int iterations = 51; - { - String iterationsString = System.getProperty("iterations"); - if (iterationsString != null) { - iterations = Integer.parseInt(iterationsString); - } - } - int iterationStep = 25; - { - String iterationsString = System.getProperty("iterationStep"); - if (iterationsString != null) { - iterationStep = Integer.parseInt(iterationsString); - } - } - @Test - public void testConcurrentWrite() throws IOException, InterruptedException, BookieException { - int size = 1024; - int totalwrites = 128; - if (System.getProperty("totalwrites") != null) { - totalwrites = Integer.parseInt(System.getProperty("totalwrites")); - } - System.out.println("Running up to " + iterations + " iterations"); - System.out.println("Total writes = " + totalwrites); - int ledgers; - for(ledgers = 1; ledgers <= iterations; ledgers += iterationStep) { - long duration = doWrites(ledgers, size, totalwrites); - System.out.println(totalwrites + " on " + ledgers + " took " + duration + " ms"); - } - System.out.println("ledgers " + ledgers); - for(ledgers = 1; ledgers <= iterations; ledgers += iterationStep) { - long duration = doReads(ledgers, size, totalwrites); - System.out.println(ledgers + " read " + duration + " ms"); - } - } - - private long doReads(int ledgers, int size, int totalwrites) - throws IOException, InterruptedException, BookieException { - long start = System.currentTimeMillis(); - for(int i = 1; i <= totalwrites/ledgers; i++) { - for(int j = 1; j <= ledgers; j++) { - ByteBuffer entry = bookie.readEntry(j, i); - // skip the ledger id and the entry id - entry.getLong(); - entry.getLong(); - assertEquals(j + "@" + i, j+2, entry.getLong()); - assertEquals(j + "@" + i, i+3, entry.getLong()); - } - } - long finish = System.currentTimeMillis(); - return finish - start; - } - private long doWrites(int ledgers, int size, int totalwrites) - throws IOException, InterruptedException, BookieException { - throttle = new Semaphore(10000); - WriteCallback cb = new WriteCallback() { - @Override - public void writeComplete(int rc, long ledgerId, long entryId, - InetSocketAddress addr, Object ctx) { - AtomicInteger counter = (AtomicInteger)ctx; - counter.getAndIncrement(); - throttle.release(); - } - }; - AtomicInteger counter = new AtomicInteger(); - long start = System.currentTimeMillis(); - for(int i = 1; i <= totalwrites/ledgers; i++) { - for(int j = 1; j <= ledgers; j++) { - ByteBuffer bytes = ByteBuffer.allocate(size); - bytes.putLong(j); - bytes.putLong(i); - bytes.putLong(j+2); - bytes.putLong(i+3); - bytes.put(("This is ledger " + j + " entry " + i).getBytes()); - bytes.position(0); - bytes.limit(bytes.capacity()); - throttle.acquire(); - bookie.addEntry(bytes, cb, counter, zeros); - } - } - long finish = System.currentTimeMillis(); - return finish - start; - } -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerDeleteTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerDeleteTest.java deleted file mode 100644 index b8ad6905d90..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerDeleteTest.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.io.File; - -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.log4j.Logger; -import org.junit.Before; -import org.junit.Test; - -/** - * This class tests the ledger delete functionality both from the BookKeeper - * client and the server side. - */ -public class LedgerDeleteTest extends BaseTestCase { - static Logger LOG = Logger.getLogger(LedgerDeleteTest.class); - DigestType digestType; - - public LedgerDeleteTest(DigestType digestType) { - super(3); - this.digestType = digestType; - } - - @Before - @Override - public void setUp() throws Exception { - // Set up the configuration properties needed. - System.setProperty("logSizeLimit", Long.toString(2 * 1024 * 1024L)); - System.setProperty("gcWaitTime", "1000"); - super.setUp(); - } - - /** - * Common method to create ledgers and write entries to them. - */ - private LedgerHandle[] writeLedgerEntries(int numLedgers, int msgSize, int numMsgs) throws Exception { - // Create the ledgers - LedgerHandle[] lhs = new LedgerHandle[numLedgers]; - for (int i = 0; i < numLedgers; i++) { - lhs[i] = bkc.createLedger(digestType, "".getBytes()); - } - - // Create a dummy message string to write as ledger entries - StringBuilder msgSB = new StringBuilder(); - for (int i = 0; i < msgSize; i++) { - msgSB.append("a"); - } - String msg = msgSB.toString(); - - // Write all of the entries for all of the ledgers - for (int i = 0; i < numMsgs; i++) { - for (int j = 0; j < numLedgers; j++) { - lhs[j].addEntry(msg.getBytes()); - } - } - - // Return the ledger handles to the inserted ledgers and entries - return lhs; - } - - /** - * This test writes enough ledger entries to roll over the entry log file. - * It will then delete all of the ledgers from the client and let the - * server's EntryLogger garbage collector thread delete the initial entry - * log file. - * - * @throws Exception - */ - @Test - public void testLedgerDelete() throws Exception { - // Write enough ledger entries so that we roll over the initial entryLog (0.log) - LedgerHandle[] lhs = writeLedgerEntries(3, 1024, 1024); - - // Delete all of these ledgers from the BookKeeper client - for (LedgerHandle lh : lhs) { - bkc.deleteLedger(lh.getId()); - } - LOG.info("Finished deleting all ledgers so waiting for the GC thread to clean up the entryLogs"); - Thread.sleep(2000); - - // Verify that the first entry log (0.log) has been deleted from all of the Bookie Servers. - for (File ledgerDirectory : tmpDirs) { - for (File f : ledgerDirectory.listFiles()) { - assertFalse("Found the entry log file (0.log) that should have been deleted in ledgerDirectory: " - + ledgerDirectory, f.isFile() && f.getName().equals("0.log")); - } - } - } - - /** - * This test is similar to testLedgerDelete() except it will stop and - * restart the Bookie Servers after it has written out the ledger entries. - * On restart, there will be existing entry logs and ledger index files for - * the EntryLogger and LedgerCache to read and store into memory. - * - * @throws Exception - */ - @Test - public void testLedgerDeleteWithExistingEntryLogs() throws Exception { - // Write enough ledger entries so that we roll over the initial entryLog (0.log) - LedgerHandle[] lhs = writeLedgerEntries(3, 1024, 1024); - - /* - * Shutdown the Bookie Servers and restart them using the same ledger - * directories. This will test the reading of pre-existing ledger index - * files in the LedgerCache during startup of a Bookie Server. - */ - for (BookieServer server : bs) { - server.shutdown(); - } - bs.clear(); - int j = 0; - for (File f : tmpDirs) { - BookieServer server = new BookieServer(initialPort + j, HOSTPORT, f, new File[] { f }); - server.start(); - bs.add(server); - j++; - } - - // Delete all of these ledgers from the BookKeeper client - for (LedgerHandle lh : lhs) { - bkc.deleteLedger(lh.getId()); - } - LOG.info("Finished deleting all ledgers so waiting for the GC thread to clean up the entryLogs"); - Thread.sleep(2000); - - /* - * Verify that the first two entry logs ([0,1].log) have been deleted - * from all of the Bookie Servers. When we restart the servers in this - * test, a new entry log is created. We know then that the first two - * entry logs should be deleted. - */ - for (File ledgerDirectory : tmpDirs) { - for (File f : ledgerDirectory.listFiles()) { - assertFalse("Found the entry log file ([0,1].log) that should have been deleted in ledgerDirectory: " - + ledgerDirectory, f.isFile() && (f.getName().equals("0.log") || f.getName().equals("1.log"))); - } - } - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerRecoveryTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerRecoveryTest.java deleted file mode 100644 index 2340df08a27..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LedgerRecoveryTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import org.junit.*; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.log4j.Logger; - -/** - * This unit test tests ledger recovery. - * - * - */ - -public class LedgerRecoveryTest extends BaseTestCase { - static Logger LOG = Logger.getLogger(LedgerRecoveryTest.class); - - DigestType digestType; - - public LedgerRecoveryTest(DigestType digestType) { - super(3); - this.digestType = digestType; - } - - private void testInternal(int numEntries) throws Exception { - /* - * Create ledger. - */ - LedgerHandle beforelh = null; - beforelh = bkc.createLedger(digestType, "".getBytes()); - - String tmp = "BookKeeper is cool!"; - for (int i = 0; i < numEntries; i++) { - beforelh.addEntry(tmp.getBytes()); - } - - /* - * Try to open ledger. - */ - LedgerHandle afterlh = bkc.openLedger(beforelh.getId(), digestType, "".getBytes()); - - /* - * Check if has recovered properly. - */ - assertTrue("Has not recovered correctly: " + afterlh.getLastAddConfirmed(), - afterlh.getLastAddConfirmed() == numEntries - 1); - } - - @Test - public void testLedgerRecovery() throws Exception { - testInternal(100); - - } - - @Test - public void testEmptyLedgerRecoveryOne() throws Exception{ - testInternal(1); - } - - @Test - public void testEmptyLedgerRecovery() throws Exception{ - testInternal(0); - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LoopbackClient.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LoopbackClient.java deleted file mode 100644 index faf97911a12..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/LoopbackClient.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.net.InetSocketAddress; -import java.io.IOException; -import java.lang.InterruptedException; -import java.util.Arrays; -import java.util.concurrent.Executors; - -import org.apache.bookkeeper.proto.BookieClient; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.bookkeeper.util.OrderedSafeExecutor; -import org.apache.log4j.Logger; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; - -/** - * This class tests BookieClient. It just sends the a new entry to itself. - * - * - * - */ - -class LoopbackClient implements WriteCallback { - Logger LOG = Logger.getLogger(LoopbackClient.class); - BookieClient client; - static int recvTimeout = 2000; - long begin = 0; - int limit; - OrderedSafeExecutor executor; - - static class Counter { - int c; - int limit; - - Counter(int limit) { - this.c = 0; - this.limit = limit; - } - - synchronized void increment() { - if (++c == limit) - this.notify(); - } - } - - LoopbackClient(ClientSocketChannelFactory channelFactory, OrderedSafeExecutor executor, long begin, int limit) throws IOException { - this.client = new BookieClient(channelFactory, executor); - this.begin = begin; - } - - void write(long ledgerId, long entry, byte[] data, InetSocketAddress addr, WriteCallback cb, Object ctx) - throws IOException, InterruptedException { - LOG.info("Ledger id: " + ledgerId + ", Entry: " + entry); - byte[] passwd = new byte[20]; - Arrays.fill(passwd, (byte) 'a'); - - client.addEntry(addr, ledgerId, passwd, entry, ChannelBuffers.wrappedBuffer(data), cb, ctx); - } - - public void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, Object ctx) { - Counter counter = (Counter) ctx; - counter.increment(); - } - - public static void main(String args[]) { - byte[] data = new byte[Integer.parseInt(args[0])]; - Integer limit = Integer.parseInt(args[1]); - Counter c = new Counter(limit); - long ledgerId = Long.valueOf("0").longValue(); - long begin = System.currentTimeMillis(); - - LoopbackClient lb; - ClientSocketChannelFactory channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors - .newCachedThreadPool()); - OrderedSafeExecutor executor = new OrderedSafeExecutor(2); - try { - InetSocketAddress addr = new InetSocketAddress("127.0.0.1", Integer.valueOf(args[2]).intValue()); - lb = new LoopbackClient(channelFactory, executor, begin, limit.intValue()); - - for (int i = 0; i < limit; i++) { - lb.write(ledgerId, i, data, addr, lb, c); - } - - synchronized (c) { - c.wait(); - System.out.println("Time to write all entries: " + (System.currentTimeMillis() - begin)); - } - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - -} diff --git a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/NIOServerFactoryTest.java b/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/NIOServerFactoryTest.java deleted file mode 100644 index 980f59d2e47..00000000000 --- a/src/contrib/bookkeeper/test/org/apache/bookkeeper/test/NIOServerFactoryTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.apache.bookkeeper.test; - -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -import java.net.Socket; -import java.nio.ByteBuffer; - -import org.apache.bookkeeper.proto.NIOServerFactory; -import org.apache.bookkeeper.proto.NIOServerFactory.Cnxn; -import org.apache.bookkeeper.proto.NIOServerFactory.PacketProcessor; -import org.junit.Test; - -import junit.framework.TestCase; - -public class NIOServerFactoryTest extends TestCase { - PacketProcessor problemProcessor = new PacketProcessor() { - - public void processPacket(ByteBuffer packet, Cnxn src) { - if (packet.getInt() == 1) { - throw new RuntimeException("Really bad thing happened"); - } - src.sendResponse(new ByteBuffer[] { ByteBuffer.allocate(4) }); - } - - }; - - @Test - public void testProblemProcessor() throws Exception { - NIOServerFactory factory = new NIOServerFactory(22334, problemProcessor); - Socket s = new Socket("127.0.0.1", 22334); - s.setSoTimeout(5000); - try { - s.getOutputStream().write("\0\0\0\4\0\0\0\1".getBytes()); - s.getOutputStream().write("\0\0\0\4\0\0\0\2".getBytes()); - s.getInputStream().read(); - } finally { - s.close(); - factory.shutdown(); - } - } -} diff --git a/src/contrib/build-contrib.xml b/src/contrib/build-contrib.xml deleted file mode 100644 index 925d9f85e3c..00000000000 --- a/src/contrib/build-contrib.xml +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/build.xml b/src/contrib/build.xml deleted file mode 100644 index 7be6d286fe9..00000000000 --- a/src/contrib/build.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/fatjar/README.txt b/src/contrib/fatjar/README.txt deleted file mode 100644 index f8027ae8c9e..00000000000 --- a/src/contrib/fatjar/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -This package contains build to create a fat zookeeper jar. You need to run ant to create the fat jar. -To run the fatjar you can use. java -jar zoookeeper-*fatjar.jar diff --git a/src/contrib/fatjar/build.xml b/src/contrib/fatjar/build.xml deleted file mode 100644 index cd56844a799..00000000000 --- a/src/contrib/fatjar/build.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/fatjar/conf/mainClasses b/src/contrib/fatjar/conf/mainClasses deleted file mode 100644 index 2b0fc83f468..00000000000 --- a/src/contrib/fatjar/conf/mainClasses +++ /dev/null @@ -1,10 +0,0 @@ -::Client Commands -client:org.apache.zookeeper.ZooKeeperMain:Client shell to ZooKeeper -::Server Commands -server:org.apache.zookeeper.server.quorum.QuorumPeerMain:Start ZooKeeper server -::Test Commands -generateLoad:org.apache.zookeeper.test.system.GenerateLoad:A distributed load generator for testing -quorumBench:org.apache.zookeeper.server.QuorumBenchmark:A benchmark of just the quorum protocol -abBench:org.apache.zookeeper.server.quorum.AtomicBroadcastBenchmark:A benchmark of just the atomic broadcast -ic:org.apache.zookeeper.test.system.InstanceContainer:A container that will instantiate classes as directed by an instance manager -systest:org.apache.zookeeper.test.system.BaseSysTest:Start system test diff --git a/src/contrib/fatjar/src/java/org/apache/zookeeper/util/FatJarMain.java b/src/contrib/fatjar/src/java/org/apache/zookeeper/util/FatJarMain.java deleted file mode 100644 index bdf0eaec546..00000000000 --- a/src/contrib/fatjar/src/java/org/apache/zookeeper/util/FatJarMain.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.util; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; - -/** - * This is a generic Main class that is completely driven by the - * /mainClasses resource on the class path. This resource has the - * format: - *
- * cmd:mainClass:Description
- * 
- * Any lines starting with # will be skipped - * - */ -public class FatJarMain { - static class Cmd { - Cmd(String cmd, String clazz, String desc) { - this.cmd = cmd; - this.clazz = clazz; - this.desc = desc; - } - String cmd; - String clazz; - String desc; - } - static HashMap cmds = new HashMap(); - static ArrayList order = new ArrayList(); - - /** - * @param args the first parameter of args will be used as an - * index into the /mainClasses resource. The rest will be passed - * to the mainClass to run. - * @throws IOException - * @throws ClassNotFoundException - * @throws NoSuchMethodException - * @throws SecurityException - * @throws IllegalAccessException - * @throws IllegalArgumentException - */ - public static void main(String[] args) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException { - InputStream is = FatJarMain.class.getResourceAsStream("/mainClasses"); - if (is == null) { - System.err.println("Couldn't find /mainClasses in classpath."); - System.exit(3); - } - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - String line; - while((line = br.readLine()) != null) { - String parts[] = line.split(":", 3); - if (parts.length != 3 || (parts[0].length() > 0 && parts[0].charAt(0) == '#')) { - continue; - } - if (parts[0].length() > 0) { - cmds.put(parts[0], new Cmd(parts[0], parts[1], parts[2])); - // We use the order array to preserve the order of the commands - // for help. The hashmap will not preserver order. (It may be overkill.) - order.add(parts[0]); - } else { - // Just put the description in - order.add(parts[2]); - } - } - if (args.length == 0) { - doHelp(); - return; - } - Cmd cmd = cmds.get(args[0]); - if (cmd == null) { - doHelp(); - return; - } - Class clazz = Class.forName(cmd.clazz); - Method main = clazz.getMethod("main", String[].class); - String newArgs[] = new String[args.length-1]; - System.arraycopy(args, 1, newArgs, 0, newArgs.length); - try { - main.invoke(null, (Object)newArgs); - } catch(InvocationTargetException e) { - if (e.getCause() != null) { - e.getCause().printStackTrace(); - } else { - e.printStackTrace(); - } - } - } - - private static void doHelp() { - System.err.println("USAGE: FatJarMain cmd args"); - System.err.println("Available cmds:"); - for(String c: order) { - Cmd cmd = cmds.get(c); - if (cmd != null) { - System.err.println(" " + c + " " + cmd.desc); - } else { - System.err.println(c); - } - } - System.exit(2); - } - -} diff --git a/src/contrib/hedwig/LICENSE.txt b/src/contrib/hedwig/LICENSE.txt deleted file mode 100644 index d6456956733..00000000000 --- a/src/contrib/hedwig/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/contrib/hedwig/NOTICE.txt b/src/contrib/hedwig/NOTICE.txt deleted file mode 100644 index 2cbefade9d4..00000000000 --- a/src/contrib/hedwig/NOTICE.txt +++ /dev/null @@ -1,2 +0,0 @@ -Copyright (c) 2010 Yahoo! Inc. All rights reserved. - diff --git a/src/contrib/hedwig/README b/src/contrib/hedwig/README deleted file mode 100644 index 4a9445e7efc..00000000000 --- a/src/contrib/hedwig/README +++ /dev/null @@ -1,3 +0,0 @@ -Hedwig is a large scale pub/sub system built on top of ZooKeeper and BookKeeper. - -For documentation on building, setting up, and using Hedwig see the `doc` directory. diff --git a/src/contrib/hedwig/client/pom.xml b/src/contrib/hedwig/client/pom.xml deleted file mode 100644 index d373af22416..00000000000 --- a/src/contrib/hedwig/client/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - 4.0.0 - - org.apache.hedwig - hedwig - 1.0-SNAPSHOT - - - org.apache.hedwig.client.App - - org.apache.hedwig - client - jar - 1.0-SNAPSHOT - client - http://maven.apache.org - - - junit - junit - 4.8.1 - test - - - org.apache.hedwig - protocol - 1.0-SNAPSHOT - compile - - - log4j - log4j - 1.2.14 - compile - - - org.jboss.netty - netty - 3.1.2.GA - compile - - - commons-configuration - commons-configuration - 1.6 - - - - - jboss.release - JBoss releases - http://repository.jboss.org/maven2 - - - diff --git a/src/contrib/hedwig/client/src/main/cpp/Makefile.am b/src/contrib/hedwig/client/src/main/cpp/Makefile.am deleted file mode 100644 index b683095e6b8..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -ACLOCAL_AMFLAGS = -I m4 - -SUBDIRS = lib test - -library_includedir=$(includedir)/hedwig-0.1/hedwig -library_include_HEADERS = inc/hedwig/callback.h inc/hedwig/client.h inc/hedwig/exceptions.h inc/hedwig/publish.h inc/hedwig/subscribe.h - -pkgconfigdir = $(libdir)/pkgconfig -nodist_pkgconfig_DATA = hedwig-0.1.pc - -EXTRA_DIST = $(DX_CONFIG) doc/html diff --git a/src/contrib/hedwig/client/src/main/cpp/aminclude.am b/src/contrib/hedwig/client/src/main/cpp/aminclude.am deleted file mode 100644 index 420049eca35..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/aminclude.am +++ /dev/null @@ -1,186 +0,0 @@ -# Copyright (C) 2004 Oren Ben-Kiki -# This file is distributed under the same terms as the Automake macro files. - -# Generate automatic documentation using Doxygen. Goals and variables values -# are controlled by the various DX_COND_??? conditionals set by autoconf. -# -# The provided goals are: -# doxygen-doc: Generate all doxygen documentation. -# doxygen-run: Run doxygen, which will generate some of the documentation -# (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post -# processing required for the rest of it (PS, PDF, and some MAN). -# doxygen-man: Rename some doxygen generated man pages. -# doxygen-ps: Generate doxygen PostScript documentation. -# doxygen-pdf: Generate doxygen PDF documentation. -# -# Note that by default these are not integrated into the automake goals. If -# doxygen is used to generate man pages, you can achieve this integration by -# setting man3_MANS to the list of man pages generated and then adding the -# dependency: -# -# $(man3_MANS): doxygen-doc -# -# This will cause make to run doxygen and generate all the documentation. -# -# The following variable is intended for use in Makefile.am: -# -# DX_CLEANFILES = everything to clean. -# -# This is usually added to MOSTLYCLEANFILES. - -## --------------------------------- ## -## Format-independent Doxygen rules. ## -## --------------------------------- ## - -if DX_COND_doc - -## ------------------------------- ## -## Rules specific for HTML output. ## -## ------------------------------- ## - -if DX_COND_html - -DX_CLEAN_HTML = @DX_DOCDIR@/html - -endif DX_COND_html - -## ------------------------------ ## -## Rules specific for CHM output. ## -## ------------------------------ ## - -if DX_COND_chm - -DX_CLEAN_CHM = @DX_DOCDIR@/chm - -if DX_COND_chi - -DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi - -endif DX_COND_chi - -endif DX_COND_chm - -## ------------------------------ ## -## Rules specific for MAN output. ## -## ------------------------------ ## - -if DX_COND_man - -DX_CLEAN_MAN = @DX_DOCDIR@/man - -endif DX_COND_man - -## ------------------------------ ## -## Rules specific for RTF output. ## -## ------------------------------ ## - -if DX_COND_rtf - -DX_CLEAN_RTF = @DX_DOCDIR@/rtf - -endif DX_COND_rtf - -## ------------------------------ ## -## Rules specific for XML output. ## -## ------------------------------ ## - -if DX_COND_xml - -DX_CLEAN_XML = @DX_DOCDIR@/xml - -endif DX_COND_xml - -## ----------------------------- ## -## Rules specific for PS output. ## -## ----------------------------- ## - -if DX_COND_ps - -DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps - -DX_PS_GOAL = doxygen-ps - -doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps - -@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag - cd @DX_DOCDIR@/latex; \ - rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ - $(DX_LATEX) refman.tex; \ - $(MAKEINDEX_PATH) refman.idx; \ - $(DX_LATEX) refman.tex; \ - countdown=5; \ - while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ - refman.log > /dev/null 2>&1 \ - && test $$countdown -gt 0; do \ - $(DX_LATEX) refman.tex; \ - countdown=`expr $$countdown - 1`; \ - done; \ - $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi - -endif DX_COND_ps - -## ------------------------------ ## -## Rules specific for PDF output. ## -## ------------------------------ ## - -if DX_COND_pdf - -DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf - -DX_PDF_GOAL = doxygen-pdf - -doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf - -@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag - cd @DX_DOCDIR@/latex; \ - rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ - $(DX_PDFLATEX) refman.tex; \ - $(DX_MAKEINDEX) refman.idx; \ - $(DX_PDFLATEX) refman.tex; \ - countdown=5; \ - while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ - refman.log > /dev/null 2>&1 \ - && test $$countdown -gt 0; do \ - $(DX_PDFLATEX) refman.tex; \ - countdown=`expr $$countdown - 1`; \ - done; \ - mv refman.pdf ../@PACKAGE@.pdf - -endif DX_COND_pdf - -## ------------------------------------------------- ## -## Rules specific for LaTeX (shared for PS and PDF). ## -## ------------------------------------------------- ## - -if DX_COND_latex - -DX_CLEAN_LATEX = @DX_DOCDIR@/latex - -endif DX_COND_latex - -.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) - -.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) - -doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag - -doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) - -@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS) - rm -rf @DX_DOCDIR@ - $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) - -DX_CLEANFILES = \ - @DX_DOCDIR@/@PACKAGE@.tag \ - -r \ - $(DX_CLEAN_HTML) \ - $(DX_CLEAN_CHM) \ - $(DX_CLEAN_CHI) \ - $(DX_CLEAN_MAN) \ - $(DX_CLEAN_RTF) \ - $(DX_CLEAN_XML) \ - $(DX_CLEAN_PS) \ - $(DX_CLEAN_PDF) \ - $(DX_CLEAN_LATEX) - -endif DX_COND_doc diff --git a/src/contrib/hedwig/client/src/main/cpp/c-doc.Doxyfile b/src/contrib/hedwig/client/src/main/cpp/c-doc.Doxyfile deleted file mode 100644 index 8e1999e2400..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/c-doc.Doxyfile +++ /dev/null @@ -1,1252 +0,0 @@ -# Doxyfile 1.4.7 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = $(PROJECT)-$(VERSION) - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = $(DOCDIR) - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, -# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, -# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, -# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, -# Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# This tag can be used to specify the encoding used in the generated output. -# The encoding is not always determined by the language that is chosen, -# but also whether or not the output is meant for Windows or non-Windows users. -# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES -# forces the Windows encoding (this is the default for the Windows binary), -# whereas setting the tag to NO uses a Unix-style encoding (the default for -# all platforms other than Windows). - -USE_WINDOWS_ENCODING = NO - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like the Qt-style comments (thus requiring an -# explicit @brief command for a brief description. - -JAVADOC_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to -# include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST = YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from the -# version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = inc/ lib/ - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = $(GENERATE_HTML) - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = $(GENERATE_HTMLHELP) - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = ../$(PROJECT).chm - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = $(HHC_PATH) - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = $(GENERATE_CHI) - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = $(GENERATE_LATEX) - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = $(PAPER_SIZE) - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = $(GENERATE_PDF) - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = $(GENERATE_RTF) - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = $(GENERATE_MAN) - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = $(GENERATE_XML) - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = $(DOCDIR)/$(PROJECT).tag - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = $(HAVE_DOT) - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a caller dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = $(DOT_PATH) - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_WIDTH = 1024 - -# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_HEIGHT = 1024 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that a graph may be further truncated if the graph's -# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH -# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), -# the graph is not depth-constrained. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/src/contrib/hedwig/client/src/main/cpp/config.h.in b/src/contrib/hedwig/client/src/main/cpp/config.h.in deleted file mode 100644 index 19266b3b5e4..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/config.h.in +++ /dev/null @@ -1,56 +0,0 @@ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#undef LT_OBJDIR - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS diff --git a/src/contrib/hedwig/client/src/main/cpp/configure.ac b/src/contrib/hedwig/client/src/main/cpp/configure.ac deleted file mode 100644 index 64f8385357b..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/configure.ac +++ /dev/null @@ -1,40 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -AC_INIT([Hedwig C++ Client], [0.1], [zookeeper-dev@hadoop.apache.org], [hedwig-cpp], [http://hadoop.apache.org/zookeeper//]) - -AC_PREREQ([2.59]) -AM_INIT_AUTOMAKE([1.9 no-define foreign]) -AC_CONFIG_HEADERS([config.h]) -AC_PROG_CXX -AC_LANG([C++]) -AC_CONFIG_FILES([Makefile lib/Makefile test/Makefile hedwig-0.1.pc]) -AC_PROG_LIBTOOL -AC_CONFIG_MACRO_DIR([m4]) -PKG_CHECK_MODULES([DEPS], [log4cpp protobuf cppunit]) -AX_BOOST_BASE -AX_BOOST_ASIO -AX_BOOST_THREAD - -DX_HTML_FEATURE(ON) -DX_INIT_DOXYGEN(hedwig-c++, c-doc.Doxyfile, doc) - -CXXFLAGS="$CXXFLAGS -Wall" - -AC_OUTPUT - diff --git a/src/contrib/hedwig/client/src/main/cpp/hedwig-0.1.pc.in b/src/contrib/hedwig/client/src/main/cpp/hedwig-0.1.pc.in deleted file mode 100644 index 1e7eea1644d..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/hedwig-0.1.pc.in +++ /dev/null @@ -1,30 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: something -Description: Some library. -Requires: -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lhedwig01 -Cflags: -I${includedir}/hedwig-0.1 -I${libdir}/hedwig-0.1/include - diff --git a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/callback.h b/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/callback.h deleted file mode 100644 index c975e88efbf..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/callback.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HEDWIG_CALLBACK_H -#define HEDWIG_CALLBACK_H - -#include -#include -#include -#include - -namespace Hedwig { - class OperationCallback { - public: - virtual void operationComplete() = 0; - virtual void operationFailed(const std::exception& exception) = 0; - - virtual ~OperationCallback() {}; - }; - typedef std::tr1::shared_ptr OperationCallbackPtr; - - class MessageHandlerCallback { - public: - virtual void consume(const std::string& topic, const std::string& subscriberId, const Message& msg, OperationCallbackPtr& callback) = 0; - - virtual ~MessageHandlerCallback() {}; - }; - typedef std::tr1::shared_ptr MessageHandlerCallbackPtr; -} - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/client.h b/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/client.h deleted file mode 100644 index 7bda0ae3914..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/client.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HEDWIG_CLIENT_H -#define HEDWIG_CLIENT_H - -#include -#include - -#include -#include -#include -#include -#include - -namespace Hedwig { - - class ClientImpl; - typedef boost::shared_ptr ClientImplPtr; - - class Configuration { - public: - static const std::string DEFAULT_SERVER; - static const std::string MESSAGE_CONSUME_RETRY_WAIT_TIME; - static const std::string SUBSCRIBER_CONSUME_RETRY_WAIT_TIME; - static const std::string MAX_MESSAGE_QUEUE_SIZE; - static const std::string RECONNECT_SUBSCRIBE_RETRY_WAIT_TIME; - static const std::string SYNC_REQUEST_TIMEOUT; - - public: - Configuration() {}; - virtual int getInt(const std::string& key, int defaultVal) const = 0; - virtual const std::string get(const std::string& key, const std::string& defaultVal) const = 0; - virtual bool getBool(const std::string& key, bool defaultVal) const = 0; - - virtual ~Configuration() {} - }; - - /** - Main Hedwig client class. This class is used to acquire an instance of the Subscriber of Publisher. - */ - class Client : private boost::noncopyable { - public: - Client(const Configuration& conf); - - /** - Retrieve the subscriber object - */ - Subscriber& getSubscriber(); - - /** - Retrieve the publisher object - */ - Publisher& getPublisher(); - - ~Client(); - - private: - ClientImplPtr clientimpl; - }; - - -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/exceptions.h b/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/exceptions.h deleted file mode 100644 index 72b7ac37e3b..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/exceptions.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HEDWIG_EXCEPTION_H -#define HEDWIG_EXCEPTION_H - -#include - -namespace Hedwig { - - class ClientException : public std::exception { }; - - class ClientTimeoutException : public ClientException {}; - - class ServiceDownException : public ClientException {}; - class CannotConnectException : public ClientException {}; - class UnexpectedResponseException : public ClientException {}; - class OomException : public ClientException {}; - class UnknownRequestException : public ClientException {}; - class InvalidRedirectException : public ClientException {}; - - class PublisherException : public ClientException { }; - - - class SubscriberException : public ClientException { }; - class AlreadySubscribedException : public SubscriberException {}; - class NotSubscribedException : public SubscriberException {}; - - class ConfigurationException : public ClientException { }; - class InvalidPortException : public ConfigurationException {}; - class HostResolutionException : public ClientException {}; - - class InvalidStateException : public ClientException {}; - class ShuttingDownException : public InvalidStateException {}; -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/publish.h b/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/publish.h deleted file mode 100644 index 22009de7a9d..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/publish.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HEDWIG_PUBLISH_H -#define HEDWIG_PUBLISH_H - -#include - -#include -#include -#include -#include - -namespace Hedwig { - - /** - Interface for publishing to a hedwig instance. - */ - class Publisher : private boost::noncopyable { - public: - /** - Publish message for topic, and block until we receive a ACK response from the hedwig server. - - @param topic Topic to publish to. - @param message Data to publish for topic. - */ - virtual void publish(const std::string& topic, const std::string& message) = 0; - - /** - Asynchronously publish message for topic. - - @code - OperationCallbackPtr callback(new MyCallback()); - pub.asyncPublish(callback); - @endcode - - @param topic Topic to publish to. - @param message Data to publish to topic - @param callback Callback which will be used to report success or failure. Success is only reported once the server replies with an ACK response to the publication. - */ - virtual void asyncPublish(const std::string& topic, const std::string& message, const OperationCallbackPtr& callback) = 0; - - virtual ~Publisher() {} - }; -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/subscribe.h b/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/subscribe.h deleted file mode 100644 index 775a32c15fd..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/inc/hedwig/subscribe.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HEDWIG_SUBSCRIBE_H -#define HEDWIG_SUBSCRIBE_H - -#include - -#include -#include -#include -#include - -namespace Hedwig { - - /** - Interface for subscribing to a hedwig instance. - */ - class Subscriber : private boost::noncopyable { - public: - virtual void subscribe(const std::string& topic, const std::string& subscriberId, const SubscribeRequest::CreateOrAttach mode) = 0; - virtual void asyncSubscribe(const std::string& topic, const std::string& subscriberId, const SubscribeRequest::CreateOrAttach mode, const OperationCallbackPtr& callback) = 0; - - virtual void unsubscribe(const std::string& topic, const std::string& subscriberId) = 0; - virtual void asyncUnsubscribe(const std::string& topic, const std::string& subscriberId, const OperationCallbackPtr& callback) = 0; - - virtual void consume(const std::string& topic, const std::string& subscriberId, const MessageSeqId& messageSeqId) = 0; - - virtual void startDelivery(const std::string& topic, const std::string& subscriberId, const MessageHandlerCallbackPtr& callback) = 0; - virtual void stopDelivery(const std::string& topic, const std::string& subscriberId) = 0; - - virtual void closeSubscription(const std::string& topic, const std::string& subscriberId) = 0; - - virtual ~Subscriber() {} - }; -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/Makefile.am b/src/contrib/hedwig/client/src/main/cpp/lib/Makefile.am deleted file mode 100644 index 9a531388996..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/Makefile.am +++ /dev/null @@ -1,32 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -PROTODEF = ../../../../../protocol/src/main/protobuf/PubSubProtocol.proto - -lib_LTLIBRARIES = libhedwig01.la -libhedwig01_la_SOURCES = protocol.cpp channel.cpp client.cpp util.cpp clientimpl.cpp publisherimpl.cpp subscriberimpl.cpp eventdispatcher.cpp data.cpp -libhedwig01_la_CPPFLAGS = -I$(top_srcdir)/inc $(DEPS_CFLAGS) -libhedwig01_la_LIBADD = $(DEPS_LIBS) $(BOOST_CPPFLAGS) -libhedwig01_la_LDFLAGS = -no-undefined $(BOOST_ASIO_LIB) $(BOOST_LDFLAGS) $(BOOST_THREAD_LIB) - -protocol.cpp: $(PROTODEF) - protoc --cpp_out=. -I`dirname $(PROTODEF)` $(PROTODEF) - sed "s/PubSubProtocol.pb.h/hedwig\/protocol.h/" PubSubProtocol.pb.cc > protocol.cpp - rm PubSubProtocol.pb.cc - mv PubSubProtocol.pb.h $(top_srcdir)/inc/hedwig/protocol.h - diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/channel.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/channel.cpp deleted file mode 100644 index 0663ed6e84a..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/channel.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include "channel.h" -#include "util.h" -#include "clientimpl.h" - -#include -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -DuplexChannel::DuplexChannel(EventDispatcher& dispatcher, const HostAddress& addr, - const Configuration& cfg, const ChannelHandlerPtr& handler) - : dispatcher(dispatcher), address(addr), handler(handler), - socket(dispatcher.getService()), instream(&in_buf), copy_buf(NULL), copy_buf_length(0), - state(UNINITIALISED), receiving(false), sending(false) -{ - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating DuplexChannel(" << this << ")"; - } -} - -/*static*/ void DuplexChannel::connectCallbackHandler(DuplexChannelPtr channel, - const boost::system::error_code& error) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::connectCallbackHandler error(" << error - << ") channel(" << channel.get() << ")"; - } - - if (error) { - channel->channelDisconnected(ChannelConnectException()); - channel->setState(DEAD); - return; - } - - channel->setState(CONNECTED); - - boost::system::error_code ec; - boost::asio::ip::tcp::no_delay option(true); - - channel->socket.set_option(option, ec); - if (ec) { - channel->channelDisconnected(ChannelSetupException()); - channel->setState(DEAD); - return; - } - - channel->startSending(); - channel->startReceiving(); -} - -void DuplexChannel::connect() { - setState(CONNECTING); - - boost::asio::ip::tcp::endpoint endp(boost::asio::ip::address_v4(address.ip()), address.port()); - boost::system::error_code error = boost::asio::error::host_not_found; - - socket.async_connect(endp, boost::bind(&DuplexChannel::connectCallbackHandler, - shared_from_this(), - boost::asio::placeholders::error)); -} - -/*static*/ void DuplexChannel::messageReadCallbackHandler(DuplexChannelPtr channel, - std::size_t message_size, - const boost::system::error_code& error, - std::size_t bytes_transferred) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::messageReadCallbackHandler " << error << ", " - << bytes_transferred << " channel(" << channel.get() << ")"; - } - - if (error) { - LOG.errorStream() << "Invalid read error (" << error << ") bytes_transferred (" - << bytes_transferred << ") channel(" << channel.get() << ")"; - channel->channelDisconnected(ChannelReadException()); - return; - } - - if (channel->copy_buf_length < message_size) { - channel->copy_buf_length = message_size; - channel->copy_buf = (char*)realloc(channel->copy_buf, channel->copy_buf_length); - if (channel->copy_buf == NULL) { - LOG.errorStream() << "Error allocating buffer. channel(" << channel.get() << ")"; - return; - } - } - - channel->instream.read(channel->copy_buf, message_size); - PubSubResponsePtr response(new PubSubResponse()); - bool err = response->ParseFromArray(channel->copy_buf, message_size); - - - if (!err) { - LOG.errorStream() << "Error parsing message. channel(" << channel.get() << ")"; - - channel->channelDisconnected(ChannelReadException()); - return; - } else if (LOG.isDebugEnabled()) { - LOG.debugStream() << "channel(" << channel.get() << ") : " << channel->in_buf.size() - << " bytes left in buffer"; - } - - ChannelHandlerPtr h; - { - boost::shared_lock lock(channel->destruction_lock); - if (channel->handler.get()) { - h = channel->handler; - } - } - if (h.get()) { - h->messageReceived(channel, response); - } - - DuplexChannel::readSize(channel); -} - -/*static*/ void DuplexChannel::sizeReadCallbackHandler(DuplexChannelPtr channel, - const boost::system::error_code& error, - std::size_t bytes_transferred) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::sizeReadCallbackHandler " << error << ", " - << bytes_transferred << " channel(" << channel.get() << ")"; - } - - if (error) { - LOG.errorStream() << "Invalid read error (" << error << ") bytes_transferred (" - << bytes_transferred << ") channel(" << channel.get() << ")"; - channel->channelDisconnected(ChannelReadException()); - return; - } - - if (channel->in_buf.size() < sizeof(uint32_t)) { - LOG.errorStream() << "Not enough data in stream. Must have been an error reading. " - << " Closing channel(" << channel.get() << ")"; - channel->channelDisconnected(ChannelReadException()); - return; - } - - uint32_t size; - std::istream is(&channel->in_buf); - is.read((char*)&size, sizeof(uint32_t)); - size = ntohl(size); - - int toread = size - channel->in_buf.size(); - if (LOG.isDebugEnabled()) { - LOG.debugStream() << " size of incoming message " << size << ", currently in buffer " - << channel->in_buf.size() << " channel(" << channel.get() << ")"; - } - if (toread <= 0) { - DuplexChannel::messageReadCallbackHandler(channel, size, error, 0); - } else { - boost::asio::async_read(channel->socket, channel->in_buf, - boost::asio::transfer_at_least(toread), - boost::bind(&DuplexChannel::messageReadCallbackHandler, - channel, size, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } -} - -/*static*/ void DuplexChannel::readSize(DuplexChannelPtr channel) { - if (!channel->isReceiving()) { - return; - } - - int toread = sizeof(uint32_t) - channel->in_buf.size(); - if (LOG.isDebugEnabled()) { - LOG.debugStream() << " size of incoming message " << sizeof(uint32_t) - << ", currently in buffer " << channel->in_buf.size() - << " channel(" << channel.get() << ")"; - } - - if (toread < 0) { - DuplexChannel::sizeReadCallbackHandler(channel, boost::system::error_code(), 0); - } else { - // in_buf_size.prepare(sizeof(uint32_t)); - boost::asio::async_read(channel->socket, channel->in_buf, - boost::asio::transfer_at_least(sizeof(uint32_t)), - boost::bind(&DuplexChannel::sizeReadCallbackHandler, - channel, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } -} - -void DuplexChannel::startReceiving() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::startReceiving channel(" << this << ")"; - } - - boost::lock_guard lock(receiving_lock); - receiving = true; - - DuplexChannel::readSize(shared_from_this()); -} - -bool DuplexChannel::isReceiving() { - return receiving; -} - -void DuplexChannel::stopReceiving() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::stopReceiving channel(" << this << ")"; - } - - boost::lock_guard lock(receiving_lock); - receiving = false; -} - -void DuplexChannel::startSending() { - { - boost::shared_lock lock(state_lock); - if (state != CONNECTED) { - return; - } - } - - boost::lock_guard lock(sending_lock); - if (sending) { - return; - } - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::startSending channel(" << this << ")"; - } - - WriteRequest w; - { - boost::lock_guard lock(write_lock); - if (write_queue.empty()) { - return; - } - w = write_queue.front(); - write_queue.pop_front(); - } - - sending = true; - - std::ostream os(&out_buf); - uint32_t size = htonl(w.first->ByteSize()); - os.write((char*)&size, sizeof(uint32_t)); - - bool err = w.first->SerializeToOstream(&os); - if (!err) { - w.second->operationFailed(ChannelWriteException()); - channelDisconnected(ChannelWriteException()); - return; - } - - boost::asio::async_write(socket, out_buf, - boost::bind(&DuplexChannel::writeCallbackHandler, - shared_from_this(), - w.second, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); -} - - -const HostAddress& DuplexChannel::getHostAddress() const { - return address; -} - -void DuplexChannel::channelDisconnected(const std::exception& e) { - setState(DEAD); - - { - boost::lock_guard lock(write_lock); - while (!write_queue.empty()) { - WriteRequest w = write_queue.front(); - write_queue.pop_front(); - w.second->operationFailed(e); - } - } - - ChannelHandlerPtr h; - { - boost::shared_lock lock(destruction_lock); - if (handler.get()) { - h = handler; - } - } - if (h.get()) { - h->channelDisconnected(shared_from_this(), e); - } -} - -void DuplexChannel::kill() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Killing duplex channel (" << this << ")"; - } - - bool connected = false; - { - boost::shared_lock statelock(state_lock); - connected = (state == CONNECTING || state == CONNECTED); - } - - boost::lock_guard lock(destruction_lock); - if (connected) { - setState(DEAD); - - socket.cancel(); - socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); - socket.close(); - } - handler = ChannelHandlerPtr(); // clear the handler in case it ever referenced the channel*/ -} - -DuplexChannel::~DuplexChannel() { - /** If we are going away, fail all transactions that haven't been completed */ - failAllTransactions(); - kill(); - free(copy_buf); - copy_buf = NULL; - copy_buf_length = 0; - - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Destroying DuplexChannel(" << this << ")"; - } -} - -/*static*/ void DuplexChannel::writeCallbackHandler(DuplexChannelPtr channel, OperationCallbackPtr callback, - const boost::system::error_code& error, - std::size_t bytes_transferred) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::writeCallbackHandler " << error << ", " - << bytes_transferred << " channel(" << channel.get() << ")"; - } - - if (error) { - callback->operationFailed(ChannelWriteException()); - channel->channelDisconnected(ChannelWriteException()); - return; - } - - callback->operationComplete(); - - channel->out_buf.consume(bytes_transferred); - - { - boost::lock_guard lock(channel->sending_lock); - channel->sending = false; - } - - channel->startSending(); -} - -void DuplexChannel::writeRequest(const PubSubRequestPtr& m, const OperationCallbackPtr& callback) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "DuplexChannel::writeRequest channel(" << this << ") txnid(" - << m->txnid() << ") shouldClaim("<< m->has_shouldclaim() << ", " - << m->shouldclaim() << ")"; - } - - { - boost::shared_lock lock(state_lock); - if (state != CONNECTED && state != CONNECTING) { - LOG.errorStream() << "Tried to write transaction [" << m->txnid() << "] to a channel [" - << this << "] which is " << (state == DEAD ? "DEAD" : "UNINITIALISED"); - callback->operationFailed(UninitialisedChannelException()); - } - } - - { - boost::lock_guard lock(write_lock); - WriteRequest w(m, callback); - write_queue.push_back(w); - } - - startSending(); -} - -/** - Store the transaction data for a request. -*/ -void DuplexChannel::storeTransaction(const PubSubDataPtr& data) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Storing txnid(" << data->getTxnId() << ") for channel(" << this << ")"; - } - boost::lock_guard lock(txnid2data_lock); - txnid2data[data->getTxnId()] = data; -} - -/** - Give the transaction back to the caller. -*/ -PubSubDataPtr DuplexChannel::retrieveTransaction(long txnid) { - boost::lock_guard lock(txnid2data_lock); - - PubSubDataPtr data = txnid2data[txnid]; - txnid2data.erase(txnid); - if (data == NULL) { - LOG.errorStream() << "Transaction txnid(" << txnid - << ") doesn't exist in channel (" << this << ")"; - } - - return data; -} - -void DuplexChannel::failAllTransactions() { - boost::lock_guard lock(txnid2data_lock); - for (TransactionMap::iterator iter = txnid2data.begin(); iter != txnid2data.end(); ++iter) { - PubSubDataPtr& data = (*iter).second; - data->getCallback()->operationFailed(ChannelDiedException()); - } - txnid2data.clear(); -} - -void DuplexChannel::setState(State s) { - boost::lock_guard lock(state_lock); - state = s; -} diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/channel.h b/src/contrib/hedwig/client/src/main/cpp/lib/channel.h deleted file mode 100644 index ea780548293..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/channel.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HEDWIG_CHANNEL_H -#define HEDWIG_CHANNEL_H - -#include -#include -#include -#include "util.h" -#include "data.h" -#include "eventdispatcher.h" - -#include -#include - -#include - -#include -#include - -#include -#include -#include - -namespace Hedwig { - class ChannelException : public std::exception { }; - class UninitialisedChannelException : public ChannelException {}; - - class ChannelConnectException : public ChannelException {}; - class CannotCreateSocketException : public ChannelConnectException {}; - class ChannelSetupException : public ChannelConnectException {}; - class ChannelNotConnectedException : public ChannelConnectException {}; - - class ChannelDiedException : public ChannelException {}; - - class ChannelWriteException : public ChannelException {}; - class ChannelReadException : public ChannelException {}; - class ChannelThreadException : public ChannelException {}; - - class DuplexChannel; - typedef boost::shared_ptr DuplexChannelPtr; - - class ChannelHandler { - public: - virtual void messageReceived(const DuplexChannelPtr& channel, const PubSubResponsePtr& m) = 0; - virtual void channelConnected(const DuplexChannelPtr& channel) = 0; - - virtual void channelDisconnected(const DuplexChannelPtr& channel, const std::exception& e) = 0; - virtual void exceptionOccurred(const DuplexChannelPtr& channel, const std::exception& e) = 0; - - virtual ~ChannelHandler() {} - }; - - typedef boost::shared_ptr ChannelHandlerPtr; - - - class DuplexChannel : public boost::enable_shared_from_this { - public: - DuplexChannel(EventDispatcher& dispatcher, const HostAddress& addr, - const Configuration& cfg, const ChannelHandlerPtr& handler); - static void connectCallbackHandler(DuplexChannelPtr channel, - const boost::system::error_code& error); - void connect(); - - static void writeCallbackHandler(DuplexChannelPtr channel, OperationCallbackPtr callback, - const boost::system::error_code& error, - std::size_t bytes_transferred); - void writeRequest(const PubSubRequestPtr& m, const OperationCallbackPtr& callback); - - const HostAddress& getHostAddress() const; - - void storeTransaction(const PubSubDataPtr& data); - PubSubDataPtr retrieveTransaction(long txnid); - void failAllTransactions(); - - static void sizeReadCallbackHandler(DuplexChannelPtr channel, - const boost::system::error_code& error, - std::size_t bytes_transferred); - static void messageReadCallbackHandler(DuplexChannelPtr channel, std::size_t messagesize, - const boost::system::error_code& error, - std::size_t bytes_transferred); - static void readSize(DuplexChannelPtr channel); - - void startReceiving(); - bool isReceiving(); - void stopReceiving(); - - void startSending(); - - void channelDisconnected(const std::exception& e); - virtual void kill(); - - virtual ~DuplexChannel(); - private: - enum State { UNINITIALISED, CONNECTING, CONNECTED, DEAD }; - - void setState(State s); - - EventDispatcher& dispatcher; - - HostAddress address; - ChannelHandlerPtr handler; - - boost::asio::ip::tcp::socket socket; - boost::asio::streambuf in_buf; - std::istream instream; - - // only exists because protobufs can't play nice with streams (if there's more than message len in it, it tries to read all) - char* copy_buf; - std::size_t copy_buf_length; - - boost::asio::streambuf out_buf; - - typedef std::pair WriteRequest; - boost::mutex write_lock; - std::deque write_queue; - - State state; - boost::shared_mutex state_lock; - - bool receiving; - boost::mutex receiving_lock; - - bool sending; - boost::mutex sending_lock; - - typedef std::tr1::unordered_map TransactionMap; - - TransactionMap txnid2data; - boost::mutex txnid2data_lock; - boost::shared_mutex destruction_lock; - }; - - - struct DuplexChannelPtrHash : public std::unary_function { - size_t operator()(const Hedwig::DuplexChannelPtr& channel) const { - return reinterpret_cast(channel.get()); - } - }; -}; -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/client.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/client.cpp deleted file mode 100644 index cd3dbe0165d..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/client.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "clientimpl.h" -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -const std::string Configuration::DEFAULT_SERVER = "hedwig.cpp.default_server"; -const std::string Configuration::MESSAGE_CONSUME_RETRY_WAIT_TIME = "hedwig.cpp.message_consume_retry_wait_time"; -const std::string Configuration::SUBSCRIBER_CONSUME_RETRY_WAIT_TIME = "hedwig.cpp.subscriber_consume_retry_wait_time"; -const std::string Configuration::MAX_MESSAGE_QUEUE_SIZE = "hedwig.cpp.max_msgqueue_size"; -const std::string Configuration::RECONNECT_SUBSCRIBE_RETRY_WAIT_TIME = "hedwig.cpp.reconnect_subscribe_retry_wait_time"; -const std::string Configuration::SYNC_REQUEST_TIMEOUT = "hedwig.cpp.sync_request_timeout"; - -Client::Client(const Configuration& conf) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Client::Client (" << this << ")"; - } - clientimpl = ClientImpl::Create( conf ); -} - -Subscriber& Client::getSubscriber() { - return clientimpl->getSubscriber(); -} - -Publisher& Client::getPublisher() { - return clientimpl->getPublisher(); -} - -Client::~Client() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Client::~Client (" << this << ")"; - } - - clientimpl->Destroy(); -} - - diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.cpp deleted file mode 100644 index 802a5ff166f..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "clientimpl.h" -#include "channel.h" -#include "publisherimpl.h" -#include "subscriberimpl.h" -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -const std::string DEFAULT_SERVER_DEFAULT_VAL = ""; - -void SyncOperationCallback::wait() { - boost::unique_lock lock(mut); - while(response==PENDING) { - if (cond.timed_wait(lock, boost::posix_time::milliseconds(timeout)) == false) { - LOG.errorStream() << "Timeout waiting for operation to complete " << this; - - response = TIMEOUT; - } - } -} - -void SyncOperationCallback::operationComplete() { - if (response == TIMEOUT) { - LOG.errorStream() << "operationCompleted successfully after timeout " << this; - return; - } - - { - boost::lock_guard lock(mut); - response = SUCCESS; - } - cond.notify_all(); -} - -void SyncOperationCallback::operationFailed(const std::exception& exception) { - if (response == TIMEOUT) { - LOG.errorStream() << "operationCompleted unsuccessfully after timeout " << this; - return; - } - - { - boost::lock_guard lock(mut); - - if (typeid(exception) == typeid(ChannelConnectException)) { - response = NOCONNECT; - } else if (typeid(exception) == typeid(ServiceDownException)) { - response = SERVICEDOWN; - } else if (typeid(exception) == typeid(AlreadySubscribedException)) { - response = ALREADY_SUBSCRIBED; - } else if (typeid(exception) == typeid(NotSubscribedException)) { - response = NOT_SUBSCRIBED; - } else { - response = UNKNOWN; - } - } - cond.notify_all(); -} - -void SyncOperationCallback::throwExceptionIfNeeded() { - switch (response) { - case SUCCESS: - break; - case NOCONNECT: - throw CannotConnectException(); - break; - case SERVICEDOWN: - throw ServiceDownException(); - break; - case ALREADY_SUBSCRIBED: - throw AlreadySubscribedException(); - break; - case NOT_SUBSCRIBED: - throw NotSubscribedException(); - break; - case TIMEOUT: - throw ClientTimeoutException(); - break; - default: - throw ClientException(); - break; - } -} - -HedwigClientChannelHandler::HedwigClientChannelHandler(const ClientImplPtr& client) - : client(client){ -} - -void HedwigClientChannelHandler::messageReceived(const DuplexChannelPtr& channel, const PubSubResponsePtr& m) { - LOG.debugStream() << "Message received txnid(" << m->txnid() << ") status(" - << m->statuscode() << ")"; - if (m->has_message()) { - LOG.errorStream() << "Subscription response, ignore for now"; - return; - } - - PubSubDataPtr data = channel->retrieveTransaction(m->txnid()); - /* you now have ownership of data, don't leave this funciton without deleting it or - palming it off to someone else */ - - if (data == NULL) { - return; - } - - if (m->statuscode() == NOT_RESPONSIBLE_FOR_TOPIC) { - client->redirectRequest(channel, data, m); - return; - } - - switch (data->getType()) { - case PUBLISH: - client->getPublisherImpl().messageHandler(m, data); - break; - case SUBSCRIBE: - case UNSUBSCRIBE: - client->getSubscriberImpl().messageHandler(m, data); - break; - default: - LOG.errorStream() << "Unimplemented request type " << data->getType(); - break; - } -} - - -void HedwigClientChannelHandler::channelConnected(const DuplexChannelPtr& channel) { - // do nothing -} - -void HedwigClientChannelHandler::channelDisconnected(const DuplexChannelPtr& channel, const std::exception& e) { - LOG.errorStream() << "Channel disconnected"; - - client->channelDied(channel); -} - -void HedwigClientChannelHandler::exceptionOccurred(const DuplexChannelPtr& channel, const std::exception& e) { - LOG.errorStream() << "Exception occurred" << e.what(); -} - -ClientTxnCounter::ClientTxnCounter() : counter(0) -{ -} - -ClientTxnCounter::~ClientTxnCounter() { -} - -/** -Increment the transaction counter and return the new value. - -@returns the next transaction id -*/ -long ClientTxnCounter::next() { // would be nice to remove lock from here, look more into it - boost::lock_guard lock(mutex); - - long next= ++counter; - - return next; -} - -ClientImplPtr ClientImpl::Create(const Configuration& conf) { - ClientImplPtr impl(new ClientImpl(conf)); - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating Clientimpl " << impl; - } - impl->dispatcher.start(); - - return impl; -} - -void ClientImpl::Destroy() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "destroying Clientimpl " << this; - } - - dispatcher.stop(); - { - boost::lock_guard lock(allchannels_lock); - - shuttingDownFlag = true; - for (ChannelMap::iterator iter = allchannels.begin(); iter != allchannels.end(); ++iter ) { - (*iter)->kill(); - } - allchannels.clear(); - } - - /* destruction of the maps will clean up any items they hold */ - - if (subscriber != NULL) { - delete subscriber; - subscriber = NULL; - } - if (publisher != NULL) { - delete publisher; - publisher = NULL; - } -} - -ClientImpl::ClientImpl(const Configuration& conf) - : conf(conf), publisher(NULL), subscriber(NULL), counterobj(), shuttingDownFlag(false) -{ -} - -Subscriber& ClientImpl::getSubscriber() { - return getSubscriberImpl(); -} - -Publisher& ClientImpl::getPublisher() { - return getPublisherImpl(); -} - -SubscriberImpl& ClientImpl::getSubscriberImpl() { - if (subscriber == NULL) { - boost::lock_guard lock(subscribercreate_lock); - if (subscriber == NULL) { - subscriber = new SubscriberImpl(shared_from_this()); - } - } - return *subscriber; -} - -PublisherImpl& ClientImpl::getPublisherImpl() { - if (publisher == NULL) { - boost::lock_guard lock(publishercreate_lock); - if (publisher == NULL) { - publisher = new PublisherImpl(shared_from_this()); - } - } - return *publisher; -} - -ClientTxnCounter& ClientImpl::counter() { - return counterobj; -} - -void ClientImpl::redirectRequest(const DuplexChannelPtr& channel, PubSubDataPtr& data, const PubSubResponsePtr& response) { - HostAddress oldhost = channel->getHostAddress(); - data->addTriedServer(oldhost); - - HostAddress h = HostAddress::fromString(response->statusmsg()); - if (data->hasTriedServer(h)) { - LOG.errorStream() << "We've been told to try request [" << data->getTxnId() << "] with [" - << h.getAddressString()<< "] by " << oldhost.getAddressString() - << " but we've already tried that. Failing operation"; - data->getCallback()->operationFailed(InvalidRedirectException()); - return; - } - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "We've been told [" << data->getTopic() << "] is on [" << h.getAddressString() - << "] by [" << oldhost.getAddressString() << "]. Redirecting request " << data->getTxnId(); - } - data->setShouldClaim(true); - - setHostForTopic(data->getTopic(), h); - DuplexChannelPtr newchannel; - try { - if (data->getType() == SUBSCRIBE) { - SubscriberClientChannelHandlerPtr handler(new SubscriberClientChannelHandler(shared_from_this(), - this->getSubscriberImpl(), data)); - newchannel = createChannel(data->getTopic(), handler); - handler->setChannel(newchannel); - getSubscriberImpl().doSubscribe(newchannel, data, handler); - } else if (data->getType() == PUBLISH) { - newchannel = getChannel(data->getTopic()); - getPublisherImpl().doPublish(newchannel, data); - } else { - newchannel = getChannel(data->getTopic()); - getSubscriberImpl().doUnsubscribe(newchannel, data); - } - } catch (ShuttingDownException& e) { - return; // no point in redirecting if we're shutting down - } -} - -ClientImpl::~ClientImpl() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "deleting Clientimpl " << this; - } -} - -DuplexChannelPtr ClientImpl::createChannel(const std::string& topic, const ChannelHandlerPtr& handler) { - // get the host address - // create a channel to the host - HostAddress addr = topic2host[topic]; - if (addr.isNullHost()) { - addr = HostAddress::fromString(conf.get(Configuration::DEFAULT_SERVER, DEFAULT_SERVER_DEFAULT_VAL)); - setHostForTopic(topic, addr); - } - - DuplexChannelPtr channel(new DuplexChannel(dispatcher, addr, conf, handler)); - - boost::lock_guard lock(allchannels_lock); - if (shuttingDownFlag) { - channel->kill(); - throw ShuttingDownException(); - } - channel->connect(); - - allchannels.insert(channel); - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "(create) All channels size: " << allchannels.size(); - } - - return channel; -} - -DuplexChannelPtr ClientImpl::getChannel(const std::string& topic) { - HostAddress addr = topic2host[topic]; - if (addr.isNullHost()) { - addr = HostAddress::fromString(conf.get(Configuration::DEFAULT_SERVER, DEFAULT_SERVER_DEFAULT_VAL)); - setHostForTopic(topic, addr); - } - DuplexChannelPtr channel = host2channel[addr]; - - if (channel.get() == 0) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << " No channel for topic, creating new channel.get() " << channel.get() << " addr " << addr.getAddressString(); - } - ChannelHandlerPtr handler(new HedwigClientChannelHandler(shared_from_this())); - channel = createChannel(topic, handler); - - boost::lock_guard lock(host2channel_lock); - host2channel[addr] = channel; - } - - return channel; -} - -void ClientImpl::setHostForTopic(const std::string& topic, const HostAddress& host) { - boost::lock_guard lock(topic2host_lock); - topic2host[topic] = host; -} - -bool ClientImpl::shuttingDown() const { - return shuttingDownFlag; -} - -/** - A channel has just died. Remove it so we never give it to any other publisher or subscriber. - - This does not delete the channel. Some publishers or subscribers will still hold it and will be errored - when they try to do anything with it. -*/ -void ClientImpl::channelDied(const DuplexChannelPtr& channel) { - if (shuttingDownFlag) { - return; - } - - boost::lock_guard h2tlock(host2topics_lock); - boost::lock_guard h2clock(host2channel_lock); - boost::lock_guard t2hlock(topic2host_lock); - boost::lock_guard aclock(allchannels_lock); - // get host - HostAddress addr = channel->getHostAddress(); - - for (Host2TopicsMap::iterator iter = host2topics.find(addr); iter != host2topics.end(); ++iter) { - topic2host.erase((*iter).second); - } - host2topics.erase(addr); - host2channel.erase(addr); - - allchannels.erase(channel); // channel should be deleted here -} - -const Configuration& ClientImpl::getConfiguration() { - return conf; -} - -boost::asio::io_service& ClientImpl::getService() { - return dispatcher.getService(); -} diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.h b/src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.h deleted file mode 100644 index 22451b0dd6f..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/clientimpl.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef HEDWIG_CLIENT_IMPL_H -#define HEDWIG_CLIENT_IMPL_H - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "util.h" -#include "channel.h" -#include "data.h" -#include "eventdispatcher.h" - -namespace Hedwig { - const int DEFAULT_SYNC_REQUEST_TIMEOUT = 5000; - - class SyncOperationCallback : public OperationCallback { - public: - SyncOperationCallback(int timeout) : response(PENDING), timeout(timeout) {} - virtual void operationComplete(); - virtual void operationFailed(const std::exception& exception); - - void wait(); - void throwExceptionIfNeeded(); - - private: - enum { - PENDING, - SUCCESS, - NOCONNECT, - SERVICEDOWN, - NOT_SUBSCRIBED, - ALREADY_SUBSCRIBED, - TIMEOUT, - UNKNOWN - } response; - - boost::condition_variable cond; - boost::mutex mut; - int timeout; - }; - - class HedwigClientChannelHandler : public ChannelHandler { - public: - HedwigClientChannelHandler(const ClientImplPtr& client); - - virtual void messageReceived(const DuplexChannelPtr& channel, const PubSubResponsePtr& m); - virtual void channelConnected(const DuplexChannelPtr& channel); - virtual void channelDisconnected(const DuplexChannelPtr& channel, const std::exception& e); - virtual void exceptionOccurred(const DuplexChannelPtr& channel, const std::exception& e); - - protected: - const ClientImplPtr client; - }; - - class PublisherImpl; - class SubscriberImpl; - - /** - Implementation of the hedwig client. This class takes care of globals such as the topic->host map and the transaction id counter. - */ - class ClientImpl : public boost::enable_shared_from_this { - public: - static ClientImplPtr Create(const Configuration& conf); - void Destroy(); - - Subscriber& getSubscriber(); - Publisher& getPublisher(); - - ClientTxnCounter& counter(); - - void redirectRequest(const DuplexChannelPtr& channel, PubSubDataPtr& data, const PubSubResponsePtr& response); - - const HostAddress& getHostForTopic(const std::string& topic); - - //DuplexChannelPtr getChannelForTopic(const std::string& topic, OperationCallback& callback); - //DuplexChannelPtr createChannelForTopic(const std::string& topic, ChannelHandlerPtr& handler, OperationCallback& callback); - DuplexChannelPtr createChannel(const std::string& topic, const ChannelHandlerPtr& handler); - DuplexChannelPtr getChannel(const std::string& topic); - - void setHostForTopic(const std::string& topic, const HostAddress& host); - - void setChannelForHost(const HostAddress& address, const DuplexChannelPtr& channel); - void channelDied(const DuplexChannelPtr& channel); - bool shuttingDown() const; - - SubscriberImpl& getSubscriberImpl(); - PublisherImpl& getPublisherImpl(); - - const Configuration& getConfiguration(); - boost::asio::io_service& getService(); - - ~ClientImpl(); - private: - ClientImpl(const Configuration& conf); - - const Configuration& conf; - - boost::mutex publishercreate_lock; - PublisherImpl* publisher; - - boost::mutex subscribercreate_lock; - SubscriberImpl* subscriber; - - ClientTxnCounter counterobj; - - EventDispatcher dispatcher; - - typedef std::tr1::unordered_multimap Host2TopicsMap; - Host2TopicsMap host2topics; - boost::shared_mutex host2topics_lock; - - std::tr1::unordered_map host2channel; - boost::shared_mutex host2channel_lock; - std::tr1::unordered_map topic2host; - boost::shared_mutex topic2host_lock; - - typedef std::tr1::unordered_set ChannelMap; - ChannelMap allchannels; - boost::shared_mutex allchannels_lock; - - bool shuttingDownFlag; - }; -}; -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/data.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/data.cpp deleted file mode 100644 index b9224e9e962..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/data.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "data.h" - -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -PubSubDataPtr PubSubData::forPublishRequest(long txnid, const std::string& topic, const std::string& body, const OperationCallbackPtr& callback) { - PubSubDataPtr ptr(new PubSubData()); - ptr->type = PUBLISH; - ptr->txnid = txnid; - ptr->topic = topic; - ptr->body = body; - ptr->callback = callback; - return ptr; -} - -PubSubDataPtr PubSubData::forSubscribeRequest(long txnid, const std::string& subscriberid, const std::string& topic, const OperationCallbackPtr& callback, SubscribeRequest::CreateOrAttach mode) { - PubSubDataPtr ptr(new PubSubData()); - ptr->type = SUBSCRIBE; - ptr->txnid = txnid; - ptr->subscriberid = subscriberid; - ptr->topic = topic; - ptr->callback = callback; - ptr->mode = mode; - return ptr; -} - -PubSubDataPtr PubSubData::forUnsubscribeRequest(long txnid, const std::string& subscriberid, const std::string& topic, const OperationCallbackPtr& callback) { - PubSubDataPtr ptr(new PubSubData()); - ptr->type = UNSUBSCRIBE; - ptr->txnid = txnid; - ptr->subscriberid = subscriberid; - ptr->topic = topic; - ptr->callback = callback; - return ptr; -} - -PubSubDataPtr PubSubData::forConsumeRequest(long txnid, const std::string& subscriberid, const std::string& topic, const MessageSeqId msgid) { - PubSubDataPtr ptr(new PubSubData()); - ptr->type = CONSUME; - ptr->txnid = txnid; - ptr->subscriberid = subscriberid; - ptr->topic = topic; - ptr->msgid = msgid; - return ptr; -} - -PubSubData::PubSubData() : shouldClaim(false) { -} - -PubSubData::~PubSubData() { -} - -OperationType PubSubData::getType() const { - return type; -} - -long PubSubData::getTxnId() const { - return txnid; -} - -const std::string& PubSubData::getTopic() const { - return topic; -} - -const std::string& PubSubData::getBody() const { - return body; -} - -const MessageSeqId PubSubData::getMessageSeqId() const { - return msgid; -} - -const PubSubRequestPtr PubSubData::getRequest() { - PubSubRequestPtr request(new Hedwig::PubSubRequest()); - request->set_protocolversion(Hedwig::VERSION_ONE); - request->set_type(type); - request->set_txnid(txnid); - if (shouldClaim) { - request->set_shouldclaim(shouldClaim); - } - request->set_topic(topic); - - if (type == PUBLISH) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating publish request"; - } - Hedwig::PublishRequest* pubreq = request->mutable_publishrequest(); - Hedwig::Message* msg = pubreq->mutable_msg(); - msg->set_body(body); - } else if (type == SUBSCRIBE) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating subscribe request"; - } - - Hedwig::SubscribeRequest* subreq = request->mutable_subscriberequest(); - subreq->set_subscriberid(subscriberid); - subreq->set_createorattach(mode); - } else if (type == CONSUME) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating consume request"; - } - - Hedwig::ConsumeRequest* conreq = request->mutable_consumerequest(); - conreq->set_subscriberid(subscriberid); - conreq->mutable_msgid()->CopyFrom(msgid); - } else if (type == UNSUBSCRIBE) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating unsubscribe request"; - } - - Hedwig::UnsubscribeRequest* unsubreq = request->mutable_unsubscriberequest(); - unsubreq->set_subscriberid(subscriberid); - } else { - LOG.errorStream() << "Tried to create a request message for the wrong type [" << type << "]"; - throw UnknownRequestException(); - } - - return request; -} - -void PubSubData::setShouldClaim(bool shouldClaim) { - this->shouldClaim = shouldClaim; -} - -void PubSubData::addTriedServer(HostAddress& h) { - triedservers.insert(h); -} - -bool PubSubData::hasTriedServer(HostAddress& h) { - return triedservers.count(h) > 0; -} - -void PubSubData::clearTriedServers() { - triedservers.clear(); -} - -OperationCallbackPtr& PubSubData::getCallback() { - return callback; -} - -void PubSubData::setCallback(const OperationCallbackPtr& callback) { - this->callback = callback; -} - -const std::string& PubSubData::getSubscriberId() const { - return subscriberid; -} - -SubscribeRequest::CreateOrAttach PubSubData::getMode() const { - return mode; -} diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/data.h b/src/contrib/hedwig/client/src/main/cpp/lib/data.h deleted file mode 100644 index 3d5fe5fc35d..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/data.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef DATA_H -#define DATA_H - -#include -#include - -#include -#include -#include "util.h" -#include -#include - -namespace Hedwig { - /** - Simple counter for transaction ids from the client - */ - class ClientTxnCounter { - public: - ClientTxnCounter(); - ~ClientTxnCounter(); - long next(); - - private: - long counter; - boost::mutex mutex; - }; - - class PubSubData; - typedef boost::shared_ptr PubSubDataPtr; - typedef boost::shared_ptr PubSubRequestPtr; - typedef boost::shared_ptr PubSubResponsePtr; - - /** - Data structure to hold information about requests and build request messages. - Used to store requests which may need to be resent to another server. - */ - class PubSubData { - public: - // to be used for publish - static PubSubDataPtr forPublishRequest(long txnid, const std::string& topic, const std::string& body, const OperationCallbackPtr& callback); - static PubSubDataPtr forSubscribeRequest(long txnid, const std::string& subscriberid, const std::string& topic, const OperationCallbackPtr& callback, SubscribeRequest::CreateOrAttach mode); - static PubSubDataPtr forUnsubscribeRequest(long txnid, const std::string& subscriberid, const std::string& topic, const OperationCallbackPtr& callback); - static PubSubDataPtr forConsumeRequest(long txnid, const std::string& subscriberid, const std::string& topic, const MessageSeqId msgid); - - ~PubSubData(); - - OperationType getType() const; - long getTxnId() const; - const std::string& getSubscriberId() const; - const std::string& getTopic() const; - const std::string& getBody() const; - const MessageSeqId getMessageSeqId() const; - - void setShouldClaim(bool shouldClaim); - - const PubSubRequestPtr getRequest(); - void setCallback(const OperationCallbackPtr& callback); - OperationCallbackPtr& getCallback(); - SubscribeRequest::CreateOrAttach getMode() const; - - void addTriedServer(HostAddress& h); - bool hasTriedServer(HostAddress& h); - void clearTriedServers(); - private: - - PubSubData(); - - OperationType type; - long txnid; - std::string subscriberid; - std::string topic; - std::string body; - bool shouldClaim; - OperationCallbackPtr callback; - SubscribeRequest::CreateOrAttach mode; - MessageSeqId msgid; - std::tr1::unordered_set triedservers; - }; - -}; -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.cpp deleted file mode 100644 index 09a9804ff67..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "eventdispatcher.h" - -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -EventDispatcher::EventDispatcher() : service(), dummy_work(NULL), t(NULL) { -} - -void EventDispatcher::run_forever() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Starting event dispatcher"; - } - - while (true) { - try { - service.run(); - break; - } catch (std::exception &e) { - LOG.errorStream() << "Exception in dispatch handler. " << e.what(); - } - } - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Event dispatcher done"; - } -} - -void EventDispatcher::start() { - if (t) { - return; - } - dummy_work = new boost::asio::io_service::work(service); - t = new boost::thread(boost::bind(&EventDispatcher::run_forever, this)); -} - -void EventDispatcher::stop() { - if (!t) { - return; - } - delete dummy_work; - dummy_work = NULL; - - service.stop(); - - t->join(); - delete t; - t = NULL; -} - -EventDispatcher::~EventDispatcher() { - delete dummy_work; -} - -boost::asio::io_service& EventDispatcher::getService() { - return service; -} diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.h b/src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.h deleted file mode 100644 index 192dbf5f21a..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/eventdispatcher.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EVENTDISPATCHER_H -#define EVENTDISPATCHER_H - -#include -#include - -namespace Hedwig { - class EventDispatcher { - public: - EventDispatcher(); - ~EventDispatcher(); - - void start(); - void stop(); - - boost::asio::io_service& getService(); - - private: - void run_forever(); - - boost::asio::io_service service; - boost::asio::io_service::work* dummy_work; - boost::thread* t; - }; -} - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/exceptions.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/exceptions.cpp deleted file mode 100644 index b46178a9013..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/exceptions.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -using namespace Hedwig; - - - - diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.cpp deleted file mode 100644 index c3cbe3377e6..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.cpp +++ /dev/null @@ -1,85 +0,0 @@ - /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "publisherimpl.h" -#include "channel.h" - -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -PublishWriteCallback::PublishWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data) : client(client), data(data) {} - -void PublishWriteCallback::operationComplete() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Successfully wrote transaction: " << data->getTxnId(); - } -} - -void PublishWriteCallback::operationFailed(const std::exception& exception) { - LOG.errorStream() << "Error writing to publisher " << exception.what(); - - data->getCallback()->operationFailed(exception); -} - -PublisherImpl::PublisherImpl(const ClientImplPtr& client) - : client(client) { -} - -void PublisherImpl::publish(const std::string& topic, const std::string& message) { - SyncOperationCallback* cb = new SyncOperationCallback(client->getConfiguration().getInt(Configuration::SYNC_REQUEST_TIMEOUT, - DEFAULT_SYNC_REQUEST_TIMEOUT)); - OperationCallbackPtr callback(cb); - asyncPublish(topic, message, callback); - cb->wait(); - - cb->throwExceptionIfNeeded(); -} - -void PublisherImpl::asyncPublish(const std::string& topic, const std::string& message, const OperationCallbackPtr& callback) { - // use release after callback to release the channel after the callback is called - PubSubDataPtr data = PubSubData::forPublishRequest(client->counter().next(), topic, message, callback); - - DuplexChannelPtr channel = client->getChannel(topic); - doPublish(channel, data); -} - -void PublisherImpl::doPublish(const DuplexChannelPtr& channel, const PubSubDataPtr& data) { - channel->storeTransaction(data); - - OperationCallbackPtr writecb(new PublishWriteCallback(client, data)); - channel->writeRequest(data->getRequest(), writecb); -} - -void PublisherImpl::messageHandler(const PubSubResponsePtr& m, const PubSubDataPtr& txn) { - switch (m->statuscode()) { - case SUCCESS: - txn->getCallback()->operationComplete(); - break; - case SERVICE_DOWN: - LOG.errorStream() << "Server responsed with SERVICE_DOWN for " << txn->getTxnId(); - txn->getCallback()->operationFailed(ServiceDownException()); - break; - default: - LOG.errorStream() << "Unexpected response " << m->statuscode() << " for " << txn->getTxnId(); - txn->getCallback()->operationFailed(UnexpectedResponseException()); - break; - } -} diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.h b/src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.h deleted file mode 100644 index 1620ab9099b..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/publisherimpl.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef PUBLISHER_IMPL_H -#define PUBLISHER_IMPL_H - -#include -#include -#include "clientimpl.h" - -namespace Hedwig { - class PublishWriteCallback : public OperationCallback { - public: - PublishWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data); - - void operationComplete(); - void operationFailed(const std::exception& exception); - private: - ClientImplPtr client; - PubSubDataPtr data; - }; - - class PublisherImpl : public Publisher { - public: - PublisherImpl(const ClientImplPtr& client); - - void publish(const std::string& topic, const std::string& message); - void asyncPublish(const std::string& topic, const std::string& message, const OperationCallbackPtr& callback); - - void messageHandler(const PubSubResponsePtr& m, const PubSubDataPtr& txn); - - void doPublish(const DuplexChannelPtr& channel, const PubSubDataPtr& data); - - private: - ClientImplPtr client; - }; - -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.cpp deleted file mode 100644 index 2efb9723a39..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "subscriberimpl.h" -#include "util.h" -#include "channel.h" - -#include -#include - -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; -const int DEFAULT_MESSAGE_CONSUME_RETRY_WAIT_TIME = 5000; -const int DEFAULT_SUBSCRIBER_CONSUME_RETRY_WAIT_TIME = 5000; -const int DEFAULT_MAX_MESSAGE_QUEUE_SIZE = 10; -const int DEFAULT_RECONNECT_SUBSCRIBE_RETRY_WAIT_TIME = 5000; - -SubscriberWriteCallback::SubscriberWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data) : client(client), data(data) {} - -void SubscriberWriteCallback::operationComplete() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Successfully wrote subscribe transaction: " << data->getTxnId(); - } -} - -void SubscriberWriteCallback::operationFailed(const std::exception& exception) { - LOG.errorStream() << "Error writing to subscriber " << exception.what(); - - //remove txn from channel pending list - data->getCallback()->operationFailed(exception); - client->getSubscriberImpl().closeSubscription(data->getTopic(), data->getSubscriberId()); -} - -UnsubscribeWriteCallback::UnsubscribeWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data) : client(client), data(data) {} - -void UnsubscribeWriteCallback::operationComplete() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Successfully wrote unsubscribe transaction: " << data->getTxnId(); - } -} - -void UnsubscribeWriteCallback::operationFailed(const std::exception& exception) { - data->getCallback()->operationFailed(exception); -} - -ConsumeWriteCallback::ConsumeWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data) - : client(client), data(data) { -} - -ConsumeWriteCallback::~ConsumeWriteCallback() { -} - -/* static */ void ConsumeWriteCallback::timerComplete(const ClientImplPtr& client, const PubSubDataPtr& data, - const boost::system::error_code& error) { - if (error) { - // shutting down - return; - } - - client->getSubscriberImpl().consume(data->getTopic(), data->getSubscriberId(), data->getMessageSeqId()); -} - - -void ConsumeWriteCallback::operationComplete() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Successfully wrote consume transaction: " << data->getTxnId(); - } -} - -void ConsumeWriteCallback::operationFailed(const std::exception& exception) { - int retrywait = client->getConfiguration().getInt(Configuration::MESSAGE_CONSUME_RETRY_WAIT_TIME, - DEFAULT_MESSAGE_CONSUME_RETRY_WAIT_TIME); - LOG.errorStream() << "Error writing consume transaction: " << data->getTxnId() << " error: " << exception.what() - << " retrying in " << retrywait << " Microseconds"; - - boost::asio::deadline_timer t(client->getService(), boost::posix_time::milliseconds(retrywait)); - - t.async_wait(boost::bind(&ConsumeWriteCallback::timerComplete, client, data, boost::asio::placeholders::error)); -} - -SubscriberConsumeCallback::SubscriberConsumeCallback(const ClientImplPtr& client, - const SubscriberClientChannelHandlerPtr& handler, - const PubSubDataPtr& data, const PubSubResponsePtr& m) - : client(client), handler(handler), data(data), m(m) -{ -} - -void SubscriberConsumeCallback::operationComplete() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "ConsumeCallback::operationComplete " << data->getTopic() << " - " << data->getSubscriberId(); - }; - client->getSubscriber().consume(data->getTopic(), data->getSubscriberId(), m->message().msgid()); -} - -/* static */ void SubscriberConsumeCallback::timerComplete(const SubscriberClientChannelHandlerPtr handler, - const PubSubResponsePtr m, - const boost::system::error_code& error) { - if (error) { - return; - } - handler->messageReceived(handler->getChannel(), m); -} - -void SubscriberConsumeCallback::operationFailed(const std::exception& exception) { - LOG.errorStream() << "ConsumeCallback::operationFailed " << data->getTopic() << " - " << data->getSubscriberId(); - - int retrywait = client->getConfiguration().getInt(Configuration::SUBSCRIBER_CONSUME_RETRY_WAIT_TIME, - DEFAULT_SUBSCRIBER_CONSUME_RETRY_WAIT_TIME); - - LOG.errorStream() << "Error passing message to client transaction: " << data->getTxnId() << " error: " << exception.what() - << " retrying in " << retrywait << " Microseconds"; - - boost::asio::deadline_timer t(client->getService(), boost::posix_time::milliseconds(retrywait)); - - t.async_wait(boost::bind(&SubscriberConsumeCallback::timerComplete, handler, m, boost::asio::placeholders::error)); -} - -SubscriberReconnectCallback::SubscriberReconnectCallback(const ClientImplPtr& client, const PubSubDataPtr& origData) - : client(client), origData(origData) { -} - -void SubscriberReconnectCallback::operationComplete() { -} - -void SubscriberReconnectCallback::operationFailed(const std::exception& exception) { - LOG.errorStream() << "Error writing to new subscriber. Channel should pick this up disconnect the channel and try to connect again " << exception.what(); - - -} - -SubscriberClientChannelHandler::SubscriberClientChannelHandler(const ClientImplPtr& client, SubscriberImpl& subscriber, const PubSubDataPtr& data) - : HedwigClientChannelHandler(client), subscriber(subscriber), origData(data), closed(false), should_wait(true) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Creating SubscriberClientChannelHandler " << this; - } -} - -SubscriberClientChannelHandler::~SubscriberClientChannelHandler() { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Cleaning up SubscriberClientChannelHandler " << this; - } -} - -void SubscriberClientChannelHandler::messageReceived(const DuplexChannelPtr& channel, const PubSubResponsePtr& m) { - if (m->has_message()) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Message received (topic:" << origData->getTopic() << ", subscriberId:" << origData->getSubscriberId() << ")"; - } - - if (this->handler.get()) { - OperationCallbackPtr callback(new SubscriberConsumeCallback(client, shared_from_this(), origData, m)); - this->handler->consume(origData->getTopic(), origData->getSubscriberId(), m->message(), callback); - } else { - queue.push_back(m); - if (queue.size() >= (std::size_t)client->getConfiguration().getInt(Configuration::MAX_MESSAGE_QUEUE_SIZE, - DEFAULT_MAX_MESSAGE_QUEUE_SIZE)) { - channel->stopReceiving(); - } - } - } else { - HedwigClientChannelHandler::messageReceived(channel, m); - } -} - -void SubscriberClientChannelHandler::close() { - closed = true; - - if (channel) { - channel->kill(); - } -} - -/*static*/ void SubscriberClientChannelHandler::reconnectTimerComplete(const SubscriberClientChannelHandlerPtr handler, - const DuplexChannelPtr channel, const std::exception e, - const boost::system::error_code& error) { - if (error) { - return; - } - handler->should_wait = false; - handler->channelDisconnected(channel, e); -} - -void SubscriberClientChannelHandler::channelDisconnected(const DuplexChannelPtr& channel, const std::exception& e) { - // has subscription been closed - if (closed) { - return; - } - - // Clean up the channel from all maps - client->channelDied(channel); - if (client->shuttingDown()) { - return; - } - - if (should_wait) { - int retrywait = client->getConfiguration().getInt(Configuration::RECONNECT_SUBSCRIBE_RETRY_WAIT_TIME, - DEFAULT_RECONNECT_SUBSCRIBE_RETRY_WAIT_TIME); - - boost::asio::deadline_timer t(client->getService(), boost::posix_time::milliseconds(retrywait)); - t.async_wait(boost::bind(&SubscriberClientChannelHandler::reconnectTimerComplete, shared_from_this(), - channel, e, boost::asio::placeholders::error)); - - } - should_wait = true; - - // setup pubsub data for reconnection attempt - origData->clearTriedServers(); - OperationCallbackPtr newcallback(new SubscriberReconnectCallback(client, origData)); - origData->setCallback(newcallback); - - // Create a new handler for the new channel - SubscriberClientChannelHandlerPtr newhandler(new SubscriberClientChannelHandler(client, subscriber, origData)); - ChannelHandlerPtr baseptr = newhandler; - - DuplexChannelPtr newchannel = client->createChannel(origData->getTopic(), baseptr); - newhandler->setChannel(newchannel); - handoverDelivery(newhandler); - - // remove record of the failed channel from the subscriber - client->getSubscriberImpl().closeSubscription(origData->getTopic(), origData->getSubscriberId()); - - // subscriber - client->getSubscriberImpl().doSubscribe(newchannel, origData, newhandler); -} - -void SubscriberClientChannelHandler::startDelivery(const MessageHandlerCallbackPtr& handler) { - this->handler = handler; - - while (!queue.empty()) { - PubSubResponsePtr m = queue.front(); - queue.pop_front(); - - OperationCallbackPtr callback(new SubscriberConsumeCallback(client, shared_from_this(), origData, m)); - - this->handler->consume(origData->getTopic(), origData->getSubscriberId(), m->message(), callback); - } - channel->startReceiving(); -} - -void SubscriberClientChannelHandler::stopDelivery() { - channel->stopReceiving(); - - this->handler = MessageHandlerCallbackPtr(); -} - - -void SubscriberClientChannelHandler::handoverDelivery(const SubscriberClientChannelHandlerPtr& newHandler) { - LOG.debugStream() << "Messages in queue " << queue.size(); - MessageHandlerCallbackPtr handler = this->handler; - stopDelivery(); // resets old handler - newHandler->startDelivery(handler); -} - -void SubscriberClientChannelHandler::setChannel(const DuplexChannelPtr& channel) { - this->channel = channel; -} - -DuplexChannelPtr& SubscriberClientChannelHandler::getChannel() { - return channel; -} - -SubscriberImpl::SubscriberImpl(const ClientImplPtr& client) - : client(client) -{ -} - -SubscriberImpl::~SubscriberImpl() -{ - LOG.debugStream() << "deleting subscriber" << this; -} - - -void SubscriberImpl::subscribe(const std::string& topic, const std::string& subscriberId, const SubscribeRequest::CreateOrAttach mode) { - SyncOperationCallback* cb = new SyncOperationCallback(client->getConfiguration().getInt(Configuration::SYNC_REQUEST_TIMEOUT, - DEFAULT_SYNC_REQUEST_TIMEOUT)); - OperationCallbackPtr callback(cb); - asyncSubscribe(topic, subscriberId, mode, callback); - cb->wait(); - - cb->throwExceptionIfNeeded(); -} - -void SubscriberImpl::asyncSubscribe(const std::string& topic, const std::string& subscriberId, const SubscribeRequest::CreateOrAttach mode, const OperationCallbackPtr& callback) { - PubSubDataPtr data = PubSubData::forSubscribeRequest(client->counter().next(), subscriberId, topic, callback, mode); - - SubscriberClientChannelHandlerPtr handler(new SubscriberClientChannelHandler(client, *this, data)); - ChannelHandlerPtr baseptr = handler; - - DuplexChannelPtr channel = client->createChannel(topic, handler); - handler->setChannel(channel); - doSubscribe(channel, data, handler); -} - -void SubscriberImpl::doSubscribe(const DuplexChannelPtr& channel, const PubSubDataPtr& data, const SubscriberClientChannelHandlerPtr& handler) { - channel->storeTransaction(data); - - OperationCallbackPtr writecb(new SubscriberWriteCallback(client, data)); - channel->writeRequest(data->getRequest(), writecb); - - boost::lock_guard lock(topicsubscriber2handler_lock); - TopicSubscriber t(data->getTopic(), data->getSubscriberId()); - SubscriberClientChannelHandlerPtr oldhandler = topicsubscriber2handler[t]; - if (oldhandler != NULL) { - oldhandler->handoverDelivery(handler); - } - topicsubscriber2handler[t] = handler; - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "Set topic subscriber for topic(" << data->getTopic() << ") subscriberId(" << data->getSubscriberId() << ") to " << handler.get() << " topicsubscriber2topic(" << &topicsubscriber2handler << ")"; - } -} - -void SubscriberImpl::unsubscribe(const std::string& topic, const std::string& subscriberId) { - SyncOperationCallback* cb = new SyncOperationCallback(client->getConfiguration().getInt(Configuration::SYNC_REQUEST_TIMEOUT, - DEFAULT_SYNC_REQUEST_TIMEOUT)); - OperationCallbackPtr callback(cb); - asyncUnsubscribe(topic, subscriberId, callback); - cb->wait(); - - cb->throwExceptionIfNeeded(); -} - -void SubscriberImpl::asyncUnsubscribe(const std::string& topic, const std::string& subscriberId, const OperationCallbackPtr& callback) { - closeSubscription(topic, subscriberId); - - PubSubDataPtr data = PubSubData::forUnsubscribeRequest(client->counter().next(), subscriberId, topic, callback); - - DuplexChannelPtr channel = client->getChannel(topic); - doUnsubscribe(channel, data); -} - -void SubscriberImpl::doUnsubscribe(const DuplexChannelPtr& channel, const PubSubDataPtr& data) { - channel->storeTransaction(data); - OperationCallbackPtr writecb(new UnsubscribeWriteCallback(client, data)); - channel->writeRequest(data->getRequest(), writecb); -} - -void SubscriberImpl::consume(const std::string& topic, const std::string& subscriberId, const MessageSeqId& messageSeqId) { - TopicSubscriber t(topic, subscriberId); - - boost::shared_lock lock(topicsubscriber2handler_lock); - SubscriberClientChannelHandlerPtr handler = topicsubscriber2handler[t]; - - if (handler.get() == 0) { - LOG.errorStream() << "Cannot consume. Bad handler for topic(" << topic << ") subscriberId(" << subscriberId << ") topicsubscriber2topic(" << &topicsubscriber2handler << ")"; - return; - } - - DuplexChannelPtr channel = handler->getChannel(); - if (channel.get() == 0) { - LOG.errorStream() << "Trying to consume a message on a topic/subscriber pair that don't have a channel. Something fishy going on. Topic: " << topic << " SubscriberId: " << subscriberId << " MessageSeqId: " << messageSeqId.localcomponent(); - } - - PubSubDataPtr data = PubSubData::forConsumeRequest(client->counter().next(), subscriberId, topic, messageSeqId); - OperationCallbackPtr writecb(new ConsumeWriteCallback(client, data)); - channel->writeRequest(data->getRequest(), writecb); -} - -void SubscriberImpl::startDelivery(const std::string& topic, const std::string& subscriberId, const MessageHandlerCallbackPtr& callback) { - TopicSubscriber t(topic, subscriberId); - - boost::shared_lock lock(topicsubscriber2handler_lock); - SubscriberClientChannelHandlerPtr handler = topicsubscriber2handler[t]; - - if (handler.get() == 0) { - LOG.errorStream() << "Trying to start deliver on a non existant handler topic = " << topic << ", subscriber = " << subscriberId; - } - handler->startDelivery(callback); -} - -void SubscriberImpl::stopDelivery(const std::string& topic, const std::string& subscriberId) { - TopicSubscriber t(topic, subscriberId); - - boost::shared_lock lock(topicsubscriber2handler_lock); - SubscriberClientChannelHandlerPtr handler = topicsubscriber2handler[t]; - - if (handler.get() == 0) { - LOG.errorStream() << "Trying to start deliver on a non existant handler topic = " << topic << ", subscriber = " << subscriberId; - } - handler->stopDelivery(); -} - -void SubscriberImpl::closeSubscription(const std::string& topic, const std::string& subscriberId) { - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "closeSubscription (" << topic << ", " << subscriberId << ")"; - } - TopicSubscriber t(topic, subscriberId); - - SubscriberClientChannelHandlerPtr handler; - { - boost::lock_guard lock(topicsubscriber2handler_lock); - handler = topicsubscriber2handler[t]; - topicsubscriber2handler.erase(t); - } - - if (handler.get() != 0) { - handler->close(); - } -} - -/** - takes ownership of txn -*/ -void SubscriberImpl::messageHandler(const PubSubResponsePtr& m, const PubSubDataPtr& txn) { - if (!txn.get()) { - LOG.errorStream() << "Invalid transaction"; - return; - } - - if (LOG.isDebugEnabled()) { - LOG.debugStream() << "message received with status " << m->statuscode(); - } - switch (m->statuscode()) { - case SUCCESS: - txn->getCallback()->operationComplete(); - break; - case SERVICE_DOWN: - txn->getCallback()->operationFailed(ServiceDownException()); - break; - case CLIENT_ALREADY_SUBSCRIBED: - case TOPIC_BUSY: - txn->getCallback()->operationFailed(AlreadySubscribedException()); - break; - case CLIENT_NOT_SUBSCRIBED: - txn->getCallback()->operationFailed(NotSubscribedException()); - break; - default: - txn->getCallback()->operationFailed(UnexpectedResponseException()); - break; - } -} diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.h b/src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.h deleted file mode 100644 index 1412940fc0a..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/subscriberimpl.h +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef SUBSCRIBE_IMPL_H -#define SUBSCRIBE_IMPL_H - -#include -#include -#include "clientimpl.h" -#include -#include -#include - -#include -#include -#include - -namespace Hedwig { - class SubscriberWriteCallback : public OperationCallback { - public: - SubscriberWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data); - - void operationComplete(); - void operationFailed(const std::exception& exception); - private: - const ClientImplPtr client; - const PubSubDataPtr data; - }; - - class UnsubscribeWriteCallback : public OperationCallback { - public: - UnsubscribeWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data); - - void operationComplete(); - void operationFailed(const std::exception& exception); - private: - const ClientImplPtr client; - const PubSubDataPtr data; - }; - - class ConsumeWriteCallback : public OperationCallback { - public: - ConsumeWriteCallback(const ClientImplPtr& client, const PubSubDataPtr& data); - ~ConsumeWriteCallback(); - - void operationComplete(); - void operationFailed(const std::exception& exception); - - static void timerComplete(const ClientImplPtr& client, const PubSubDataPtr& data, const boost::system::error_code& error); - private: - const ClientImplPtr client; - const PubSubDataPtr data; - }; - - class SubscriberReconnectCallback : public OperationCallback { - public: - SubscriberReconnectCallback(const ClientImplPtr& client, const PubSubDataPtr& origData); - - void operationComplete(); - void operationFailed(const std::exception& exception); - private: - const ClientImplPtr client; - const PubSubDataPtr origData; - }; - - class SubscriberClientChannelHandler; - typedef boost::shared_ptr SubscriberClientChannelHandlerPtr; - - class SubscriberConsumeCallback : public OperationCallback { - public: - SubscriberConsumeCallback(const ClientImplPtr& client, const SubscriberClientChannelHandlerPtr& handler, const PubSubDataPtr& data, const PubSubResponsePtr& m); - - void operationComplete(); - void operationFailed(const std::exception& exception); - static void timerComplete(const SubscriberClientChannelHandlerPtr handler, - const PubSubResponsePtr m, - const boost::system::error_code& error); - - private: - const ClientImplPtr client; - const SubscriberClientChannelHandlerPtr handler; - - const PubSubDataPtr data; - const PubSubResponsePtr m; - }; - - class SubscriberClientChannelHandler : public HedwigClientChannelHandler, - public boost::enable_shared_from_this { - public: - SubscriberClientChannelHandler(const ClientImplPtr& client, SubscriberImpl& subscriber, const PubSubDataPtr& data); - ~SubscriberClientChannelHandler(); - - void messageReceived(const DuplexChannelPtr& channel, const PubSubResponsePtr& m); - void channelDisconnected(const DuplexChannelPtr& channel, const std::exception& e); - - void startDelivery(const MessageHandlerCallbackPtr& handler); - void stopDelivery(); - - void handoverDelivery(const SubscriberClientChannelHandlerPtr& newHandler); - - void setChannel(const DuplexChannelPtr& channel); - DuplexChannelPtr& getChannel(); - - static void reconnectTimerComplete(const SubscriberClientChannelHandlerPtr handler, const DuplexChannelPtr channel, const std::exception e, - const boost::system::error_code& error); - - void close(); - private: - - SubscriberImpl& subscriber; - std::deque queue; - - MessageHandlerCallbackPtr handler; - PubSubDataPtr origData; - DuplexChannelPtr channel; - bool closed; - bool should_wait; - }; - - class SubscriberImpl : public Subscriber { - public: - SubscriberImpl(const ClientImplPtr& client); - ~SubscriberImpl(); - - void subscribe(const std::string& topic, const std::string& subscriberId, const SubscribeRequest::CreateOrAttach mode); - void asyncSubscribe(const std::string& topic, const std::string& subscriberId, const SubscribeRequest::CreateOrAttach mode, const OperationCallbackPtr& callback); - - void unsubscribe(const std::string& topic, const std::string& subscriberId); - void asyncUnsubscribe(const std::string& topic, const std::string& subscriberId, const OperationCallbackPtr& callback); - - void consume(const std::string& topic, const std::string& subscriberId, const MessageSeqId& messageSeqId); - - void startDelivery(const std::string& topic, const std::string& subscriberId, const MessageHandlerCallbackPtr& callback); - void stopDelivery(const std::string& topic, const std::string& subscriberId); - - void closeSubscription(const std::string& topic, const std::string& subscriberId); - - void messageHandler(const PubSubResponsePtr& m, const PubSubDataPtr& txn); - - void doSubscribe(const DuplexChannelPtr& channel, const PubSubDataPtr& data, const SubscriberClientChannelHandlerPtr& handler); - void doUnsubscribe(const DuplexChannelPtr& channel, const PubSubDataPtr& data); - - private: - const ClientImplPtr client; - - std::tr1::unordered_map topicsubscriber2handler; - boost::shared_mutex topicsubscriber2handler_lock; - }; - -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/util.cpp b/src/contrib/hedwig/client/src/main/cpp/lib/util.cpp deleted file mode 100644 index 0872526cc62..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/util.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include - -#include -#include -#include "util.h" -#include "channel.h" -#include -#include -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwig."__FILE__); - -using namespace Hedwig; - -#define MAX_HOSTNAME_LENGTH 256 -const std::string UNITIALISED_HOST("UNINITIALISED HOST"); - -const int DEFAULT_PORT = 4080; -const int DEFAULT_SSL_PORT = 9876; - -HostAddress::HostAddress() : initialised(false), address_str() { - memset(&socket_addr, 0, sizeof(struct sockaddr_in)); -} - -HostAddress::~HostAddress() { -} - -bool HostAddress::isNullHost() const { - return !initialised; -} - -bool HostAddress::operator==(const HostAddress& other) const { - return (other.ip() == ip() && other.port() == port()); -} - -const std::string& HostAddress::getAddressString() const { - if (!isNullHost()) { - return address_str; - } else { - return UNITIALISED_HOST; - } -} - -uint32_t HostAddress::ip() const { - return ntohl(socket_addr.sin_addr.s_addr);; -} - -uint16_t HostAddress::port() const { - return ntohs(socket_addr.sin_port); -} - -const struct sockaddr_in& HostAddress::socketAddress() const { - return socket_addr; -} - - -void HostAddress::parse_string() { - char* url = strdup(address_str.c_str()); - - if (url == NULL) { - LOG.errorStream() << "You seems to be out of memory"; - throw OomException(); - } - int port = DEFAULT_PORT; - int sslport = DEFAULT_SSL_PORT; - - char *colon = strchr(url, ':'); - if (colon) { - *colon = 0; - colon++; - - char* sslcolon = strchr(colon, ':'); - if (sslcolon) { - *sslcolon = 0; - sslcolon++; - - sslport = strtol(sslcolon, NULL, 10); - if (sslport == 0) { - LOG.errorStream() << "Invalid SSL port given: [" << sslcolon << "]"; - free((void*)url); - throw InvalidPortException(); - } - } - - port = strtol(colon, NULL, 10); - if (port == 0) { - LOG.errorStream() << "Invalid port given: [" << colon << "]"; - free((void*)url); - throw InvalidPortException(); - } - } - - int err = 0; - - struct addrinfo *addr; - struct addrinfo hints; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; - - err = getaddrinfo(url, NULL, &hints, &addr); - if (err != 0) { - LOG.errorStream() << "Couldn't resolve host [" << url << "]:" << hstrerror(err); - free((void*)url); - throw HostResolutionException(); - } - - sockaddr_in* sa_ptr = (sockaddr_in*)addr->ai_addr; - socket_addr = *sa_ptr; - socket_addr.sin_port = htons(port); - //socket_addr.sin_family = AF_INET; - - free((void*)url); - free((void*)addr); -} - -HostAddress HostAddress::fromString(std::string str) { - HostAddress h; - h.address_str = str; - h.parse_string(); - h.initialised = true; - return h; -} - diff --git a/src/contrib/hedwig/client/src/main/cpp/lib/util.h b/src/contrib/hedwig/client/src/main/cpp/lib/util.h deleted file mode 100644 index 79137e8de2c..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/lib/util.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef HEDWIG_UTIL_H -#define HEDWIG_UTIL_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Hedwig { - typedef std::pair TopicSubscriber; - - /** - Representation of a hosts address - */ - class HostAddress { - public: - HostAddress(); - ~HostAddress(); - - bool operator==(const HostAddress& other) const; - - bool isNullHost() const; - const std::string& getAddressString() const; - uint32_t ip() const; - uint16_t port() const; - const sockaddr_in& socketAddress() const; - - static HostAddress fromString(std::string host); - - private: - - void parse_string(); - - bool initialised; - std::string address_str; - struct sockaddr_in socket_addr; - }; - - /** - Hash a host address. Takes the least significant 16-bits of the address and the 16-bits of the - port and packs them into one 32-bit number. While collisons are theoretically very possible, they - shouldn't happen as the hedwig servers should be in the same subnet. - */ - struct HostAddressHash : public std::unary_function { - size_t operator()(const Hedwig::HostAddress& address) const { - return (address.ip() << 16) & (address.port()); - } - }; - - - /** - Hash a channel pointer, just returns the pointer. - */ - struct TopicSubscriberHash : public std::unary_function { - size_t operator()(const Hedwig::TopicSubscriber& topicsub) const { - std::string fullstr = topicsub.first + topicsub.second; - return std::tr1::hash()(fullstr); - } - }; -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/log4cpp.conf b/src/contrib/hedwig/client/src/main/cpp/log4cpp.conf deleted file mode 100644 index 5651e25269f..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/log4cpp.conf +++ /dev/null @@ -1,49 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# - -log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender -log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout - -#log4j.appender.hedwig=org.apache.log4j.RollingFileAppender -log4j.appender.hedwig=org.apache.log4j.ConsoleAppender -#log4j.appender.hedwig.fileName=./testLog.log -log4j.appender.hedwig.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwig.layout.ConversionPattern=[%d{%H:%M:%S.%l}] %t %c %p - %m%n -log4j.appender.hedwig.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwig.layout.ConversionPattern=%.5m%n - -log4j.appender.hedwigtest=org.apache.log4j.ConsoleAppender -#log4j.appender.hedwig.fileName=./testLog.log -log4j.appender.hedwigtest.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwigtest.layout.ConversionPattern=[%d{%H:%M:%S.%l}] %c %p - %m%n -log4j.appender.hedwigtest.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwigtest.layout.ConversionPattern=%.5m%n - -# category -log4j.category.hedwig=DEBUG, hedwig -log4j.rootCategory=DEBUG - -log4j.category.hedwig.channel=ERROR -log4j.category.hedwig.util=ERROR -log4j.category.hedwigtest.servercontrol=ERROR - -log4j.category.hedwigtest=DEBUG, hedwigtest -log4j.rootCategory=DEBUG diff --git a/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_asio.m4 b/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_asio.m4 deleted file mode 100644 index 8cc46662f62..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_asio.m4 +++ /dev/null @@ -1,111 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_asio.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_ASIO -# -# DESCRIPTION -# -# Test for Asio library from the Boost C++ libraries. The macro requires a -# preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_ASIO_LIB) -# -# And sets: -# -# HAVE_BOOST_ASIO -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2008 Pete Greenwell -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 9 - -AC_DEFUN([AX_BOOST_ASIO], -[ - AC_ARG_WITH([boost-asio], - AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@], - [use the ASIO library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_asio_lib="" - else - want_boost="yes" - ax_boost_user_asio_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::ASIO library is available, - ax_cv_boost_asio, - [AC_LANG_PUSH([C++]) - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[ @%:@include - ]], - [[ - - boost::asio::io_service io; - boost::system::error_code timer_result; - boost::asio::deadline_timer t(io); - t.cancel(); - io.run_one(); - return 0; - ]]), - ax_cv_boost_asio=yes, ax_cv_boost_asio=no) - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_asio" = "xyes"; then - AC_DEFINE(HAVE_BOOST_ASIO,,[define if the Boost::ASIO library is available]) - BN=boost_system - if test "x$ax_boost_user_asio_lib" = "x"; then - for ax_lib in $BN $BN-$CC $BN-$CC-mt $BN-$CC-mt-s $BN-$CC-s \ - lib$BN lib$BN-$CC lib$BN-$CC-mt lib$BN-$CC-mt-s lib$BN-$CC-s \ - $BN-mgw $BN-mgw $BN-mgw-mt $BN-mgw-mt-s $BN-mgw-s ; do - AC_CHECK_LIB($ax_lib, main, [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_thread="yes" break], - [link_thread="no"]) - done - else - for ax_lib in $ax_boost_user_asio_lib $BN-$ax_boost_user_asio_lib; do - AC_CHECK_LIB($ax_lib, main, - [BOOST_ASIO_LIB="-l$ax_lib" AC_SUBST(BOOST_ASIO_LIB) link_asio="yes" break], - [link_asio="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_asio" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_base.m4 b/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_base.m4 deleted file mode 100644 index 8f935f6bc61..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_base.m4 +++ /dev/null @@ -1,252 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# DESCRIPTION -# -# Test for the Boost C++ libraries of a particular version (or newer) -# -# If no path to the installed boost library is given the macro searchs -# under /usr, /usr/local, /opt and /opt/local and evaluates the -# $BOOST_ROOT environment variable. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) -# -# And sets: -# -# HAVE_BOOST -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg -# Copyright (c) 2009 Peter Adolphs -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 17 - -AC_DEFUN([AX_BOOST_BASE], -[ -AC_ARG_WITH([boost], - [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], - [use Boost library from a standard location (ARG=yes), - from the specified location (ARG=), - or disable it (ARG=no) - @<:@ARG=yes@:>@ ])], - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi - ], - [want_boost="yes"]) - - -AC_ARG_WITH([boost-libdir], - AS_HELP_STRING([--with-boost-libdir=LIB_DIR], - [Force given directory for boost libraries. Note that this will overwrite library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), - [ - if test -d "$withval" - then - ac_boost_lib_path="$withval" - else - AC_MSG_ERROR(--with-boost-libdir expected directory name) - fi - ], - [ac_boost_lib_path=""] -) - -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=ifelse([$1], ,1.20.0,$1) - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) - succeeded=no - - dnl On x86_64 systems check for system libraries in both lib64 and lib. - dnl The former is specified by FHS, but e.g. Debian does not adhere to - dnl this (as it rises problems for generic multi-arch support). - dnl The last entry in the list is chosen by default when no libraries - dnl are found, e.g. when only header-only libraries are installed! - libsubdirs="lib" - if test `uname -m` = x86_64; then - libsubdirs="lib64 lib lib64" - fi - - dnl first we check the system location for boost libraries - dnl this location ist chosen if boost libraries are installed with the --layout=system option - dnl or if you install boost with RPM - if test "$ac_boost_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_path/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path/include" - elif test "$cross_compiling" != yes; then - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - for libsubdir in $libsubdirs ; do - if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" - break; - fi - done - fi - - dnl overwrite ld flags if we have required special directory with - dnl --with-boost-libdir parameter - if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi - - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_REQUIRE([AC_PROG_CXX]) - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - - - - dnl if we found no boost with system layout we search for boost libraries - dnl built and installed without the --layout=system option or for a staged(not installed) version - if test "x$succeeded" != "xyes"; then - _version=0 - if test "$ac_boost_path" != ""; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - fi - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" - done - fi - else - if test "$cross_compiling" != yes; then - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` - V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then - _version=$_version_tmp - best_path=$ac_boost_path - fi - done - fi - done - - VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "$ac_boost_lib_path" = ""; then - for libsubdir in $libsubdirs ; do - if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - BOOST_LDFLAGS="-L$best_path/$libsubdir" - fi - fi - - if test "x$BOOST_ROOT" != "x"; then - for libsubdir in $libsubdirs ; do - if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi - done - if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then - version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` - stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` - stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` - V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then - AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) - BOOST_CPPFLAGS="-I$BOOST_ROOT" - BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" - fi - fi - fi - fi - - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ - AC_MSG_RESULT(yes) - succeeded=yes - found_system=yes - ],[ - ]) - AC_LANG_POP([C++]) - fi - - if test "$succeeded" != "yes" ; then - if test "$_version" = "0" ; then - AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) - else - AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) - fi - # execute ACTION-IF-NOT-FOUND (if present): - ifelse([$3], , :, [$3]) - else - AC_SUBST(BOOST_CPPFLAGS) - AC_SUBST(BOOST_LDFLAGS) - AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) - # execute ACTION-IF-FOUND (if present): - ifelse([$2], , :, [$2]) - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" -fi - -]) diff --git a/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_thread.m4 b/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_thread.m4 deleted file mode 100644 index fb7e5308610..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/m4/ax_boost_thread.m4 +++ /dev/null @@ -1,149 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_THREAD -# -# DESCRIPTION -# -# Test for Thread library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_THREAD_LIB) -# -# And sets: -# -# HAVE_BOOST_THREAD -# -# LICENSE -# -# Copyright (c) 2009 Thomas Porschberg -# Copyright (c) 2009 Michael Tindal -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 17 - -AC_DEFUN([AX_BOOST_THREAD], -[ - AC_ARG_WITH([boost-thread], - AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], - [use the Thread library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-thread=boost_thread-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_thread_lib="" - else - want_boost="yes" - ax_boost_user_thread_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::Thread library is available, - ax_cv_boost_thread, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - - if test "x$build_os" = "xsolaris" ; then - CXXFLAGS="-pthreads $CXXFLAGS" - elif test "x$build_os" = "xming32" ; then - CXXFLAGS="-mthreads $CXXFLAGS" - else - CXXFLAGS="-pthread $CXXFLAGS" - fi - AC_COMPILE_IFELSE(AC_LANG_PROGRAM([[@%:@include ]], - [[boost::thread_group thrds; - return 0;]]), - ax_cv_boost_thread=yes, ax_cv_boost_thread=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_thread" = "xyes"; then - if test "x$build_os" = "xsolaris" ; then - BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - elif test "x$build_os" = "xming32" ; then - BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - else - BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - fi - - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - case "x$build_os" in - *bsd* ) - LDFLAGS="-pthread $LDFLAGS" - break; - ;; - esac - if test "x$ax_boost_user_thread_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_thread*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_thread.*\)\.a*$;\1;'`; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - if test "x$link_thread" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_thread*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_thread.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_thread*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_thread.*\)\.a*$;\1;'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], - [link_thread="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_thread" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - else - case "x$build_os" in - *bsd* ) - BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" - break; - ;; - esac - - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/src/contrib/hedwig/client/src/main/cpp/m4/ax_doxygen.m4 b/src/contrib/hedwig/client/src/main/cpp/m4/ax_doxygen.m4 deleted file mode 100644 index 6334fd41223..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/m4/ax_doxygen.m4 +++ /dev/null @@ -1,533 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html -# =========================================================================== -# -# SYNOPSIS -# -# DX_INIT_DOXYGEN(PROJECT-NAME, DOXYFILE-PATH, [OUTPUT-DIR]) -# DX_DOXYGEN_FEATURE(ON|OFF) -# DX_DOT_FEATURE(ON|OFF) -# DX_HTML_FEATURE(ON|OFF) -# DX_CHM_FEATURE(ON|OFF) -# DX_CHI_FEATURE(ON|OFF) -# DX_MAN_FEATURE(ON|OFF) -# DX_RTF_FEATURE(ON|OFF) -# DX_XML_FEATURE(ON|OFF) -# DX_PDF_FEATURE(ON|OFF) -# DX_PS_FEATURE(ON|OFF) -# -# DESCRIPTION -# -# The DX_*_FEATURE macros control the default setting for the given -# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for -# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML -# help (for MS users), 'CHI' for generating a seperate .chi file by the -# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate -# output formats. The environment variable DOXYGEN_PAPER_SIZE may be -# specified to override the default 'a4wide' paper size. -# -# By default, HTML, PDF and PS documentation is generated as this seems to -# be the most popular and portable combination. MAN pages created by -# Doxygen are usually problematic, though by picking an appropriate subset -# and doing some massaging they might be better than nothing. CHM and RTF -# are specific for MS (note that you can't generate both HTML and CHM at -# the same time). The XML is rather useless unless you apply specialized -# post-processing to it. -# -# The macros mainly control the default state of the feature. The use can -# override the default by specifying --enable or --disable. The macros -# ensure that contradictory flags are not given (e.g., -# --enable-doxygen-html and --enable-doxygen-chm, -# --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each -# feature will be automatically disabled (with a warning) if the required -# programs are missing. -# -# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN -# with the following parameters: a one-word name for the project for use -# as a filename base etc., an optional configuration file name (the -# default is 'Doxyfile', the same as Doxygen's default), and an optional -# output directory name (the default is 'doxygen-doc'). -# -# Automake Support -# -# The following is a template aminclude.am file for use with Automake. -# Make targets and variables values are controlled by the various -# DX_COND_* conditionals set by autoconf. -# -# The provided targets are: -# -# doxygen-doc: Generate all doxygen documentation. -# -# doxygen-run: Run doxygen, which will generate some of the -# documentation (HTML, CHM, CHI, MAN, RTF, XML) -# but will not do the post processing required -# for the rest of it (PS, PDF, and some MAN). -# -# doxygen-man: Rename some doxygen generated man pages. -# -# doxygen-ps: Generate doxygen PostScript documentation. -# -# doxygen-pdf: Generate doxygen PDF documentation. -# -# Note that by default these are not integrated into the automake targets. -# If doxygen is used to generate man pages, you can achieve this -# integration by setting man3_MANS to the list of man pages generated and -# then adding the dependency: -# -# $(man3_MANS): doxygen-doc -# -# This will cause make to run doxygen and generate all the documentation. -# -# The following variable is intended for use in Makefile.am: -# -# DX_CLEANFILES = everything to clean. -# -# Then add this variable to MOSTLYCLEANFILES. -# -# ----- begin aminclude.am ------------------------------------- -# -# ## --------------------------------- ## -# ## Format-independent Doxygen rules. ## -# ## --------------------------------- ## -# -# if DX_COND_doc -# -# ## ------------------------------- ## -# ## Rules specific for HTML output. ## -# ## ------------------------------- ## -# -# if DX_COND_html -# -# DX_CLEAN_HTML = @DX_DOCDIR@/html -# -# endif DX_COND_html -# -# ## ------------------------------ ## -# ## Rules specific for CHM output. ## -# ## ------------------------------ ## -# -# if DX_COND_chm -# -# DX_CLEAN_CHM = @DX_DOCDIR@/chm -# -# if DX_COND_chi -# -# DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi -# -# endif DX_COND_chi -# -# endif DX_COND_chm -# -# ## ------------------------------ ## -# ## Rules specific for MAN output. ## -# ## ------------------------------ ## -# -# if DX_COND_man -# -# DX_CLEAN_MAN = @DX_DOCDIR@/man -# -# endif DX_COND_man -# -# ## ------------------------------ ## -# ## Rules specific for RTF output. ## -# ## ------------------------------ ## -# -# if DX_COND_rtf -# -# DX_CLEAN_RTF = @DX_DOCDIR@/rtf -# -# endif DX_COND_rtf -# -# ## ------------------------------ ## -# ## Rules specific for XML output. ## -# ## ------------------------------ ## -# -# if DX_COND_xml -# -# DX_CLEAN_XML = @DX_DOCDIR@/xml -# -# endif DX_COND_xml -# -# ## ----------------------------- ## -# ## Rules specific for PS output. ## -# ## ----------------------------- ## -# -# if DX_COND_ps -# -# DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps -# -# DX_PS_GOAL = doxygen-ps -# -# doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps -# -# @DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag -# cd @DX_DOCDIR@/latex; \ -# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ -# $(DX_LATEX) refman.tex; \ -# $(MAKEINDEX_PATH) refman.idx; \ -# $(DX_LATEX) refman.tex; \ -# countdown=5; \ -# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ -# refman.log > /dev/null 2>&1 \ -# && test $$countdown -gt 0; do \ -# $(DX_LATEX) refman.tex; \ -# countdown=`expr $$countdown - 1`; \ -# done; \ -# $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi -# -# endif DX_COND_ps -# -# ## ------------------------------ ## -# ## Rules specific for PDF output. ## -# ## ------------------------------ ## -# -# if DX_COND_pdf -# -# DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf -# -# DX_PDF_GOAL = doxygen-pdf -# -# doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf -# -# @DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag -# cd @DX_DOCDIR@/latex; \ -# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ -# $(DX_PDFLATEX) refman.tex; \ -# $(DX_MAKEINDEX) refman.idx; \ -# $(DX_PDFLATEX) refman.tex; \ -# countdown=5; \ -# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ -# refman.log > /dev/null 2>&1 \ -# && test $$countdown -gt 0; do \ -# $(DX_PDFLATEX) refman.tex; \ -# countdown=`expr $$countdown - 1`; \ -# done; \ -# mv refman.pdf ../@PACKAGE@.pdf -# -# endif DX_COND_pdf -# -# ## ------------------------------------------------- ## -# ## Rules specific for LaTeX (shared for PS and PDF). ## -# ## ------------------------------------------------- ## -# -# if DX_COND_latex -# -# DX_CLEAN_LATEX = @DX_DOCDIR@/latex -# -# endif DX_COND_latex -# -# .PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) -# -# .INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) -# -# doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag -# -# doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) -# -# @DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS) -# rm -rf @DX_DOCDIR@ -# $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) -# -# DX_CLEANFILES = \ -# @DX_DOCDIR@/@PACKAGE@.tag \ -# -r \ -# $(DX_CLEAN_HTML) \ -# $(DX_CLEAN_CHM) \ -# $(DX_CLEAN_CHI) \ -# $(DX_CLEAN_MAN) \ -# $(DX_CLEAN_RTF) \ -# $(DX_CLEAN_XML) \ -# $(DX_CLEAN_PS) \ -# $(DX_CLEAN_PDF) \ -# $(DX_CLEAN_LATEX) -# -# endif DX_COND_doc -# -# ----- end aminclude.am --------------------------------------- -# -# LICENSE -# -# Copyright (c) 2009 Oren Ben-Kiki -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 10 - -## ----------## -## Defaults. ## -## ----------## - -DX_ENV="" -AC_DEFUN([DX_FEATURE_doc], ON) -AC_DEFUN([DX_FEATURE_dot], ON) -AC_DEFUN([DX_FEATURE_man], OFF) -AC_DEFUN([DX_FEATURE_html], ON) -AC_DEFUN([DX_FEATURE_chm], OFF) -AC_DEFUN([DX_FEATURE_chi], OFF) -AC_DEFUN([DX_FEATURE_rtf], OFF) -AC_DEFUN([DX_FEATURE_xml], OFF) -AC_DEFUN([DX_FEATURE_pdf], ON) -AC_DEFUN([DX_FEATURE_ps], ON) - -## --------------- ## -## Private macros. ## -## --------------- ## - -# DX_ENV_APPEND(VARIABLE, VALUE) -# ------------------------------ -# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. -AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) - -# DX_DIRNAME_EXPR -# --------------- -# Expand into a shell expression prints the directory part of a path. -AC_DEFUN([DX_DIRNAME_EXPR], - [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) - -# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) -# ------------------------------------- -# Expands according to the M4 (static) status of the feature. -AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) - -# DX_REQUIRE_PROG(VARIABLE, PROGRAM) -# ---------------------------------- -# Require the specified program to be found for the DX_CURRENT_FEATURE to work. -AC_DEFUN([DX_REQUIRE_PROG], [ -AC_PATH_TOOL([$1], [$2]) -if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then - AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) - AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) -fi -]) - -# DX_TEST_FEATURE(FEATURE) -# ------------------------ -# Expand to a shell expression testing whether the feature is active. -AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) - -# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) -# ------------------------------------------------- -# Verify that a required features has the right state before trying to turn on -# the DX_CURRENT_FEATURE. -AC_DEFUN([DX_CHECK_DEPEND], [ -test "$DX_FLAG_$1" = "$2" \ -|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, - requires, contradicts) doxygen-DX_CURRENT_FEATURE]) -]) - -# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) -# ---------------------------------------------------------- -# Turn off the DX_CURRENT_FEATURE if the required feature is off. -AC_DEFUN([DX_CLEAR_DEPEND], [ -test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0) -]) - -# DX_FEATURE_ARG(FEATURE, DESCRIPTION, -# CHECK_DEPEND, CLEAR_DEPEND, -# REQUIRE, DO-IF-ON, DO-IF-OFF) -# -------------------------------------------- -# Parse the command-line option controlling a feature. CHECK_DEPEND is called -# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), -# otherwise CLEAR_DEPEND is called to turn off the default state if a required -# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional -# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and -# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. -AC_DEFUN([DX_ARG_ABLE], [ - AC_DEFUN([DX_CURRENT_FEATURE], [$1]) - AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) - AC_ARG_ENABLE(doxygen-$1, - [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], - [--enable-doxygen-$1]), - DX_IF_FEATURE([$1], [don't $2], [$2]))], - [ -case "$enableval" in -#( -y|Y|yes|Yes|YES) - AC_SUBST([DX_FLAG_$1], 1) - $3 -;; #( -n|N|no|No|NO) - AC_SUBST([DX_FLAG_$1], 0) -;; #( -*) - AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) -;; -esac -], [ -AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) -$4 -]) -if DX_TEST_FEATURE([$1]); then - $5 - : -fi -if DX_TEST_FEATURE([$1]); then - AM_CONDITIONAL(DX_COND_$1, :) - $6 - : -else - AM_CONDITIONAL(DX_COND_$1, false) - $7 - : -fi -]) - -## -------------- ## -## Public macros. ## -## -------------- ## - -# DX_XXX_FEATURE(DEFAULT_STATE) -# ----------------------------- -AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) -AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) -AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) -AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) -AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) -AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) -AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) -AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) -AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) -AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) - -# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) -# --------------------------------------------------------- -# PROJECT also serves as the base name for the documentation files. -# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". -AC_DEFUN([DX_INIT_DOXYGEN], [ - -# Files: -AC_SUBST([DX_PROJECT], [$1]) -AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) -AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) - -# Environment variables used inside doxygen.cfg: -DX_ENV_APPEND(SRCDIR, $srcdir) -DX_ENV_APPEND(PROJECT, $DX_PROJECT) -DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) -DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) - -# Doxygen itself: -DX_ARG_ABLE(doc, [generate any doxygen documentation], - [], - [], - [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) - DX_REQUIRE_PROG([DX_PERL], perl)], - [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) - -# Dot for graphics: -DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_DOT], dot)], - [DX_ENV_APPEND(HAVE_DOT, YES) - DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], - [DX_ENV_APPEND(HAVE_DOT, NO)]) - -# Man pages generation: -DX_ARG_ABLE(man, [generate doxygen manual pages], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [], - [DX_ENV_APPEND(GENERATE_MAN, YES)], - [DX_ENV_APPEND(GENERATE_MAN, NO)]) - -# RTF file generation: -DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [], - [DX_ENV_APPEND(GENERATE_RTF, YES)], - [DX_ENV_APPEND(GENERATE_RTF, NO)]) - -# XML file generation: -DX_ARG_ABLE(xml, [generate doxygen XML documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [], - [DX_ENV_APPEND(GENERATE_XML, YES)], - [DX_ENV_APPEND(GENERATE_XML, NO)]) - -# (Compressed) HTML help generation: -DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_HHC], hhc)], - [DX_ENV_APPEND(HHC_PATH, $DX_HHC) - DX_ENV_APPEND(GENERATE_HTML, YES) - DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], - [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) - -# Seperate CHI file generation. -DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], - [DX_CHECK_DEPEND(chm, 1)], - [DX_CLEAR_DEPEND(chm, 1)], - [], - [DX_ENV_APPEND(GENERATE_CHI, YES)], - [DX_ENV_APPEND(GENERATE_CHI, NO)]) - -# Plain HTML pages generation: -DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], - [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], - [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], - [], - [DX_ENV_APPEND(GENERATE_HTML, YES)], - [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) - -# PostScript file generation: -DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_LATEX], latex) - DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) - DX_REQUIRE_PROG([DX_DVIPS], dvips) - DX_REQUIRE_PROG([DX_EGREP], egrep)]) - -# PDF file generation: -DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], - [DX_CHECK_DEPEND(doc, 1)], - [DX_CLEAR_DEPEND(doc, 1)], - [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) - DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) - DX_REQUIRE_PROG([DX_EGREP], egrep)]) - -# LaTeX generation for PS and/or PDF: -if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then - AM_CONDITIONAL(DX_COND_latex, :) - DX_ENV_APPEND(GENERATE_LATEX, YES) -else - AM_CONDITIONAL(DX_COND_latex, false) - DX_ENV_APPEND(GENERATE_LATEX, NO) -fi - -# Paper size for PS and/or PDF: -AC_ARG_VAR(DOXYGEN_PAPER_SIZE, - [a4wide (default), a4, letter, legal or executive]) -case "$DOXYGEN_PAPER_SIZE" in -#( -"") - AC_SUBST(DOXYGEN_PAPER_SIZE, "") -;; #( -a4wide|a4|letter|legal|executive) - DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) -;; #( -*) - AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) -;; -esac - -#For debugging: -#echo DX_FLAG_doc=$DX_FLAG_doc -#echo DX_FLAG_dot=$DX_FLAG_dot -#echo DX_FLAG_man=$DX_FLAG_man -#echo DX_FLAG_html=$DX_FLAG_html -#echo DX_FLAG_chm=$DX_FLAG_chm -#echo DX_FLAG_chi=$DX_FLAG_chi -#echo DX_FLAG_rtf=$DX_FLAG_rtf -#echo DX_FLAG_xml=$DX_FLAG_xml -#echo DX_FLAG_pdf=$DX_FLAG_pdf -#echo DX_FLAG_ps=$DX_FLAG_ps -#echo DX_ENV=$DX_ENV -]) diff --git a/src/contrib/hedwig/client/src/main/cpp/scripts/log4cpp.conf b/src/contrib/hedwig/client/src/main/cpp/scripts/log4cpp.conf deleted file mode 100644 index fc1084cdb82..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/scripts/log4cpp.conf +++ /dev/null @@ -1,49 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# - -log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender -log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout - -#log4j.appender.hedwig=org.apache.log4j.RollingFileAppender -log4j.appender.hedwig=org.apache.log4j.ConsoleAppender -#log4j.appender.hedwig.fileName=./testLog.log -log4j.appender.hedwig.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwig.layout.ConversionPattern=[%d{%H:%M:%S.%l}] %t %c %p - %m%n -log4j.appender.hedwig.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwig.layout.ConversionPattern=%.5m%n - -log4j.appender.hedwigtest=org.apache.log4j.ConsoleAppender -#log4j.appender.hedwig.fileName=./testLog.log -log4j.appender.hedwigtest.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwigtest.layout.ConversionPattern=[%d{%H:%M:%S.%l}] %c %p - %m%n -log4j.appender.hedwigtest.layout=org.apache.log4j.PatternLayout -log4j.appender.hedwigtest.layout.ConversionPattern=%.5m%n - -# category -log4j.category.hedwig=DEBUG, hedwig -log4j.rootCategory=DEBUG - -#log4j.category.hedwig.channel=ERROR -log4j.category.hedwig.util=ERROR -log4j.category.hedwigtest.servercontrol=ERROR - -log4j.category.hedwigtest=DEBUG, hedwigtest -log4j.rootCategory=DEBUG diff --git a/src/contrib/hedwig/client/src/main/cpp/scripts/network-delays.sh b/src/contrib/hedwig/client/src/main/cpp/scripts/network-delays.sh deleted file mode 100644 index f56609860b6..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/scripts/network-delays.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -setup_delays() { - - UNAME=`uname -s` - - echo "Setting delay to ${1}ms" - case "$UNAME" in - Darwin|FreeBSD) - sudo ipfw pipe 1 config delay ${1}ms - sudo ipfw add pipe 1 dst-port 12349 - sudo ipfw add pipe 1 dst-port 12350 - sudo ipfw add pipe 1 src-port 12349 - sudo ipfw add pipe 1 src-port 12350 - ;; - Linux) - sudo tc qdisc add dev lo root handle 1: prio - sudo tc qdisc add dev lo parent 1:3 handle 30: netem delay ${1}ms - sudo tc filter add dev lo protocol ip parent 1:0 prio 3 u32 match ip dport 12349 0xffff flowid 1:3 - sudo tc filter add dev lo protocol ip parent 1:0 prio 3 u32 match ip dport 12350 0xffff flowid 1:3 - sudo tc filter add dev lo protocol ip parent 1:0 prio 3 u32 match ip sport 12349 0xffff flowid 1:3 - sudo tc filter add dev lo protocol ip parent 1:0 prio 3 u32 match ip sport 12350 0xffff flowid 1:3 - ;; - *) - echo "Unknown system type, $UNAME, only Linux, Darwin & FreeBSD supported" - ;; - esac -} - -clear_delays() { - UNAME=`uname -s` - - case "$UNAME" in - Darwin|FreeBSD) - echo "Flushing ipfw" - sudo ipfw -f -q flush - ;; - Linux) - echo "Clearing delay" - sudo tc qdisc del dev lo root - ;; - *) - echo "Unknown system type, $UNAME, only Linux, Darwin & FreeBSD supported" - ;; - esac -} - diff --git a/src/contrib/hedwig/client/src/main/cpp/scripts/server-control.sh b/src/contrib/hedwig/client/src/main/cpp/scripts/server-control.sh deleted file mode 100644 index fa7f1c1a273..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/scripts/server-control.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -HEDWIGBASE=../../../../.. - -HEDWIGJAR=`ls $HEDWIGBASE/server/target/server-*-with-dependencies.jar` -if [ ! $? -eq 0 ]; then - echo "\n\nCould not find server-VERSION-with-dependencies.jar. \nYou need to build the java part of hedwig. \nRun mvn package in the toplevel hedwig directory.\n\n" - exit 1; -fi - -HEDWIGSERVERTESTS=$HEDWIGBASE/server/target/test-classes/ -if [ ! -e $HEDWIGSERVERTESTS ]; then - echo "\n\nThe hedwig java server tests need to be build.\b\b" - exit 1; -fi - -export CP=.:$HEDWIGJAR:$HEDWIGSERVERTESTS - -start_control_server() { - if [ -e server-control.pid ]; then - kill -9 `cat server-control.pid` - rm server-control.pid - fi - java -cp $CP -Dlog4j.configuration=log4j.properties org.apache.hedwig.ServerControlDaemon <&- 1> servercontrol.out 2>&1 & - echo $! > server-control.pid - sleep 5 -} - -stop_control_server() { - kill -9 `cat server-control.pid` - rm server-control.pid -} diff --git a/src/contrib/hedwig/client/src/main/cpp/scripts/tester.sh b/src/contrib/hedwig/client/src/main/cpp/scripts/tester.sh deleted file mode 100644 index c2880765d9f..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/scripts/tester.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -cd `dirname $0`; - -source network-delays.sh -source server-control.sh - -all() { - if [ "z$HEDWIG_NETWORK_DELAY" != "z" ]; then - setup_delays $HEDWIG_NETWORK_DELAY - fi - - start_control_server; - - ../test/hedwigtest - RESULT=$? - stop_control_server; - - if [ "z$HEDWIG_NETWORK_DELAY" != "z" ]; then - clear_delays - else - cat < - Set the millisecond delay for accessing the hedwig servers for the tests. - -tester.sh clear-delays - Clear the delay for accessing the hedwig servers. -EOF - ;; -esac diff --git a/src/contrib/hedwig/client/src/main/cpp/test/Makefile.am b/src/contrib/hedwig/client/src/main/cpp/test/Makefile.am deleted file mode 100644 index 84db87fa671..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -bin_PROGRAMS = hedwigtest -hedwigtest_SOURCES = main.cpp utiltest.cpp pubsubdatatest.cpp publishtest.cpp subscribetest.cpp servercontrol.cpp pubsubtest.cpp -hedwigtest_CPPFLAGS = -I$(top_srcdir)/inc $(DEPS_CFLAGS) $(BOOST_CPPFLAGS) -hedwigtest_LDADD = $(DEPS_LIBS) -L$(top_builddir)/lib -lhedwig01 -hedwigtest_LDFLAGS = -no-undefined $(BOOST_ASIO_LIB) $(BOOST_LDFLAGS) $(BOOST_THREAD_LIB) - -check: hedwigtest - bash ../scripts/tester.sh all diff --git a/src/contrib/hedwig/client/src/main/cpp/test/main.cpp b/src/contrib/hedwig/client/src/main/cpp/test/main.cpp deleted file mode 100644 index 000cd56c524..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/main.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "../lib/channel.h" -#include "../lib/util.h" -#include -#include -#include -#include -#include -#include "servercontrol.h" -#include "util.h" - -#include -#include - -#include -#include - -HedwigCppTextTestProgressListener gprogress; - -int main( int argc, char **argv) -{ - try { - if (getenv("LOG4CPP_CONF") == NULL) { - std::cerr << "Set LOG4CPP_CONF in your environment to get logging." << std::endl; - } else { - log4cpp::PropertyConfigurator::configure(getenv("LOG4CPP_CONF")); - } - } catch (log4cpp::ConfigureFailure &e) { - std::cerr << "log4cpp configuration failure while loading : " << e.what() << std::endl; - } catch (std::exception &e) { - std::cerr << "exception caught while configuring log4cpp via : " << e.what() << std::endl; - } catch (...) { - std::cerr << "unknown exception while configuring log4cpp vi'." << std::endl; - } - std::string testPath = (argc > 2) ? std::string(argv[2]) : ""; - - CppUnit::TextTestRunner runner; - - if (argc > 1) { - CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(argv[1]); - - runner.addTest( registry.makeTest() ); - } else { - CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry("*"); - registry.addRegistry("Util"); - registry.addRegistry("Subscribe"); - registry.addRegistry("Publish"); - registry.addRegistry("PubSub"); - - runner.addTest( registry.makeTest() ); - } - - runner.eventManager().addListener( &gprogress ); - - bool ret = runner.run(testPath); - google::protobuf::ShutdownProtobufLibrary(); - - log4cpp::Category::shutdown(); - - return (ret == true) ? 0 : 1; -} diff --git a/src/contrib/hedwig/client/src/main/cpp/test/publishtest.cpp b/src/contrib/hedwig/client/src/main/cpp/test/publishtest.cpp deleted file mode 100644 index b30acde6680..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/publishtest.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include "../lib/clientimpl.h" -#include -#include -#include -#include - -#include - -#include "servercontrol.h" -#include "util.h" - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwigtest."__FILE__); - -using namespace CppUnit; - -class PublishTestSuite : public CppUnit::TestFixture { -private: - HedwigTest::ServerControl* control; - HedwigTest::TestServerPtr zk; - HedwigTest::TestServerPtr bk1; - HedwigTest::TestServerPtr bk2; - HedwigTest::TestServerPtr bk3; - HedwigTest::TestServerPtr hw1; - HedwigTest::TestServerPtr hw2; - - CPPUNIT_TEST_SUITE( PublishTestSuite ); - CPPUNIT_TEST(testSyncPublish); - CPPUNIT_TEST(testAsyncPublish); - CPPUNIT_TEST(testMultipleAsyncPublish); - // CPPUNIT_TEST(simplePublish); - //CPPUNIT_TEST(simplePublishAndSubscribe); - //CPPUNIT_TEST(publishAndSubscribeWithRedirect); - CPPUNIT_TEST_SUITE_END(); - -public: - PublishTestSuite() : control(NULL) { - } - - ~PublishTestSuite() { - } - - void setUp() - { - control = new HedwigTest::ServerControl(HedwigTest::DEFAULT_CONTROLSERVER_PORT); - zk = control->startZookeeperServer(12345); - bk1 = control->startBookieServer(12346, zk); - bk2 = control->startBookieServer(12347, zk); - bk3 = control->startBookieServer(12348, zk); - - std::string region("testRegion"); - hw1 = control->startPubSubServer(12349, region, zk); - hw2 = control->startPubSubServer(12350, region, zk); - } - - void tearDown() - { - if (hw2.get()) { - hw2->kill(); - } - if (hw1.get()) { - hw1->kill(); - } - - if (bk1.get()) { - bk1->kill(); - } - if (bk2.get()) { - bk2->kill(); - } - if (bk3.get()) { - bk3->kill(); - } - - if (zk.get()) { - zk->kill(); - } - if (control) { - delete control; - } - } - - void testSyncPublish() { - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - - Hedwig::Client* client = new Hedwig::Client(*conf); - Hedwig::Publisher& pub = client->getPublisher(); - - pub.publish("testTopic", "testMessage 1"); - - delete client; - delete conf; - } - - void testAsyncPublish() { - SimpleWaitCondition* cond = new SimpleWaitCondition(); - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - Hedwig::Client* client = new Hedwig::Client(*conf); - Hedwig::Publisher& pub = client->getPublisher(); - - Hedwig::OperationCallbackPtr testcb(new TestCallback(cond)); - pub.asyncPublish("testTopic", "async test message", testcb); - - cond->wait(); - - CPPUNIT_ASSERT(cond->wasSuccess()); - - delete cond; - delete client; - delete conf; - } - - void testMultipleAsyncPublish() { - SimpleWaitCondition* cond1 = new SimpleWaitCondition(); - SimpleWaitCondition* cond2 = new SimpleWaitCondition(); - SimpleWaitCondition* cond3 = new SimpleWaitCondition(); - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - Hedwig::Client* client = new Hedwig::Client(*conf); - Hedwig::Publisher& pub = client->getPublisher(); - - Hedwig::OperationCallbackPtr testcb1(new TestCallback(cond1)); - Hedwig::OperationCallbackPtr testcb2(new TestCallback(cond2)); - Hedwig::OperationCallbackPtr testcb3(new TestCallback(cond3)); - - pub.asyncPublish("testTopic", "async test message #1", testcb1); - pub.asyncPublish("testTopic", "async test message #2", testcb2); - pub.asyncPublish("testTopic", "async test message #3", testcb3); - - cond3->wait(); - CPPUNIT_ASSERT(cond3->wasSuccess()); - cond2->wait(); - CPPUNIT_ASSERT(cond2->wasSuccess()); - cond1->wait(); - CPPUNIT_ASSERT(cond1->wasSuccess()); - - delete cond3; delete cond2; delete cond1; - delete client; - delete conf; - } - /* void simplePublish() { - LOG.debugStream() << ">>> simplePublish"; - SimpleWaitCondition* cond = new SimpleWaitCondition(); - - Hedwig::Configuration* conf = new Configuration1(); - Hedwig::Client* client = new Hedwig::Client(*conf); - Hedwig::Publisher& pub = client->getPublisher(); - - Hedwig::OperationCallbackPtr testcb(new TestCallback(cond)); - pub.asyncPublish("foobar", "barfoo", testcb); - - LOG.debugStream() << "wait for response"; - cond->wait(); - delete cond; - LOG.debugStream() << "got response"; - - - delete client; - delete conf; - LOG.debugStream() << "<<< simplePublish"; - } - - class MyMessageHandler : public Hedwig::MessageHandlerCallback { - public: - MyMessageHandler(SimpleWaitCondition* cond) : cond(cond) {} - - void consume(const std::string& topic, const std::string& subscriberId, const Hedwig::Message& msg, Hedwig::OperationCallbackPtr& callback) { - LOG.debugStream() << "Topic: " << topic << " subscriberId: " << subscriberId; - LOG.debugStream() << " Message: " << msg.body(); - - callback->operationComplete(); - cond->setTrue(); - cond->signal(); - } - private: - SimpleWaitCondition* cond; - };*/ - /* - void simplePublishAndSubscribe() { - SimpleWaitCondition* cond1 = new SimpleWaitCondition(); - SimpleWaitCondition* cond2 = new SimpleWaitCondition(); - SimpleWaitCondition* cond3 = new SimpleWaitCondition(); - - Hedwig::Configuration* conf = new Configuration1(); - Hedwig::Client* client = new Hedwig::Client(*conf); - Hedwig::Publisher& pub = client->getPublisher(); - Hedwig::Subscriber& sub = client->getSubscriber(); - - std::string topic("foobar"); - std::string sid("mysubscriber"); - Hedwig::OperationCallbackPtr testcb1(new TestCallback(cond1)); - sub.asyncSubscribe(topic, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH, testcb1); - Hedwig::MessageHandlerCallbackPtr messagecb(new MyMessageHandler(cond2)); - sub.startDelivery(topic, sid, messagecb); - cond1->wait(); - - Hedwig::OperationCallbackPtr testcb2(new TestCallback(cond3)); - pub.asyncPublish("foobar", "barfoo", testcb2); - cond3->wait(); - cond2->wait(); - - delete cond1; - delete cond3; - delete cond2; - - delete client; - delete conf; - } - - void publishAndSubscribeWithRedirect() { - SimpleWaitCondition* cond1 = new SimpleWaitCondition(); - SimpleWaitCondition* cond2 = new SimpleWaitCondition(); - SimpleWaitCondition* cond3 = new SimpleWaitCondition(); - SimpleWaitCondition* cond4 = new SimpleWaitCondition(); - - Hedwig::Configuration* publishconf = new Configuration1(); - Hedwig::Configuration* subscribeconf = new Configuration2(); - - Hedwig::Client* publishclient = new Hedwig::Client(*publishconf); - Hedwig::Publisher& pub = publishclient->getPublisher(); - - Hedwig::Client* subscribeclient = new Hedwig::Client(*subscribeconf); - Hedwig::Subscriber& sub = subscribeclient->getSubscriber(); - - LOG.debugStream() << "publishing"; - Hedwig::OperationCallbackPtr testcb2(new TestCallback(cond3)); - pub.asyncPublish("foobar", "barfoo", testcb2); - cond3->wait(); - - LOG.debugStream() << "Subscribing"; - std::string topic("foobar"); - std::string sid("mysubscriber"); - Hedwig::OperationCallbackPtr testcb1(new TestCallback(cond1)); - sub.asyncSubscribe(topic, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH, testcb1); - LOG.debugStream() << "Starting delivery"; - Hedwig::MessageHandlerCallbackPtr messagecb(new MyMessageHandler(cond2)); - sub.startDelivery(topic, sid, messagecb); - - LOG.debugStream() << "Subscribe wait"; - cond1->wait(); - - Hedwig::OperationCallbackPtr testcb3(new TestCallback(cond4)); - pub.asyncPublish("foobar", "barfoo", testcb3); - cond4->wait(); - - - LOG.debugStream() << "Delivery wait"; - - cond2->wait(); - - sub.stopDelivery(topic, sid); - - delete cond1; - delete cond3; - delete cond2; - delete cond4; - - delete subscribeclient; - delete publishclient; - delete publishconf; - delete subscribeconf; - }*/ -}; - -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( PublishTestSuite, "Publish"); diff --git a/src/contrib/hedwig/client/src/main/cpp/test/pubsubdatatest.cpp b/src/contrib/hedwig/client/src/main/cpp/test/pubsubdatatest.cpp deleted file mode 100644 index bb7484b890e..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/pubsubdatatest.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include "../lib/clientimpl.h" -#include -#include - -using namespace CppUnit; - -class PubSubDataTestSuite : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE( PubSubDataTestSuite ); - CPPUNIT_TEST(createPubSubData); - CPPUNIT_TEST_SUITE_END(); - -public: - void setUp() - { - } - - void tearDown() - { - } - - void createPubSubData() { - - } -}; - -CPPUNIT_TEST_SUITE_REGISTRATION( PubSubDataTestSuite ); diff --git a/src/contrib/hedwig/client/src/main/cpp/test/pubsubtest.cpp b/src/contrib/hedwig/client/src/main/cpp/test/pubsubtest.cpp deleted file mode 100644 index 4afc5f73bfe..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/pubsubtest.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include "../lib/clientimpl.h" -#include -#include -#include -#include - -#include - -#include "servercontrol.h" -#include "util.h" - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwigtest."__FILE__); - -class PubSubTestSuite : public CppUnit::TestFixture { -private: - HedwigTest::ServerControl* control; - HedwigTest::TestServerPtr zk; - HedwigTest::TestServerPtr bk1; - HedwigTest::TestServerPtr bk2; - HedwigTest::TestServerPtr bk3; - HedwigTest::TestServerPtr hw1; - - - CPPUNIT_TEST_SUITE( PubSubTestSuite ); - CPPUNIT_TEST(testPubSubContinuousOverClose); - // CPPUNIT_TEST(testPubSubContinuousOverServerDown); - CPPUNIT_TEST(testMultiTopic); - CPPUNIT_TEST(testMultiTopicMultiSubscriber); - CPPUNIT_TEST_SUITE_END(); - -public: - PubSubTestSuite() : control(NULL) { - - } - - ~PubSubTestSuite() { - } - - void setUp() - { - control = new HedwigTest::ServerControl(HedwigTest::DEFAULT_CONTROLSERVER_PORT); - zk = control->startZookeeperServer(12345); - bk1 = control->startBookieServer(12346, zk); - bk2 = control->startBookieServer(12347, zk); - bk3 = control->startBookieServer(12348, zk); - - std::string region("testRegion"); - hw1 = control->startPubSubServer(12349, region, zk); - } - - void tearDown() - { - try { - if (hw1.get()) { - hw1->kill(); - } - - if (bk1.get()) { - bk1->kill(); - } - if (bk2.get()) { - bk2->kill(); - } - if (bk3.get()) { - bk3->kill(); - } - - if (zk.get()) { - zk->kill(); - } - } catch (std::exception& e) { - // don't allow an exception to break everything, we're going deleting the control no matter what - } - if (control) { - delete control; - } - } - - class MyMessageHandlerCallback : public Hedwig::MessageHandlerCallback { - public: - MyMessageHandlerCallback(const std::string& topic, const std::string& subscriberId) : messagesReceived(0), topic(topic), subscriberId(subscriberId) { - - } - - virtual void consume(const std::string& topic, const std::string& subscriberId, const Hedwig::Message& msg, Hedwig::OperationCallbackPtr& callback) { - if (topic == this->topic && subscriberId == this->subscriberId) { - boost::lock_guard lock(mutex); - - messagesReceived++; - lastMessage = msg.body(); - callback->operationComplete(); - } - } - - std::string getLastMessage() { - boost::lock_guard lock(mutex); - std::string s = lastMessage; - return s; - } - - int numMessagesReceived() { - boost::lock_guard lock(mutex); - int i = messagesReceived; - return i; - } - - protected: - boost::mutex mutex; - int messagesReceived; - std::string lastMessage; - std::string topic; - std::string subscriberId; - }; - - void testPubSubContinuousOverClose() { - std::string topic = "pubSubTopic"; - std::string sid = "MySubscriberid-1"; - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - Hedwig::Publisher& pub = client->getPublisher(); - - sub.subscribe(topic, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - MyMessageHandlerCallback* cb = new MyMessageHandlerCallback(topic, sid); - Hedwig::MessageHandlerCallbackPtr handler(cb); - - sub.startDelivery(topic, sid, handler); - pub.publish(topic, "Test Message 1"); - bool pass = false; - for (int i = 0; i < 10; i++) { - sleep(3); - if (cb->numMessagesReceived() > 0) { - if (cb->getLastMessage() == "Test Message 1") { - pass = true; - break; - } - } - } - CPPUNIT_ASSERT(pass); - sub.closeSubscription(topic, sid); - - pub.publish(topic, "Test Message 2"); - - sub.subscribe(topic, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - sub.startDelivery(topic, sid, handler); - pass = false; - for (int i = 0; i < 10; i++) { - sleep(3); - if (cb->numMessagesReceived() > 0) { - if (cb->getLastMessage() == "Test Message 2") { - pass = true; - break; - } - } - } - CPPUNIT_ASSERT(pass); - } - - /* void testPubSubContinuousOverServerDown() { - std::string topic = "pubSubTopic"; - std::string sid = "MySubscriberid-1"; - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - Hedwig::Publisher& pub = client->getPublisher(); - - sub.subscribe(topic, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - MyMessageHandlerCallback* cb = new MyMessageHandlerCallback(topic, sid); - Hedwig::MessageHandlerCallbackPtr handler(cb); - - sub.startDelivery(topic, sid, handler); - pub.publish(topic, "Test Message 1"); - bool pass = false; - for (int i = 0; i < 10; i++) { - sleep(3); - if (cb->numMessagesReceived() > 0) { - if (cb->getLastMessage() == "Test Message 1") { - pass = true; - break; - } - } - } - CPPUNIT_ASSERT(pass); - sub.closeSubscription(topic, sid); - - pub.publish(topic, "Test Message 2"); - - sub.subscribe(topic, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - sub.startDelivery(topic, sid, handler); - pass = false; - for (int i = 0; i < 10; i++) { - sleep(3); - if (cb->numMessagesReceived() > 0) { - if (cb->getLastMessage() == "Test Message 2") { - pass = true; - break; - } - } - } - CPPUNIT_ASSERT(pass); - }*/ - - void testMultiTopic() { - std::string topicA = "pubSubTopicA"; - std::string topicB = "pubSubTopicB"; - std::string sid = "MySubscriberid-3"; - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - Hedwig::Publisher& pub = client->getPublisher(); - - sub.subscribe(topicA, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - sub.subscribe(topicB, sid, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - - MyMessageHandlerCallback* cbA = new MyMessageHandlerCallback(topicA, sid); - Hedwig::MessageHandlerCallbackPtr handlerA(cbA); - sub.startDelivery(topicA, sid, handlerA); - - MyMessageHandlerCallback* cbB = new MyMessageHandlerCallback(topicB, sid); - Hedwig::MessageHandlerCallbackPtr handlerB(cbB); - sub.startDelivery(topicB, sid, handlerB); - - pub.publish(topicA, "Test Message A"); - pub.publish(topicB, "Test Message B"); - int passA = false, passB = false; - - for (int i = 0; i < 10; i++) { - sleep(3); - if (cbA->numMessagesReceived() > 0) { - if (cbA->getLastMessage() == "Test Message A") { - passA = true; - } - } - if (cbB->numMessagesReceived() > 0) { - if (cbB->getLastMessage() == "Test Message B") { - passB = true; - } - } - if (passA && passB) { - break; - } - } - CPPUNIT_ASSERT(passA && passB); - } - - void testMultiTopicMultiSubscriber() { - std::string topicA = "pubSubTopicA"; - std::string topicB = "pubSubTopicB"; - std::string sidA = "MySubscriberid-4"; - std::string sidB = "MySubscriberid-5"; - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - Hedwig::Publisher& pub = client->getPublisher(); - - sub.subscribe(topicA, sidA, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - sub.subscribe(topicB, sidB, Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - - MyMessageHandlerCallback* cbA = new MyMessageHandlerCallback(topicA, sidA); - Hedwig::MessageHandlerCallbackPtr handlerA(cbA); - sub.startDelivery(topicA, sidA, handlerA); - - MyMessageHandlerCallback* cbB = new MyMessageHandlerCallback(topicB, sidB); - Hedwig::MessageHandlerCallbackPtr handlerB(cbB); - sub.startDelivery(topicB, sidB, handlerB); - - pub.publish(topicA, "Test Message A"); - pub.publish(topicB, "Test Message B"); - int passA = false, passB = false; - - for (int i = 0; i < 10; i++) { - sleep(3); - if (cbA->numMessagesReceived() > 0) { - if (cbA->getLastMessage() == "Test Message A") { - passA = true; - } - } - if (cbB->numMessagesReceived() > 0) { - if (cbB->getLastMessage() == "Test Message B") { - passB = true; - } - } - if (passA && passB) { - break; - } - } - CPPUNIT_ASSERT(passA && passB); - } -}; - -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( PubSubTestSuite, "PubSub" ); diff --git a/src/contrib/hedwig/client/src/main/cpp/test/servercontrol.cpp b/src/contrib/hedwig/client/src/main/cpp/test/servercontrol.cpp deleted file mode 100644 index b422a310513..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/servercontrol.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include "servercontrol.h" - - -#include - -#include "util.h" - -#include -#include - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwigtest."__FILE__); - -extern HedwigCppTextTestProgressListener gprogress; - -using namespace HedwigTest; - -const int MAX_COMMAND_LN = 256; - -class TestServerImpl : public TestServer { -public: - TestServerImpl(std::string& address, ServerControl& sc); - ~TestServerImpl(); - void kill(); - std::string& getAddress(); - -private: - std::string address; - ServerControl& sc; -}; - -TestServerImpl::TestServerImpl(std::string& address, ServerControl& sc) : address(address), sc(sc) { -} - -TestServerImpl::~TestServerImpl() { -} - -void TestServerImpl::kill() { - std::ostringstream sstr; - sstr << "KILL " << address << std::endl; - ServerControl::ServerResponse resp = sc.requestResponse(sstr.str()); - if (resp.status != "OK") { - LOG.errorStream() << "Error killing Server " << resp.message; - throw ErrorKillingServerException(); - } -} - -std::string& TestServerImpl::getAddress() { - return address; -} - -ServerControl::ServerControl(int port) { - socketfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - - if (-1 == socketfd) { - LOG.errorStream() << "Couldn't create socket"; - throw CantConnectToServerControlDaemonException(); - } - - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - - if (-1 == ::connect(socketfd, (const sockaddr *)&addr, sizeof(struct sockaddr))) { - LOG.errorStream() << "Couldn't connect socket"; - close(socketfd); - throw CantConnectToServerControlDaemonException(); - } - - requestResponse("TEST " + gprogress.getTestName() + "\n"); -} - -ServerControl::~ServerControl() { - close(socketfd); -} - - -ServerControl::ServerResponse ServerControl::requestResponse(std::string request) { - socketlock.lock(); - char response[MAX_COMMAND_LN]; - - LOG.debugStream() << "REQ: " << request.c_str() << " " << request.length(); - send(socketfd, request.c_str(), request.length(), 0); - - memset(response, 0, MAX_COMMAND_LN); - recv(socketfd, response, MAX_COMMAND_LN, 0); - LOG.debugStream() << "RESP: " << response; - - socketlock.unlock(); - - char* space = strchr(response, ' '); - if (space == NULL) { - throw InvalidServerControlDaemonResponseException(); - } - char* status = response; - *space = 0; - - char* message = space+1; - char* cr = strchr(message, '\n'); - if (cr != NULL) { - *cr = 0; - } - if (strlen(message) < 1) { - throw InvalidServerControlDaemonResponseException(); - } - LOG.debugStream() << "$" << message << "$"; - ServerControl::ServerResponse resp = { std::string(status), std::string(message) }; - return resp; -} - -TestServerPtr ServerControl::startZookeeperServer(int port) { - std::ostringstream sstr; - sstr << "START ZOOKEEPER " << port << std::endl; - - std::string req(sstr.str()); - LOG.debugStream() << req; - - ServerControl::ServerResponse resp = requestResponse(req); - if (resp.status == "OK") { - return TestServerPtr(new TestServerImpl(resp.message, *this)); - } else { - LOG.errorStream() << "Error creating zookeeper on port " << port << " " << resp.message; - throw ErrorCreatingServerException(); - } -} - -TestServerPtr ServerControl::startBookieServer(int port, TestServerPtr& zookeeperServer) { - std::ostringstream sstr; - sstr << "START BOOKKEEPER " << port << " " << zookeeperServer->getAddress() << std::endl; - - std::string req(sstr.str()); - LOG.debugStream() << req; - - ServerControl::ServerResponse resp = requestResponse(req); - if (resp.status == "OK") { - return TestServerPtr(new TestServerImpl(resp.message, *this)); - } else { - LOG.errorStream() << "Error creating bookkeeper on port " << port << " " << resp.message; - throw ErrorCreatingServerException(); - } -} - -TestServerPtr ServerControl::startPubSubServer(int port, std::string& region, TestServerPtr& zookeeperServer) { - std::ostringstream sstr; - sstr << "START HEDWIG " << port << " " << region << " " << zookeeperServer->getAddress() << std::endl; - - std::string req(sstr.str()); - LOG.debugStream() << req; - - ServerControl::ServerResponse resp = requestResponse(req); - if (resp.status == "OK") { - return TestServerPtr(new TestServerImpl(resp.message, *this)); - } else { - LOG.errorStream() << "Error creating hedwig on port " << port << " " << resp.message; - throw ErrorCreatingServerException(); - } -} diff --git a/src/contrib/hedwig/client/src/main/cpp/test/servercontrol.h b/src/contrib/hedwig/client/src/main/cpp/test/servercontrol.h deleted file mode 100644 index cac09e6ef41..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/servercontrol.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SERVERCONTROL_H -#define SERVERCONTROL_H - -#include -#include -#include -#include "../lib/util.h" - -namespace HedwigTest { - const int DEFAULT_CONTROLSERVER_PORT = 5672; - - class TestException : public std::exception {}; - class CantConnectToServerControlDaemonException : public TestException {}; - class InvalidServerControlDaemonResponseException : public TestException {}; - class ErrorCreatingServerException : public TestException {}; - class ErrorKillingServerException : public TestException {}; - - class TestServer { - public: - virtual void kill() = 0; - virtual std::string& getAddress() = 0; - virtual ~TestServer() {} - }; - - typedef std::tr1::shared_ptr TestServerPtr; - - class ServerControl { - public: - ServerControl(int port); - ~ServerControl(); - - TestServerPtr startZookeeperServer(int port); - TestServerPtr startBookieServer(int port, TestServerPtr& zookeeperServer); - TestServerPtr startPubSubServer(int port, std::string& region, TestServerPtr& zookeeperServer); - - struct ServerResponse { - std::string status; - std::string message; - }; - ServerResponse requestResponse(std::string request); - - public: - int socketfd; - boost::mutex socketlock; - }; -}; - -#endif diff --git a/src/contrib/hedwig/client/src/main/cpp/test/subscribetest.cpp b/src/contrib/hedwig/client/src/main/cpp/test/subscribetest.cpp deleted file mode 100644 index fdcfc3635b0..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/subscribetest.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include "../lib/clientimpl.h" -#include -#include -#include -#include - -#include - -#include "servercontrol.h" -#include "util.h" - -static log4cpp::Category &LOG = log4cpp::Category::getInstance("hedwigtest."__FILE__); - -class SubscribeTestSuite : public CppUnit::TestFixture { -private: - HedwigTest::ServerControl* control; - HedwigTest::TestServerPtr zk; - HedwigTest::TestServerPtr bk1; - HedwigTest::TestServerPtr bk2; - HedwigTest::TestServerPtr bk3; - HedwigTest::TestServerPtr hw1; - HedwigTest::TestServerPtr hw2; - - - CPPUNIT_TEST_SUITE( SubscribeTestSuite ); - CPPUNIT_TEST(testSyncSubscribe); - CPPUNIT_TEST(testSyncSubscribeAttach); - CPPUNIT_TEST(testAsyncSubscribe); - CPPUNIT_TEST(testAsyncSubcribeAndUnsubscribe); - CPPUNIT_TEST(testAsyncSubcribeAndSyncUnsubscribe); - CPPUNIT_TEST(testAsyncSubcribeCloseSubscriptionAndThenResubscribe); - CPPUNIT_TEST(testUnsubscribeWithoutSubscribe); - CPPUNIT_TEST(testSubscribeTwice); - CPPUNIT_TEST_SUITE_END(); - -public: - SubscribeTestSuite() : control(NULL) { - - } - - ~SubscribeTestSuite() { - } - - void setUp() - { - control = new HedwigTest::ServerControl(HedwigTest::DEFAULT_CONTROLSERVER_PORT); - zk = control->startZookeeperServer(12345); - bk1 = control->startBookieServer(12346, zk); - bk2 = control->startBookieServer(12347, zk); - bk3 = control->startBookieServer(12348, zk); - - std::string region("testRegion"); - hw1 = control->startPubSubServer(12349, region, zk); - hw2 = control->startPubSubServer(12350, region, zk); - } - - void tearDown() - { - try { - if (hw1.get()) { - hw1->kill(); - } - - if (bk1.get()) { - bk1->kill(); - } - if (bk2.get()) { - bk2->kill(); - } - if (bk3.get()) { - bk3->kill(); - } - - if (zk.get()) { - zk->kill(); - } - } catch (std::exception& e) { - // don't allow an exception to break everything, we're going deleting the control no matter what - } - if (control) { - delete control; - } - } - - void testSyncSubscribe() { - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - sub.subscribe("testTopic", "mySubscriberId-1", Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - } - - void testSyncSubscribeAttach() { - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - CPPUNIT_ASSERT_THROW(sub.subscribe("iAmATopicWhoDoesNotExist", "mySubscriberId-2", Hedwig::SubscribeRequest::ATTACH), Hedwig::ClientException); - } - - void testAsyncSubscribe() { - SimpleWaitCondition* cond1 = new SimpleWaitCondition(); - std::auto_ptr cond1ptr(cond1); - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - Hedwig::OperationCallbackPtr testcb1(new TestCallback(cond1)); - - sub.asyncSubscribe("testTopic", "mySubscriberId-3", Hedwig::SubscribeRequest::CREATE_OR_ATTACH, testcb1); - - cond1->wait(); - CPPUNIT_ASSERT(cond1->wasSuccess()); - } - - void testAsyncSubcribeAndUnsubscribe() { - SimpleWaitCondition* cond1 = new SimpleWaitCondition(); - std::auto_ptr cond1ptr(cond1); - SimpleWaitCondition* cond2 = new SimpleWaitCondition(); - std::auto_ptr cond2ptr(cond2); - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - Hedwig::OperationCallbackPtr testcb1(new TestCallback(cond1)); - Hedwig::OperationCallbackPtr testcb2(new TestCallback(cond2)); - - sub.asyncSubscribe("testTopic", "mySubscriberId-4", Hedwig::SubscribeRequest::CREATE_OR_ATTACH, testcb1); - cond1->wait(); - CPPUNIT_ASSERT(cond1->wasSuccess()); - - sub.asyncUnsubscribe("testTopic", "mySubscriberId-4", testcb2); - cond2->wait(); - CPPUNIT_ASSERT(cond2->wasSuccess()); - } - - void testAsyncSubcribeAndSyncUnsubscribe() { - SimpleWaitCondition* cond1 = new SimpleWaitCondition(); - std::auto_ptr cond1ptr(cond1); - - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - Hedwig::OperationCallbackPtr testcb1(new TestCallback(cond1)); - - sub.asyncSubscribe("testTopic", "mySubscriberId-5", Hedwig::SubscribeRequest::CREATE_OR_ATTACH, testcb1); - cond1->wait(); - CPPUNIT_ASSERT(cond1->wasSuccess()); - - sub.unsubscribe("testTopic", "mySubscriberId-5"); - } - - void testAsyncSubcribeCloseSubscriptionAndThenResubscribe() { - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - sub.subscribe("testTopic", "mySubscriberId-6", Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - sub.closeSubscription("testTopic", "mySubscriberId-6"); - sub.subscribe("testTopic", "mySubscriberId-6", Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - sub.unsubscribe("testTopic", "mySubscriberId-6"); - } - - void testUnsubscribeWithoutSubscribe() { - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - CPPUNIT_ASSERT_THROW(sub.unsubscribe("testTopic", "mySubscriberId-7"), Hedwig::NotSubscribedException); - } - - void testSubscribeTwice() { - Hedwig::Configuration* conf = new TestServerConfiguration(hw1); - std::auto_ptr confptr(conf); - - Hedwig::Client* client = new Hedwig::Client(*conf); - std::auto_ptr clientptr(client); - - Hedwig::Subscriber& sub = client->getSubscriber(); - - sub.subscribe("testTopic", "mySubscriberId-8", Hedwig::SubscribeRequest::CREATE_OR_ATTACH); - CPPUNIT_ASSERT_THROW(sub.subscribe("testTopic", "mySubscriberId-8", Hedwig::SubscribeRequest::CREATE_OR_ATTACH), Hedwig::AlreadySubscribedException); - } -}; - -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( SubscribeTestSuite, "Subscribe" ); diff --git a/src/contrib/hedwig/client/src/main/cpp/test/test.sh b/src/contrib/hedwig/client/src/main/cpp/test/test.sh deleted file mode 100644 index c75bc3f1fb1..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/test.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -export LD_LIBRARY_PATH=/usr/lib/jvm/java-6-sun/jre/lib/i386/server/:/usr/lib/jvm/java-6-sun/jre/lib/i386/ -export CLASSPATH=$HOME/src/hedwig/server/target/test-classes:$HOME/src/hedwig/server/lib/bookkeeper-SNAPSHOT.jar:$HOME/src/hedwig/server/lib/zookeeper-SNAPSHOT.jar:$HOME/src/hedwig/server/target/classes:$HOME/src/hedwig/protocol/target/classes:$HOME/src/hedwig/client/target/classes:$HOME/.m2/repository/commons-configuration/commons-configuration/1.6/commons-configuration-1.6.jar:$HOME/.m2/repository/org/jboss/netty/netty/3.1.2.GA/netty-3.1.2.GA.jar:$HOME/.m2/repository/commons-lang/commons-lang/2.4/commons-lang-2.4.jar:$HOME/.m2/repository/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar:$HOME/.m2/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar:$HOME/.m2/repository/com/google/protobuf/protobuf-java/2.3.0/protobuf-java-2.3.0.jar:$HOME/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar:$HOME/src/hedwig/client/target/classes/ - -./hedwigtest \ No newline at end of file diff --git a/src/contrib/hedwig/client/src/main/cpp/test/util.h b/src/contrib/hedwig/client/src/main/cpp/test/util.h deleted file mode 100644 index ce340adca17..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/util.h +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "../lib/clientimpl.h" -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - -static log4cpp::Category &UTILLOG = log4cpp::Category::getInstance("hedwigtest."__FILE__); - -class SimpleWaitCondition { -public: - SimpleWaitCondition() : flag(false), success(false) {}; - ~SimpleWaitCondition() { wait(); } - - void wait() { - boost::unique_lock lock(mut); - while(!flag) - { - cond.wait(lock); - } - } - - void notify() { - { - boost::lock_guard lock(mut); - flag = true; - } - cond.notify_all(); - } - - void setSuccess(bool s) { - success = s; - } - - bool wasSuccess() { - return success; - } - -private: - bool flag; - boost::condition_variable cond; - boost::mutex mut; - bool success; -}; - -class TestCallback : public Hedwig::OperationCallback { -public: - TestCallback(SimpleWaitCondition* cond) - : cond(cond) { - } - - virtual void operationComplete() { - UTILLOG.debugStream() << "operationComplete"; - cond->setSuccess(true); - cond->notify(); - - } - - virtual void operationFailed(const std::exception& exception) { - UTILLOG.debugStream() << "operationFailed: " << exception.what(); - cond->setSuccess(false); - cond->notify(); - } - - -private: - SimpleWaitCondition *cond; -}; - - -class TestServerConfiguration : public Hedwig::Configuration { -public: - TestServerConfiguration(HedwigTest::TestServerPtr& server) : server(server), address(server->getAddress()) {} - - virtual int getInt(const std::string& /*key*/, int defaultVal) const { - return defaultVal; - } - - virtual const std::string get(const std::string& key, const std::string& defaultVal) const { - if (key == Configuration::DEFAULT_SERVER) { - return address; - } else { - return defaultVal; - } - } - - virtual bool getBool(const std::string& /*key*/, bool defaultVal) const { - return defaultVal; - } - -private: - HedwigTest::TestServerPtr server; - const std::string address; -}; - - -class HedwigCppTextTestProgressListener : public CppUnit::TextTestProgressListener -{ - public: - void startTest( CppUnit::Test *test ) { - std::cout << "\n****\n\nStarting " << test->getName() << "\n\n****" << std::endl; - current_test = test->getName(); - } - - void addFailure( const CppUnit::TestFailure &failure ) { - std::cout << "\n!!!!!\n\nFailed\n\n!!!!!" << std::endl; - - } - - void endTestRun( CppUnit::Test *test, - CppUnit::TestResult *eventManager ) { - } - - std::string& getTestName() { - return current_test; - } - -private: - std::string current_test; -}; diff --git a/src/contrib/hedwig/client/src/main/cpp/test/utiltest.cpp b/src/contrib/hedwig/client/src/main/cpp/test/utiltest.cpp deleted file mode 100644 index 99ef5f38f62..00000000000 --- a/src/contrib/hedwig/client/src/main/cpp/test/utiltest.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include "../lib/util.h" -#include -#include - -using namespace CppUnit; - -class UtilTestSuite : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE( UtilTestSuite ); - CPPUNIT_TEST(testHostAddress); - CPPUNIT_TEST_SUITE_END(); - -public: - void setUp() - { - } - - void tearDown() - { - } - - void testHostAddress() { - // good address (no ports) - Hedwig::HostAddress a1 = Hedwig::HostAddress::fromString("www.yahoo.com"); - CPPUNIT_ASSERT(a1.port() == 4080); - - // good address with ip (no ports) - Hedwig::HostAddress a2 = Hedwig::HostAddress::fromString("127.0.0.1"); - CPPUNIT_ASSERT(a2.port() == 4080); - CPPUNIT_ASSERT(a2.ip() == ((127 << 24) | 1)); - - // good address - Hedwig::HostAddress a3 = Hedwig::HostAddress::fromString("www.yahoo.com:80"); - CPPUNIT_ASSERT(a3.port() == 80); - - // good address with ip - Hedwig::HostAddress a4 = Hedwig::HostAddress::fromString("127.0.0.1:80"); - CPPUNIT_ASSERT(a4.port() == 80); - CPPUNIT_ASSERT(a4.ip() == ((127 << 24) | 1)); - - // good address (with ssl) - Hedwig::HostAddress a5 = Hedwig::HostAddress::fromString("www.yahoo.com:80:443"); - CPPUNIT_ASSERT(a5.port() == 80); - - // good address with ip - Hedwig::HostAddress a6 = Hedwig::HostAddress::fromString("127.0.0.1:80:443"); - CPPUNIT_ASSERT(a6.port() == 80); - CPPUNIT_ASSERT(a6.ip() == ((127 << 24) | 1)); - - // nothing - CPPUNIT_ASSERT_THROW(Hedwig::HostAddress::fromString(""), Hedwig::HostResolutionException); - - // nothing but colons - CPPUNIT_ASSERT_THROW(Hedwig::HostAddress::fromString("::::::::::::::::"), Hedwig::ConfigurationException); - - // only port number - CPPUNIT_ASSERT_THROW(Hedwig::HostAddress::fromString(":80"), Hedwig::HostResolutionException); - - // text after colon (isn't supported) - CPPUNIT_ASSERT_THROW(Hedwig::HostAddress::fromString("www.yahoo.com:http"), Hedwig::ConfigurationException); - - // invalid hostname - CPPUNIT_ASSERT_THROW(Hedwig::HostAddress::fromString("com.oohay.www:80"), Hedwig::HostResolutionException); - - // null - CPPUNIT_ASSERT_THROW(Hedwig::HostAddress::fromString(NULL), std::logic_error); - } -}; - -CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( UtilTestSuite, "Util" ); diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/MessageHandler.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/MessageHandler.java deleted file mode 100644 index 07fa4be6508..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/MessageHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.api; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.util.Callback; - -/** - * Interface to define the client handler logic to consume messages it is - * subscribed to. - * - */ -public interface MessageHandler { - - /** - * Consumes a message it is subscribed to and has been delivered to it. - * - * @param topic - * The topic name where the message came from. - * @param subscriberId - * ID of the subscriber. - * @param msg - * The message object to consume. - * @param callback - * Callback to invoke when the message consumption has been done. - * @param context - * Calling context that the Callback needs since this is done - * asynchronously. - */ - public void consume(ByteString topic, ByteString subscriberId, Message msg, Callback callback, Object context); - -} \ No newline at end of file diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Publisher.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Publisher.java deleted file mode 100644 index a714eb54af8..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Publisher.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.api; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException.CouldNotConnectException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.util.Callback; - -/** - * Interface to define the client Publisher API. - * - */ -public interface Publisher { - - /** - * Publishes a message on the given topic. - * - * @param topic - * Topic name to publish on - * @param msg - * Message object to serialize and publish - * @throws CouldNotConnectException - * If we are not able to connect to the server host - * @throws ServiceDownException - * If we are unable to publish the message to the topic. - */ - public void publish(ByteString topic, Message msg) throws CouldNotConnectException, ServiceDownException; - - /** - * Publishes a message asynchronously on the given topic. - * - * @param topic - * Topic name to publish on - * @param msg - * Message object to serialize and publish - * @param callback - * Callback to invoke when the publish to the server has actually - * gone through. This will have to deal with error conditions on - * the async publish request. - * @param context - * Calling context that the Callback needs since this is done - * asynchronously. - */ - public void asyncPublish(ByteString topic, Message msg, Callback callback, Object context); - -} \ No newline at end of file diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Subscriber.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Subscriber.java deleted file mode 100644 index f1e35e5e4d7..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/api/Subscriber.java +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.api; - -import java.util.List; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.exceptions.InvalidSubscriberIdException; -import org.apache.hedwig.exceptions.PubSubException.ClientAlreadySubscribedException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.exceptions.PubSubException.CouldNotConnectException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.util.Callback; - -/** - * Interface to define the client Subscriber API. - * - */ -public interface Subscriber { - - /** - * Subscribe to the given topic for the inputted subscriberId. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @param mode - * Whether to prohibit, tolerate, or require an existing - * subscription. - * @throws CouldNotConnectException - * If we are not able to connect to the server host - * @throws ClientAlreadySubscribedException - * If client is already subscribed to the topic - * @throws ServiceDownException - * If unable to subscribe to topic - * @throws InvalidSubscriberIdException - * If the subscriberId is not valid. We may want to set aside - * certain formats of subscriberId's for different purposes. - * e.g. local vs. hub subscriber - */ - public void subscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode) - throws CouldNotConnectException, ClientAlreadySubscribedException, ServiceDownException, - InvalidSubscriberIdException; - - /** - * Subscribe to the given topic asynchronously for the inputted subscriberId - * disregarding if the topic has been created yet or not. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @param mode - * Whether to prohibit, tolerate, or require an existing - * subscription. - * @param callback - * Callback to invoke when the subscribe request to the server - * has actually gone through. This will have to deal with error - * conditions on the async subscribe request. - * @param context - * Calling context that the Callback needs since this is done - * asynchronously. - */ - public void asyncSubscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode, Callback callback, - Object context); - - /** - * Unsubscribe from a topic that the subscriberId user has previously - * subscribed to. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @throws CouldNotConnectException - * If we are not able to connect to the server host - * @throws ClientNotSubscribedException - * If the client is not currently subscribed to the topic - * @throws ServiceDownException - * If the server was down and unable to complete the request - * @throws InvalidSubscriberIdException - * If the subscriberId is not valid. We may want to set aside - * certain formats of subscriberId's for different purposes. - * e.g. local vs. hub subscriber - */ - public void unsubscribe(ByteString topic, ByteString subscriberId) throws CouldNotConnectException, - ClientNotSubscribedException, ServiceDownException, InvalidSubscriberIdException; - - /** - * Unsubscribe from a topic asynchronously that the subscriberId user has - * previously subscribed to. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @param callback - * Callback to invoke when the unsubscribe request to the server - * has actually gone through. This will have to deal with error - * conditions on the async unsubscribe request. - * @param context - * Calling context that the Callback needs since this is done - * asynchronously. - */ - public void asyncUnsubscribe(ByteString topic, ByteString subscriberId, Callback callback, Object context); - - /** - * Manually send a consume message to the server for the given inputs. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @param messageSeqId - * Message Sequence ID for the latest message that the client app - * has successfully consumed. All messages up to that point will - * also be considered as consumed. - * @throws ClientNotSubscribedException - * If the client is not currently subscribed to the topic based - * on the client's local state. - */ - public void consume(ByteString topic, ByteString subscriberId, MessageSeqId messageSeqId) - throws ClientNotSubscribedException; - - /** - * Checks if the subscriberId client is currently subscribed to the given - * topic. - * - * @param topic - * Topic name of the subscription. - * @param subscriberId - * ID of the subscriber - * @throws CouldNotConnectException - * If we are not able to connect to the server host - * @throws ServiceDownException - * If there is an error checking the server if the client has a - * subscription - * @return Boolean indicating if the client has a subscription or not. - */ - public boolean hasSubscription(ByteString topic, ByteString subscriberId) throws CouldNotConnectException, - ServiceDownException; - - /** - * Fills the input List with the subscriptions this subscriberId client is - * subscribed to. - * - * @param subscriberId - * ID of the subscriber - * @return List filled with subscription name (topic) strings. - * @throws CouldNotConnectException - * If we are not able to connect to the server host - * @throws ServiceDownException - * If there is an error retrieving the list of topics - */ - public List getSubscriptionList(ByteString subscriberId) throws CouldNotConnectException, - ServiceDownException; - - /** - * Begin delivery of messages from the server to us for this topic and - * subscriberId. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @param messageHandler - * Message Handler that will consume the subscribed messages - * @throws ClientNotSubscribedException - * If the client is not currently subscribed to the topic - */ - public void startDelivery(ByteString topic, ByteString subscriberId, MessageHandler messageHandler) - throws ClientNotSubscribedException; - - /** - * Stop delivery of messages for this topic and subscriberId. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @throws ClientNotSubscribedException - * If the client is not currently subscribed to the topic - */ - public void stopDelivery(ByteString topic, ByteString subscriberId) throws ClientNotSubscribedException; - - /** - * Closes all of the client side cached data for this subscription without - * actually sending an unsubscribe request to the server. This will close - * the subscribe channel synchronously (if it exists) for the topic. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @throws ServiceDownException - * If the subscribe channel was not able to be closed - * successfully - */ - public void closeSubscription(ByteString topic, ByteString subscriberId) throws ServiceDownException; - - /** - * Closes all of the client side cached data for this subscription without - * actually sending an unsubscribe request to the server. This will close - * the subscribe channel asynchronously (if it exists) for the topic. - * - * @param topic - * Topic name of the subscription - * @param subscriberId - * ID of the subscriber - * @param callback - * Callback to invoke when the subscribe channel has been closed. - * @param context - * Calling context that the Callback needs since this is done - * asynchronously. - */ - public void asyncCloseSubscription(ByteString topic, ByteString subscriberId, Callback callback, - Object context); - -} \ No newline at end of file diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkPublisher.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkPublisher.java deleted file mode 100644 index 6815249bffb..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkPublisher.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.benchmark; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.api.Publisher; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.client.benchmark.BenchmarkUtils.BenchmarkCallback; -import org.apache.hedwig.client.benchmark.BenchmarkUtils.ThroughputLatencyAggregator; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.util.Callback; - -public class BenchmarkPublisher extends BenchmarkWorker { - Publisher publisher; - Subscriber subscriber; - int msgSize; - int nParallel; - double rate; - - public BenchmarkPublisher(int numTopics, int numMessages, int numRegions, int startTopicLabel, int partitionIndex, - int numPartitions, Publisher publisher, Subscriber subscriber, int msgSize, int nParallel, int rate) { - super(numTopics, numMessages, numRegions, startTopicLabel, partitionIndex, numPartitions); - this.publisher = publisher; - this.msgSize = msgSize; - this.subscriber = subscriber; - this.nParallel = nParallel; - - this.rate = rate / (numRegions * numPartitions + 0.0); - } - - public void warmup(int nWarmup) throws Exception { - ByteString topic = ByteString.copyFromUtf8("warmup" + partitionIndex); - ByteString subId = ByteString.copyFromUtf8("sub"); - subscriber.subscribe(topic, subId, CreateOrAttach.CREATE_OR_ATTACH); - - subscriber.startDelivery(topic, subId, new MessageHandler() { - @Override - public void consume(ByteString topic, ByteString subscriberId, Message msg, Callback callback, - Object context) { - // noop - callback.operationFinished(context, null); - } - }); - - // picking constants arbitarily for warmup phase - ThroughputLatencyAggregator agg = new ThroughputLatencyAggregator("acked pubs", nWarmup, 100); - Message msg = getMsg(1024); - for (int i = 0; i < nWarmup; i++) { - publisher.asyncPublish(topic, msg, new BenchmarkCallback(agg), null); - } - - if (agg.tpAgg.queue.take() > 0) { - throw new RuntimeException("Warmup publishes failed!"); - } - - } - - public Message getMsg(int size) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < size; i++) { - sb.append('a'); - } - final ByteString body = ByteString.copyFromUtf8(sb.toString()); - Message msg = Message.newBuilder().setBody(body).build(); - return msg; - } - - public Void call() throws Exception { - Message msg = getMsg(msgSize); - - // Single warmup for every topic - int myPublishCount = 0; - for (int i = 0; i < numTopics; i++) { - if (!HedwigBenchmark.amIResponsibleForTopic(startTopicLabel + i, partitionIndex, numPartitions)){ - continue; - } - ByteString topic = ByteString.copyFromUtf8(HedwigBenchmark.TOPIC_PREFIX + (startTopicLabel + i)); - publisher.publish(topic, msg); - myPublishCount++; - } - - long startTime = System.currentTimeMillis(); - int myPublishLimit = numMessages / numRegions / numPartitions - myPublishCount; - myPublishCount = 0; - ThroughputLatencyAggregator agg = new ThroughputLatencyAggregator("acked pubs", myPublishLimit, nParallel); - - int topicLabel = 0; - - while (myPublishCount < myPublishLimit) { - int topicNum = startTopicLabel + topicLabel; - topicLabel = (topicLabel + 1) % numTopics; - - if (!HedwigBenchmark.amIResponsibleForTopic(topicNum, partitionIndex, numPartitions)) { - continue; - } - - ByteString topic = ByteString.copyFromUtf8(HedwigBenchmark.TOPIC_PREFIX + topicNum); - - if (rate > 0) { - long delay = startTime + (long) (1000 * myPublishCount / rate) - System.currentTimeMillis(); - if (delay > 0) - Thread.sleep(delay); - } - publisher.asyncPublish(topic, msg, new BenchmarkCallback(agg), null); - myPublishCount++; - } - - System.out.println("Finished unacked pubs: tput = " + BenchmarkUtils.calcTp(myPublishLimit, startTime) - + " ops/s"); - // Wait till the benchmark test has completed - agg.tpAgg.queue.take(); - System.out.println(agg.summarize(startTime)); - return null; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkSubscriber.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkSubscriber.java deleted file mode 100644 index 0244a2bddff..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkSubscriber.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.benchmark; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Callable; - -import org.apache.log4j.Logger; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.client.benchmark.BenchmarkUtils.BenchmarkCallback; -import org.apache.hedwig.client.benchmark.BenchmarkUtils.ThroughputAggregator; -import org.apache.hedwig.client.benchmark.BenchmarkUtils.ThroughputLatencyAggregator; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.RegionSpecificSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.util.Callback; - -public class BenchmarkSubscriber extends BenchmarkWorker implements Callable{ - static final Logger logger = Logger.getLogger(BenchmarkSubscriber.class); - Subscriber subscriber; - ByteString subId; - - - public BenchmarkSubscriber(int numTopics, int numMessages, int numRegions, - int startTopicLabel, int partitionIndex, int numPartitions, Subscriber subscriber, ByteString subId) { - super(numTopics, numMessages, numRegions, startTopicLabel, partitionIndex, numPartitions); - this.subscriber = subscriber; - this.subId = subId; - } - - public void warmup(int numWarmup) throws InterruptedException { - /* - * multiplying the number of ops by numParitions because we end up - * skipping many because of the partitioning logic - */ - multiSub("warmup", "warmup", 0, numWarmup, numWarmup * numPartitions); - } - - public Void call() throws Exception { - - final ThroughputAggregator agg = new ThroughputAggregator("recvs", numMessages); - final Map lastSeqIdSeenMap = new HashMap(); - - for (int i = startTopicLabel; i < startTopicLabel + numTopics; i++) { - - if (!HedwigBenchmark.amIResponsibleForTopic(i, partitionIndex, numPartitions)) { - continue; - } - - final String topic = HedwigBenchmark.TOPIC_PREFIX + i; - - subscriber.subscribe(ByteString.copyFromUtf8(topic), subId, CreateOrAttach.CREATE_OR_ATTACH); - subscriber.startDelivery(ByteString.copyFromUtf8(topic), subId, new MessageHandler() { - - @Override - public void consume(ByteString thisTopic, ByteString subscriberId, Message msg, - Callback callback, Object context) { - if (logger.isDebugEnabled()) - logger.debug("Got message from src-region: " + msg.getSrcRegion() + " with seq-id: " - + msg.getMsgId()); - - String mapKey = topic + msg.getSrcRegion().toStringUtf8(); - Long lastSeqIdSeen = lastSeqIdSeenMap.get(mapKey); - if (lastSeqIdSeen == null) { - lastSeqIdSeen = (long) 0; - } - - if (getSrcSeqId(msg) <= lastSeqIdSeen) { - logger.info("Redelivery of message, src-region: " + msg.getSrcRegion() + "seq-id: " - + msg.getMsgId()); - } else { - agg.ding(false); - } - - callback.operationFinished(context, null); - } - }); - } - System.out.println("Finished subscribing to topics and now waiting for messages to come in..."); - // Wait till the benchmark test has completed - agg.queue.take(); - System.out.println(agg.summarize(agg.earliest.get())); - return null; - } - - long getSrcSeqId(Message msg) { - if (msg.getMsgId().getRemoteComponentsCount() == 0) { - return msg.getMsgId().getLocalComponent(); - } - - for (RegionSpecificSeqId rseqId : msg.getMsgId().getRemoteComponentsList()) { - if (rseqId.getRegion().equals(msg.getSrcRegion())) - return rseqId.getSeqId(); - } - - return msg.getMsgId().getLocalComponent(); - } - - void multiSub(String label, String topicPrefix, int start, final int npar, final int count) - throws InterruptedException { - long startTime = System.currentTimeMillis(); - ThroughputLatencyAggregator agg = new ThroughputLatencyAggregator(label, count / numPartitions, npar); - int end = start + count; - for (int i = start; i < end; ++i) { - if (!HedwigBenchmark.amIResponsibleForTopic(i, partitionIndex, numPartitions)){ - continue; - } - subscriber.asyncSubscribe(ByteString.copyFromUtf8(topicPrefix + i), subId, CreateOrAttach.CREATE_OR_ATTACH, - new BenchmarkCallback(agg), null); - } - // Wait till the benchmark test has completed - agg.tpAgg.queue.take(); - if (count > 1) - System.out.println(agg.summarize(startTime)); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkUtils.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkUtils.java deleted file mode 100644 index cb4d92a02a4..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkUtils.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.benchmark; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.log4j.Logger; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.util.Callback; - -public class BenchmarkUtils { - static final Logger logger = Logger.getLogger(BenchmarkUtils.class); - - public static double calcTp(final int count, long startTime) { - return 1000. * count / (System.currentTimeMillis() - startTime); - } - - /** - * Stats aggregator for callback (round-trip) operations. Measures both - * throughput and latency. - */ - public static class ThroughputLatencyAggregator { - int numBuckets; - final ThroughputAggregator tpAgg; - final Semaphore outstanding; - final AtomicLong sum = new AtomicLong(); - - final AtomicLong[] latencyBuckets; - - // bucket[i] is count of number of operations that took >= i ms and < - // (i+1) ms. - - public ThroughputLatencyAggregator(String label, int count, int limit) throws InterruptedException { - numBuckets = Integer.getInteger("numBuckets", 101); - latencyBuckets = new AtomicLong[numBuckets]; - tpAgg = new ThroughputAggregator(label, count); - outstanding = new Semaphore(limit); - for (int i = 0; i < numBuckets; i++) { - latencyBuckets[i] = new AtomicLong(); - } - } - - public void reportLatency(long latency) { - sum.addAndGet(latency); - - int bucketIndex; - if (latency >= numBuckets) { - bucketIndex = (int) numBuckets - 1; - } else { - bucketIndex = (int) latency; - } - latencyBuckets[bucketIndex].incrementAndGet(); - } - - private String getPercentile(double percentile) { - int numInliersNeeded = (int) (percentile / 100 * tpAgg.count); - int numInliersFound = 0; - for (int i = 0; i < numBuckets - 1; i++) { - numInliersFound += latencyBuckets[i].intValue(); - if (numInliersFound > numInliersNeeded) { - return i + ""; - } - } - return " >= " + (numBuckets - 1); - } - - public String summarize(long startTime) { - double percentile = Double.parseDouble(System.getProperty("percentile", "99.9")); - return tpAgg.summarize(startTime) + ", avg latency = " + sum.get() / tpAgg.count + ", " + percentile - + "%ile latency = " + getPercentile(percentile); - } - } - - /** - * Stats aggregator for non-callback (single-shot) operations. Measures just - * throughput. - */ - public static class ThroughputAggregator { - final String label; - final int count; - final AtomicInteger done = new AtomicInteger(); - final AtomicLong earliest = new AtomicLong(); - final AtomicInteger numFailed = new AtomicInteger(); - final LinkedBlockingQueue queue = new LinkedBlockingQueue(); - - public ThroughputAggregator(final String label, final int count) { - this.label = label; - this.count = count; - if (count == 0) - queue.add(0); - if (Boolean.getBoolean("progress")) { - new Thread(new Runnable() { - @Override - public void run() { - try { - for (int doneSnap = 0, prev = 0; doneSnap < count; prev = doneSnap, doneSnap = done.get()) { - if (doneSnap > prev) { - System.out.println(label + " progress: " + doneSnap + " of " + count); - } - Thread.sleep(1000); - } - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - }).start(); - } - } - - public void ding(boolean failed) { - int snapDone = done.incrementAndGet(); - earliest.compareAndSet(0, System.currentTimeMillis()); - if (failed) - numFailed.incrementAndGet(); - if (logger.isDebugEnabled()) - logger.debug(label + " " + (failed ? "failed" : "succeeded") + ", done so far = " + snapDone); - if (snapDone == count) { - queue.add(numFailed.get()); - } - } - - public String summarize(long startTime) { - return "Finished " + label + ": count = " + done.get() + ", tput = " + calcTp(count, startTime) - + " ops/s, numFailed = " + numFailed; - } - } - - public static class BenchmarkCallback implements Callback { - - final ThroughputLatencyAggregator agg; - final long startTime; - - public BenchmarkCallback(ThroughputLatencyAggregator agg) throws InterruptedException { - this.agg = agg; - agg.outstanding.acquire(); - // Must set the start time *after* taking acquiring on outstanding. - startTime = System.currentTimeMillis(); - } - - private void finish(boolean failed) { - agg.reportLatency(System.currentTimeMillis() - startTime); - agg.tpAgg.ding(failed); - agg.outstanding.release(); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - finish(false); - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - finish(true); - } - }; - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkWorker.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkWorker.java deleted file mode 100644 index 093454fa42d..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/BenchmarkWorker.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.benchmark; - -public class BenchmarkWorker { - int numTopics; - int numMessages; - int numRegions; - int startTopicLabel; - int partitionIndex; - int numPartitions; - - public BenchmarkWorker(int numTopics, int numMessages, int numRegions, - int startTopicLabel, int partitionIndex, int numPartitions) { - this.numTopics = numTopics; - this.numMessages = numMessages; - this.numRegions = numRegions; - this.startTopicLabel = startTopicLabel; - this.partitionIndex = partitionIndex; - this.numPartitions = numPartitions; - - if (numMessages % (numTopics * numRegions) != 0) { - throw new RuntimeException("Number of messages not equally divisible among regions and topics"); - } - - if (numTopics % numPartitions != 0) { - throw new RuntimeException("Number of topics not equally divisible among partitions"); - } - - } -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/HedwigBenchmark.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/HedwigBenchmark.java deleted file mode 100644 index 077ac56b37d..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/benchmark/HedwigBenchmark.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.benchmark; - -import java.io.File; -import java.util.concurrent.Callable; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; -import org.jboss.netty.logging.InternalLoggerFactory; -import org.jboss.netty.logging.Log4JLoggerFactory; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigPublisher; -import org.apache.hedwig.client.netty.HedwigSubscriber; - -public class HedwigBenchmark implements Callable { - protected static final Logger logger = Logger.getLogger(HedwigBenchmark.class); - - static final String TOPIC_PREFIX = "topic-"; - - private final HedwigClient client; - private final HedwigPublisher publisher; - private final HedwigSubscriber subscriber; - - public HedwigBenchmark(ClientConfiguration cfg) { - client = new HedwigClient(cfg); - publisher = client.getPublisher(); - subscriber = client.getSubscriber(); - } - - static boolean amIResponsibleForTopic(int topicNum, int partitionIndex, int numPartitions) { - return topicNum % numPartitions == partitionIndex; - } - - @Override - public Void call() throws Exception { - - // - // Parameters. - // - - // What program to run: pub, sub (subscription benchmark), recv. - final String mode = System.getProperty("mode",""); - - // Number of requests to make (publishes or subscribes). - int numTopics = Integer.getInteger("nTopics", 50); - int numMessages = Integer.getInteger("nMsgs", 1000); - int numRegions = Integer.getInteger("nRegions", 1); - int startTopicLabel = Integer.getInteger("startTopicLabel", 0); - int partitionIndex = Integer.getInteger("partitionIndex", 0); - int numPartitions = Integer.getInteger("nPartitions", 1); - - int replicaIndex = Integer.getInteger("replicaIndex", 0); - - int rate = Integer.getInteger("rate", 0); - int nParallel = Integer.getInteger("npar", 100); - int msgSize = Integer.getInteger("msgSize", 1024); - - // Number of warmup subscriptions to make. - final int nWarmups = Integer.getInteger("nwarmups", 1000); - - if (mode.equals("sub")) { - BenchmarkSubscriber benchmarkSub = new BenchmarkSubscriber(numTopics, 0, 1, startTopicLabel, 0, 1, - subscriber, ByteString.copyFromUtf8("mySub")); - - benchmarkSub.warmup(nWarmups); - benchmarkSub.call(); - - } else if (mode.equals("recv")) { - - BenchmarkSubscriber benchmarkSub = new BenchmarkSubscriber(numTopics, numMessages, numRegions, - startTopicLabel, partitionIndex, numPartitions, subscriber, ByteString.copyFromUtf8("sub-" - + replicaIndex)); - - benchmarkSub.call(); - - } else if (mode.equals("pub")) { - // Offered load in msgs/second. - BenchmarkPublisher benchmarkPub = new BenchmarkPublisher(numTopics, numMessages, numRegions, - startTopicLabel, partitionIndex, numPartitions, publisher, subscriber, msgSize, nParallel, rate); - benchmarkPub.warmup(nWarmups); - benchmarkPub.call(); - - } else { - throw new Exception("unknown mode: " + mode); - } - - return null; - } - - public static void main(String[] args) throws Exception { - ClientConfiguration cfg = new ClientConfiguration(); - if (args.length > 0) { - String confFile = args[0]; - try { - cfg.loadConf(new File(confFile).toURI().toURL()); - } catch (ConfigurationException e) { - throw new RuntimeException(e); - } - } - - InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); - - HedwigBenchmark app = new HedwigBenchmark(cfg); - app.call(); - System.exit(0); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/conf/ClientConfiguration.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/conf/ClientConfiguration.java deleted file mode 100644 index f284d778fab..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/conf/ClientConfiguration.java +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.conf; - -import java.net.InetSocketAddress; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; - -import org.apache.hedwig.conf.AbstractConfiguration; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class ClientConfiguration extends AbstractConfiguration { - Logger logger = Logger.getLogger(ClientConfiguration.class); - - // Protected member variables for configuration parameter names - protected static final String DEFAULT_SERVER_HOST = "default_server_host"; - protected static final String MAX_MESSAGE_SIZE = "max_message_size"; - protected static final String MAX_SERVER_REDIRECTS = "max_server_redirects"; - protected static final String AUTO_SEND_CONSUME_MESSAGE_ENABLED = "auto_send_consume_message_enabled"; - protected static final String CONSUMED_MESSAGES_BUFFER_SIZE = "consumed_messages_buffer_size"; - protected static final String MESSAGE_CONSUME_RETRY_WAIT_TIME = "message_consume_retry_wait_time"; - protected static final String SUBSCRIBE_RECONNECT_RETRY_WAIT_TIME = "subscribe_reconnect_retry_wait_time"; - protected static final String MAX_OUTSTANDING_MESSAGES = "max_outstanding_messages"; - protected static final String SERVER_ACK_RESPONSE_TIMEOUT = "server_ack_response_timeout"; - protected static final String TIMEOUT_THREAD_RUN_INTERVAL = "timeout_thread_run_interval"; - protected static final String SSL_ENABLED = "ssl_enabled"; - - // Singletons we want to instantiate only once per ClientConfiguration - protected HedwigSocketAddress myDefaultServerAddress = null; - - // Getters for the various Client Configuration parameters. - // This should point to the default server host, or the VIP fronting all of - // the server hubs. This will return the HedwigSocketAddress which - // encapsulates both the regular and SSL port connection to the server host. - protected HedwigSocketAddress getDefaultServerHedwigSocketAddress() { - if (myDefaultServerAddress == null) - myDefaultServerAddress = new HedwigSocketAddress(conf.getString(DEFAULT_SERVER_HOST, "localhost:4080:9876")); - return myDefaultServerAddress; - } - - // This will get the default server InetSocketAddress based on if SSL is - // enabled or not. - public InetSocketAddress getDefaultServerHost() { - if (isSSLEnabled()) - return getDefaultServerHedwigSocketAddress().getSSLSocketAddress(); - else - return getDefaultServerHedwigSocketAddress().getSocketAddress(); - } - - public int getMaximumMessageSize() { - return conf.getInt(MAX_MESSAGE_SIZE, 2 * 1024 * 1024); - } - - // This parameter is for setting the maximum number of server redirects to - // allow before we consider it as an error condition. This is to stop - // infinite redirect loops in case there is a problem with the hub servers - // topic mastership. - public int getMaximumServerRedirects() { - return conf.getInt(MAX_SERVER_REDIRECTS, 2); - } - - // This parameter is a boolean flag indicating if the client library should - // automatically send the consume message to the server based on the - // configured amount of messages consumed by the client app. The client app - // could choose to override this behavior and instead, manually send the - // consume message to the server via the client library using its own - // logic and policy. - public boolean isAutoSendConsumeMessageEnabled() { - return conf.getBoolean(AUTO_SEND_CONSUME_MESSAGE_ENABLED, true); - } - - // This parameter is to set how many consumed messages we'll buffer up - // before we send the Consume message to the server indicating that all - // of the messages up to that point have been successfully consumed by - // the client. - public int getConsumedMessagesBufferSize() { - return conf.getInt(CONSUMED_MESSAGES_BUFFER_SIZE, 5); - } - - // This parameter is used to determine how long we wait before retrying the - // client app's MessageHandler to consume a subscribed messages sent to us - // from the server. The time to wait is in milliseconds. - public long getMessageConsumeRetryWaitTime() { - return conf.getLong(MESSAGE_CONSUME_RETRY_WAIT_TIME, 10000); - } - - // This parameter is used to determine how long we wait before retrying the - // Subscribe Reconnect request. This is done when the connection to a server - // disconnects and we attempt to connect to it. We'll keep on trying but - // in case the server(s) is down for a longer time, we want to throttle - // how often we do the subscribe reconnect request. The time to wait is in - // milliseconds. - public long getSubscribeReconnectRetryWaitTime() { - return conf.getLong(SUBSCRIBE_RECONNECT_RETRY_WAIT_TIME, 10000); - } - - // This parameter is for setting the maximum number of outstanding messages - // the client app can be consuming at a time for topic subscription before - // we throttle things and stop reading from the Netty Channel. - public int getMaximumOutstandingMessages() { - return conf.getInt(MAX_OUTSTANDING_MESSAGES, 10); - } - - // This parameter is used to determine how long we wait (in milliseconds) - // before we time out outstanding PubSubRequests that were written to the - // server successfully but haven't yet received the ack response. - public long getServerAckResponseTimeout() { - return conf.getLong(SERVER_ACK_RESPONSE_TIMEOUT, 30000); - } - - // This parameter is used to determine how often we run the server ack - // response timeout cleaner thread (in milliseconds). - public long getTimeoutThreadRunInterval() { - return conf.getLong(TIMEOUT_THREAD_RUN_INTERVAL, 60000); - } - - // This parameter is a boolean flag indicating if communication with the - // server should be done via SSL for encryption. This is needed for - // cross-colo hub clients listening to non-local servers. - public boolean isSSLEnabled() { - return conf.getBoolean(SSL_ENABLED, false); - } - - // Validate that the configuration properties are valid. - public void validate() throws ConfigurationException { - if (isSSLEnabled() && getDefaultServerHedwigSocketAddress().getSSLSocketAddress() == null) { - throw new ConfigurationException("SSL is enabled but a default server SSL port not given!"); - } - // Add other validation checks here - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/MessageConsumeData.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/MessageConsumeData.java deleted file mode 100644 index 50a9a8e1ec8..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/MessageConsumeData.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.data; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -/** - * Wrapper class to store all of the data points needed to encapsulate Message - * Consumption in the Subscribe flow for consuming a message sent from the - * server for a given TopicSubscriber. This will be used as the Context in the - * VoidCallback for the MessageHandlers once they've completed consuming the - * message. - * - */ -public class MessageConsumeData { - - // Member variables - public final ByteString topic; - public final ByteString subscriberId; - // This is the Message sent from the server for Subscribes for consumption - // by the client. - public final Message msg; - - // Constructor - public MessageConsumeData(final ByteString topic, final ByteString subscriberId, final Message msg) { - this.topic = topic; - this.subscriberId = subscriberId; - this.msg = msg; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (topic != null) - sb.append("Topic: " + topic.toStringUtf8()); - if (subscriberId != null) - sb.append(PubSubData.COMMA).append("SubscriberId: " + subscriberId.toStringUtf8()); - if (msg != null) - sb.append(PubSubData.COMMA).append("Message: " + msg); - return sb.toString(); - } -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/PubSubData.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/PubSubData.java deleted file mode 100644 index 047017d6648..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/PubSubData.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.data; - -import java.util.List; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.util.Callback; - -/** - * Wrapper class to store all of the data points needed to encapsulate all - * PubSub type of request operations the client will do. This includes knowing - * all of the information needed if we need to redo the publish/subscribe - * request in case of a server redirect. This will be used for all sync/async - * calls, and for all the known types of request messages to send to the server - * hubs: Publish, Subscribe, Unsubscribe, and Consume. - * - */ -public class PubSubData { - // Static string constants - protected static final String COMMA = ", "; - - // Member variables needed during object construction time. - public final ByteString topic; - public final Message msg; - public final ByteString subscriberId; - // Enum to indicate what type of operation this PubSub request data object - // is for. - public final OperationType operationType; - // Enum for subscribe requests to indicate if this is a CREATE, ATTACH, or - // CREATE_OR_ATTACH subscription request. For non-subscribe requests, - // this will be null. - public final CreateOrAttach createOrAttach; - // These two variables are not final since we might override them - // in the case of a Subscribe reconnect. - public Callback callback; - public Object context; - - // Member variables used after object has been constructed. - // List of all servers we've sent the PubSubRequest to successfully. - // This is to keep track of redirected servers that responded back to us. - public List triedServers; - // List of all servers that we've tried to connect or write to but - // was unsuccessful. We'll retry sending the PubSubRequest but will - // quit if we're trying to connect or write to a server that we've - // attempted to previously. - public List connectFailedServers; - public List writeFailedServers; - // Boolean to the hub server indicating if it should claim ownership - // of the topic the PubSubRequest is for. This is mainly used after - // a server redirect. Defaults to false. - public boolean shouldClaim = false; - // TxnID for the PubSubData if it was sent as a PubSubRequest to the hub - // server. This is used in the WriteCallback in case of failure. We want - // to remove it from the ResponseHandler.txn2PubSubData map since the - // failed PubSubRequest will not get an ack response from the server. - // This is set later in the PubSub flows only when we write the actual - // request. Therefore it is not an argument in the constructor. - public long txnId; - // Time in milliseconds using the System.currentTimeMillis() call when the - // PubSubRequest was written on the netty Channel to the server. - public long requestWriteTime; - // For synchronous calls, this variable is used to know when the background - // async process for it has completed, set in the VoidCallback. - public boolean isDone = false; - - // Constructor for all types of PubSub request data to send to the server - public PubSubData(final ByteString topic, final Message msg, final ByteString subscriberId, - final OperationType operationType, final CreateOrAttach createOrAttach, final Callback callback, - final Object context) { - this.topic = topic; - this.msg = msg; - this.subscriberId = subscriberId; - this.operationType = operationType; - this.createOrAttach = createOrAttach; - this.callback = callback; - this.context = context; - } - - // Clear all of the stored servers we've contacted or attempted to in this - // request. - public void clearServersList() { - if (triedServers != null) - triedServers.clear(); - if (connectFailedServers != null) - connectFailedServers.clear(); - if (writeFailedServers != null) - writeFailedServers.clear(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (topic != null) - sb.append("Topic: " + topic.toStringUtf8()); - if (msg != null) - sb.append(COMMA).append("Message: " + msg); - if (subscriberId != null) - sb.append(COMMA).append("SubscriberId: " + subscriberId.toStringUtf8()); - if (operationType != null) - sb.append(COMMA).append("Operation Type: " + operationType.toString()); - if (createOrAttach != null) - sb.append(COMMA).append("Create Or Attach: " + createOrAttach.toString()); - if (triedServers != null && triedServers.size() > 0) { - sb.append(COMMA).append("Tried Servers: "); - for (ByteString triedServer : triedServers) { - sb.append(triedServer.toStringUtf8()).append(COMMA); - } - } - if (connectFailedServers != null && connectFailedServers.size() > 0) { - sb.append(COMMA).append("Connect Failed Servers: "); - for (ByteString connectFailedServer : connectFailedServers) { - sb.append(connectFailedServer.toStringUtf8()).append(COMMA); - } - } - if (writeFailedServers != null && writeFailedServers.size() > 0) { - sb.append(COMMA).append("Write Failed Servers: "); - for (ByteString writeFailedServer : writeFailedServers) { - sb.append(writeFailedServer.toStringUtf8()).append(COMMA); - } - } - sb.append(COMMA).append("Should Claim: " + shouldClaim); - if (txnId != 0) - sb.append(COMMA).append("TxnID: " + txnId); - if (requestWriteTime != 0) - sb.append(COMMA).append("Request Write Time: " + requestWriteTime); - sb.append(COMMA).append("Is Done: " + isDone); - return sb.toString(); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/TopicSubscriber.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/TopicSubscriber.java deleted file mode 100644 index 3e6468d927c..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/data/TopicSubscriber.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.data; - -import org.apache.commons.lang.builder.HashCodeBuilder; - -import com.google.protobuf.ByteString; - -/** - * Wrapper class object for the Topic + SubscriberId combination. Since the - * Subscribe flows always use the Topic + SubscriberId as the logical entity, - * we'll create a simple class to encapsulate that. - * - */ -public class TopicSubscriber { - private final ByteString topic; - private final ByteString subscriberId; - private final int hashCode; - - public TopicSubscriber(final ByteString topic, final ByteString subscriberId) { - this.topic = topic; - this.subscriberId = subscriberId; - hashCode = new HashCodeBuilder().append(topic).append(subscriberId).toHashCode(); - } - - @Override - public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof TopicSubscriber)) - return false; - final TopicSubscriber obj = (TopicSubscriber) o; - return topic.equals(obj.topic) && subscriberId.equals(obj.subscriberId); - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (topic != null) - sb.append("Topic: " + topic.toStringUtf8()); - if (subscriberId != null) - sb.append(PubSubData.COMMA).append("SubscriberId: " + subscriberId.toStringUtf8()); - return sb.toString(); - } - - public ByteString getTopic() { - return topic; - } - - public ByteString getSubscriberId() { - return subscriberId; - } - -} \ No newline at end of file diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/InvalidSubscriberIdException.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/InvalidSubscriberIdException.java deleted file mode 100644 index 3e543569f09..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/InvalidSubscriberIdException.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.exceptions; - -/** - * This is a Hedwig client side exception when the local client wants to do - * subscribe type of operations. Currently, to distinguish between local and hub - * subscribers, the subscriberId will have a specific format. - */ -public class InvalidSubscriberIdException extends Exception { - - private static final long serialVersionUID = 873259807218723523L; - - public InvalidSubscriberIdException(String message) { - super(message); - } - - public InvalidSubscriberIdException(String message, Throwable t) { - super(message, t); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/ServerRedirectLoopException.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/ServerRedirectLoopException.java deleted file mode 100644 index 9c58bb02e81..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/ServerRedirectLoopException.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.exceptions; - -/** - * This is a Hedwig client side exception when the PubSubRequest is being - * redirected to a server where the request has already been sent to previously. - * To avoid having a cyclical redirect loop, this condition is checked for - * and this exception will be thrown to the client caller. - */ -public class ServerRedirectLoopException extends Exception { - - private static final long serialVersionUID = 98723508723152897L; - - public ServerRedirectLoopException(String message) { - super(message); - } - - public ServerRedirectLoopException(String message, Throwable t) { - super(message, t); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/TooManyServerRedirectsException.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/TooManyServerRedirectsException.java deleted file mode 100644 index 47c5408b4c4..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/exceptions/TooManyServerRedirectsException.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.exceptions; - -/** - * This is a Hedwig client side exception when there have been too many server - * redirects during a publish/subscribe call. We only allow a certain number of - * server redirects to find the topic master. If we have exceeded this - * configured amount, the publish/subscribe will fail with this exception. - * - */ -public class TooManyServerRedirectsException extends Exception { - - private static final long serialVersionUID = 2341192937965635310L; - - public TooManyServerRedirectsException(String message) { - super(message); - } - - public TooManyServerRedirectsException(String message, Throwable t) { - super(message, t); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/MessageConsumeCallback.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/MessageConsumeCallback.java deleted file mode 100644 index cc916aa00d9..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/MessageConsumeCallback.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.handlers; - -import java.util.TimerTask; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.client.data.MessageConsumeData; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.util.Callback; - -/** - * This is the Callback used by the MessageHandlers on the client app when - * they've finished consuming a subscription message sent from the server - * asynchronously. This callback back to the client libs will be stateless so we - * can use a singleton for the class. The object context used should be the - * MessageConsumeData type. That will contain all of the information needed to - * call the message consume logic in the client lib ResponseHandler. - * - */ -public class MessageConsumeCallback implements Callback { - - private static Logger logger = Logger.getLogger(MessageConsumeCallback.class); - - private final HedwigClient client; - - public MessageConsumeCallback(HedwigClient client) { - this.client = client; - } - - class MessageConsumeRetryTask extends TimerTask { - private final MessageConsumeData messageConsumeData; - private final TopicSubscriber topicSubscriber; - - public MessageConsumeRetryTask(MessageConsumeData messageConsumeData, TopicSubscriber topicSubscriber) { - this.messageConsumeData = messageConsumeData; - this.topicSubscriber = topicSubscriber; - } - - @Override - public void run() { - // Try to consume the message again - Channel topicSubscriberChannel = client.getSubscriber().getChannelForTopic(topicSubscriber); - HedwigClient.getResponseHandlerFromChannel(topicSubscriberChannel).getSubscribeResponseHandler() - .asyncMessageConsume(messageConsumeData.msg); - } - } - - public void operationFinished(Object ctx, Void resultOfOperation) { - MessageConsumeData messageConsumeData = (MessageConsumeData) ctx; - TopicSubscriber topicSubscriber = new TopicSubscriber(messageConsumeData.topic, messageConsumeData.subscriberId); - // Message has been successfully consumed by the client app so callback - // to the ResponseHandler indicating that the message is consumed. - Channel topicSubscriberChannel = client.getSubscriber().getChannelForTopic(topicSubscriber); - HedwigClient.getResponseHandlerFromChannel(topicSubscriberChannel).getSubscribeResponseHandler() - .messageConsumed(messageConsumeData.msg); - } - - public void operationFailed(Object ctx, PubSubException exception) { - // Message has NOT been successfully consumed by the client app so - // callback to the ResponseHandler to try the async MessageHandler - // Consume logic again. - MessageConsumeData messageConsumeData = (MessageConsumeData) ctx; - TopicSubscriber topicSubscriber = new TopicSubscriber(messageConsumeData.topic, messageConsumeData.subscriberId); - logger.error("Message was not consumed successfully by client MessageHandler: " + messageConsumeData); - - // Sleep a pre-configured amount of time (in milliseconds) before we - // do the retry. In the future, we can have more dynamic logic on - // what duration to sleep based on how many times we've retried, or - // perhaps what the last amount of time we slept was. We could stick - // some of this meta-data into the MessageConsumeData when we retry. - client.getClientTimer().schedule(new MessageConsumeRetryTask(messageConsumeData, topicSubscriber), - client.getConfiguration().getMessageConsumeRetryWaitTime()); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PubSubCallback.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PubSubCallback.java deleted file mode 100644 index 74914f64c67..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PubSubCallback.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.handlers; - -import org.apache.log4j.Logger; - -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.util.Callback; - -/** - * This class is used when we are doing synchronous type of operations. All - * underlying client ops in Hedwig are async so this is just a way to make the - * async calls synchronous. - * - */ -public class PubSubCallback implements Callback { - - private static Logger logger = Logger.getLogger(PubSubCallback.class); - - // Private member variables - private PubSubData pubSubData; - // Boolean indicator to see if the sync PubSub call was successful or not. - private boolean isCallSuccessful; - // For sync callbacks, we'd like to know what the PubSubException is thrown - // on failure. This is so we can have a handle to the exception and rethrow - // it later. - private PubSubException failureException; - - // Constructor - public PubSubCallback(PubSubData pubSubData) { - this.pubSubData = pubSubData; - } - - public void operationFinished(Object ctx, Void resultOfOperation) { - if (logger.isDebugEnabled()) - logger.debug("PubSub call succeeded for pubSubData: " + pubSubData); - // Wake up the main sync PubSub thread that is waiting for us to - // complete. - synchronized (pubSubData) { - isCallSuccessful = true; - pubSubData.isDone = true; - pubSubData.notify(); - } - } - - public void operationFailed(Object ctx, PubSubException exception) { - if (logger.isDebugEnabled()) - logger.debug("PubSub call failed with exception: " + exception + ", pubSubData: " + pubSubData); - // Wake up the main sync PubSub thread that is waiting for us to - // complete. - synchronized (pubSubData) { - isCallSuccessful = false; - failureException = exception; - pubSubData.isDone = true; - pubSubData.notify(); - } - } - - // Public getter to determine if the PubSub callback is successful or not - // based on the PubSub ack response from the server. - public boolean getIsCallSuccessful() { - return isCallSuccessful; - } - - // Public getter to retrieve what the PubSubException was that occurred when - // the operation failed. - public PubSubException getFailureException() { - return failureException; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PublishResponseHandler.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PublishResponseHandler.java deleted file mode 100644 index 69477167fc3..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/PublishResponseHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.handlers; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.ResponseHandler; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; - -public class PublishResponseHandler { - - private static Logger logger = Logger.getLogger(PublishResponseHandler.class); - - private final ResponseHandler responseHandler; - - public PublishResponseHandler(ResponseHandler responseHandler) { - this.responseHandler = responseHandler; - } - - // Main method to handle Publish Response messages from the server. - public void handlePublishResponse(PubSubResponse response, PubSubData pubSubData, Channel channel) throws Exception { - if (logger.isDebugEnabled()) - logger.debug("Handling a Publish response: " + response + ", pubSubData: " + pubSubData + ", host: " - + HedwigClient.getHostFromChannel(channel)); - switch (response.getStatusCode()) { - case SUCCESS: - // Response was success so invoke the callback's operationFinished - // method. - pubSubData.callback.operationFinished(pubSubData.context, null); - break; - case SERVICE_DOWN: - // Response was service down failure so just invoke the callback's - // operationFailed method. - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Server responded with a SERVICE_DOWN status")); - break; - case NOT_RESPONSIBLE_FOR_TOPIC: - // Redirect response so we'll need to repost the original Publish - // Request - responseHandler.handleRedirectResponse(response, pubSubData, channel); - break; - default: - // Consider all other status codes as errors, operation failed - // cases. - logger.error("Unexpected error response from server for PubSubResponse: " + response); - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Server responded with a status code of: " + response.getStatusCode())); - break; - } - } -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeReconnectCallback.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeReconnectCallback.java deleted file mode 100644 index 3db17b914f7..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeReconnectCallback.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.handlers; - -import java.util.TimerTask; - -import org.apache.log4j.Logger; - -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.util.Callback; - -/** - * This class is used when a Subscribe channel gets disconnected and we attempt - * to re-establish the connection. Once the connection to the server host for - * the topic is completed, we need to restart delivery for that topic if that - * was the case before the original channel got disconnected. This async - * callback will be the hook for this. - * - */ -public class SubscribeReconnectCallback implements Callback { - - private static Logger logger = Logger.getLogger(SubscribeReconnectCallback.class); - - // Private member variables - private final PubSubData origSubData; - private final HedwigClient client; - private final HedwigSubscriber sub; - private final ClientConfiguration cfg; - private final MessageHandler messageHandler; - - // Constructor - public SubscribeReconnectCallback(PubSubData origSubData, HedwigClient client, MessageHandler messageHandler) { - this.origSubData = origSubData; - this.client = client; - this.sub = client.getSubscriber(); - this.cfg = client.getConfiguration(); - this.messageHandler = messageHandler; - } - - class SubscribeReconnectRetryTask extends TimerTask { - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Retrying subscribe reconnect request for origSubData: " + origSubData); - // Clear out all of the servers we've contacted or attempted to from - // this request. - origSubData.clearServersList(); - client.doConnect(origSubData, cfg.getDefaultServerHost()); - } - } - - public void operationFinished(Object ctx, Void resultOfOperation) { - if (logger.isDebugEnabled()) - logger.debug("Subscribe reconnect succeeded for origSubData: " + origSubData); - // Now we want to restart delivery for the subscription channel only - // if delivery was started at the time the original subscribe channel - // was disconnected. - if (messageHandler != null) { - try { - sub.startDelivery(origSubData.topic, origSubData.subscriberId, messageHandler); - } catch (ClientNotSubscribedException e) { - // This exception should never be thrown here but just in case, - // log an error and just keep retrying the subscribe request. - logger.error("Subscribe was successful but error starting delivery for topic: " - + origSubData.topic.toStringUtf8() + ", subscriberId: " - + origSubData.subscriberId.toStringUtf8(), e); - retrySubscribeRequest(); - } - } - } - - public void operationFailed(Object ctx, PubSubException exception) { - // If the subscribe reconnect fails, just keep retrying the subscribe - // request. There isn't a way to flag to the application layer that - // a topic subscription has failed. So instead, we'll just keep - // retrying in the background until success. - logger.error("Subscribe reconnect failed with error: " + exception.getMessage()); - retrySubscribeRequest(); - } - - private void retrySubscribeRequest() { - // If the client has stopped, there is no need to proceed with any - // callback logic here. - if (client.hasStopped()) - return; - - // Retry the subscribe request but only after waiting for a - // preconfigured amount of time. - client.getClientTimer().schedule(new SubscribeReconnectRetryTask(), - client.getConfiguration().getSubscribeReconnectRetryWaitTime()); - } -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeResponseHandler.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeResponseHandler.java deleted file mode 100644 index e34360547e9..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/SubscribeResponseHandler.java +++ /dev/null @@ -1,329 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.handlers; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Queue; -import java.util.Set; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.data.MessageConsumeData; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.ResponseHandler; -import org.apache.hedwig.exceptions.PubSubException.ClientAlreadySubscribedException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; - -public class SubscribeResponseHandler { - - private static Logger logger = Logger.getLogger(SubscribeResponseHandler.class); - - private final ResponseHandler responseHandler; - - // Member variables used when this ResponseHandler is for a Subscribe - // channel. We need to be able to consume messages sent back to us from - // the server, and to also recreate the Channel connection if it ever goes - // down. For that, we need to store the original PubSubData for the - // subscribe request, and also the MessageHandler that was registered when - // delivery of messages started for the subscription. - private PubSubData origSubData; - private Channel subscribeChannel; - private MessageHandler messageHandler; - // Counter for the number of consumed messages so far to buffer up before we - // send the Consume message back to the server along with the last/largest - // message seq ID seen so far in that batch. - private int numConsumedMessagesInBuffer = 0; - private MessageSeqId lastMessageSeqId; - // Queue used for subscribes when the MessageHandler hasn't been registered - // yet but we've already received subscription messages from the server. - // This will be lazily created as needed. - private Queue subscribeMsgQueue; - // Set to store all of the outstanding subscribed messages that are pending - // to be consumed by the client app's MessageHandler. If this ever grows too - // big (e.g. problem at the client end for message consumption), we can - // throttle things by temporarily setting the Subscribe Netty Channel - // to not be readable. When the Set has shrunk sufficiently, we can turn the - // channel back on to read new messages. - private Set outstandingMsgSet; - - public SubscribeResponseHandler(ResponseHandler responseHandler) { - this.responseHandler = responseHandler; - } - - // Public getter to retrieve the original PubSubData used for the Subscribe - // request. - public PubSubData getOrigSubData() { - return origSubData; - } - - // Main method to handle Subscribe responses from the server that we sent - // a Subscribe Request to. - public void handleSubscribeResponse(PubSubResponse response, PubSubData pubSubData, Channel channel) - throws Exception { - // If this was not a successful response to the Subscribe request, we - // won't be using the Netty Channel created so just close it. - if (!response.getStatusCode().equals(StatusCode.SUCCESS)) { - HedwigClient.getResponseHandlerFromChannel(channel).channelClosedExplicitly = true; - channel.close(); - } - - if (logger.isDebugEnabled()) - logger.debug("Handling a Subscribe response: " + response + ", pubSubData: " + pubSubData + ", host: " - + HedwigClient.getHostFromChannel(channel)); - switch (response.getStatusCode()) { - case SUCCESS: - // For successful Subscribe requests, store this Channel locally - // and set it to not be readable initially. - // This way we won't be delivering messages for this topic - // subscription until the client explicitly says so. - subscribeChannel = channel; - subscribeChannel.setReadable(false); - // Store the original PubSubData used to create this successful - // Subscribe request. - origSubData = pubSubData; - // Store the mapping for the TopicSubscriber to the Channel. - // This is so we can control the starting and stopping of - // message deliveries from the server on that Channel. Store - // this only on a successful ack response from the server. - TopicSubscriber topicSubscriber = new TopicSubscriber(pubSubData.topic, pubSubData.subscriberId); - responseHandler.getSubscriber().setChannelForTopic(topicSubscriber, channel); - // Lazily create the Set to keep track of outstanding Messages - // to be consumed by the client app. At this stage, delivery for - // that topic hasn't started yet so creation of this Set should - // be thread safe. We'll create the Set with an initial capacity - // equal to the configured parameter for the maximum number of - // outstanding messages to allow. The load factor will be set to - // 1.0f which means we'll only rehash and allocate more space if - // we ever exceed the initial capacity. That should be okay - // because when that happens, things are slow already and piling - // up on the client app side to consume messages. - outstandingMsgSet = new HashSet( - responseHandler.getConfiguration().getMaximumOutstandingMessages(), 1.0f); - // Response was success so invoke the callback's operationFinished - // method. - pubSubData.callback.operationFinished(pubSubData.context, null); - break; - case CLIENT_ALREADY_SUBSCRIBED: - // For Subscribe requests, the server says that the client is - // already subscribed to it. - pubSubData.callback.operationFailed(pubSubData.context, new ClientAlreadySubscribedException( - "Client is already subscribed for topic: " + pubSubData.topic.toStringUtf8() + ", subscriberId: " - + pubSubData.subscriberId.toStringUtf8())); - break; - case SERVICE_DOWN: - // Response was service down failure so just invoke the callback's - // operationFailed method. - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Server responded with a SERVICE_DOWN status")); - break; - case NOT_RESPONSIBLE_FOR_TOPIC: - // Redirect response so we'll need to repost the original Subscribe - // Request - responseHandler.handleRedirectResponse(response, pubSubData, channel); - break; - default: - // Consider all other status codes as errors, operation failed - // cases. - logger.error("Unexpected error response from server for PubSubResponse: " + response); - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Server responded with a status code of: " + response.getStatusCode())); - break; - } - } - - // Main method to handle consuming a message for a topic that the client is - // subscribed to. - public void handleSubscribeMessage(PubSubResponse response) { - if (logger.isDebugEnabled()) - logger.debug("Handling a Subscribe message in response: " + response + ", topic: " - + origSubData.topic.toStringUtf8() + ", subscriberId: " + origSubData.subscriberId.toStringUtf8()); - Message message = response.getMessage(); - // Consume the message asynchronously that the client is subscribed - // to. Do this only if delivery for the subscription has started and - // a MessageHandler has been registered for the TopicSubscriber. - if (messageHandler != null) { - asyncMessageConsume(message); - } else { - // MessageHandler has not yet been registered so queue up these - // messages for the Topic Subscription. Make the initial lazy - // creation of the message queue thread safe just so we don't - // run into a race condition where two simultaneous threads process - // a received message and both try to create a new instance of - // the message queue. Performance overhead should be okay - // because the delivery of the topic has not even started yet - // so these messages are not consumed and just buffered up here. - synchronized (this) { - if (subscribeMsgQueue == null) - subscribeMsgQueue = new LinkedList(); - } - if (logger.isDebugEnabled()) - logger - .debug("Message has arrived but Subscribe channel does not have a registered MessageHandler yet so queueing up the message: " - + message); - subscribeMsgQueue.add(message); - } - } - - /** - * Method called when a message arrives for a subscribe Channel and we want - * to consume it asynchronously via the registered MessageHandler (should - * not be null when called here). - * - * @param message - * Message from Subscribe Channel we want to consume. - */ - protected void asyncMessageConsume(Message message) { - if (logger.isDebugEnabled()) - logger.debug("Call the client app's MessageHandler asynchronously to consume the message: " + message - + ", topic: " + origSubData.topic.toStringUtf8() + ", subscriberId: " - + origSubData.subscriberId.toStringUtf8()); - // Add this "pending to be consumed" message to the outstandingMsgSet. - outstandingMsgSet.add(message); - // Check if we've exceeded the max size for the outstanding message set. - if (outstandingMsgSet.size() >= responseHandler.getConfiguration().getMaximumOutstandingMessages() - && subscribeChannel.isReadable()) { - // Too many outstanding messages so throttle it by setting the Netty - // Channel to not be readable. - if (logger.isDebugEnabled()) - logger.debug("Too many outstanding messages (" + outstandingMsgSet.size() - + ") so throttling the subscribe netty Channel"); - subscribeChannel.setReadable(false); - } - MessageConsumeData messageConsumeData = new MessageConsumeData(origSubData.topic, origSubData.subscriberId, - message); - messageHandler.consume(origSubData.topic, origSubData.subscriberId, message, responseHandler.getClient() - .getConsumeCallback(), messageConsumeData); - } - - /** - * Method called when the client app's MessageHandler has asynchronously - * completed consuming a subscribed message sent from the server. The - * contract with the client app is that messages sent to the handler to be - * consumed will have the callback response done in the same order. So if we - * asynchronously call the MessageHandler to consume messages #1-5, that - * should call the messageConsumed method here via the VoidCallback in the - * same order. To make this thread safe, since multiple outstanding messages - * could be consumed by the client app and then called back to here, make - * this method synchronized. - * - * @param message - * Message sent from server for topic subscription that has been - * consumed by the client. - */ - protected synchronized void messageConsumed(Message message) { - if (logger.isDebugEnabled()) - logger.debug("Message has been successfully consumed by the client app for message: " + message - + ", topic: " + origSubData.topic.toStringUtf8() + ", subscriberId: " - + origSubData.subscriberId.toStringUtf8()); - // Update the consumed messages buffer variables - if (responseHandler.getConfiguration().isAutoSendConsumeMessageEnabled()) { - // Update these variables only if we are auto-sending consume - // messages to the server. Otherwise the onus is on the client app - // to call the Subscriber consume API to let the server know which - // messages it has successfully consumed. - numConsumedMessagesInBuffer++; - lastMessageSeqId = message.getMsgId(); - } - // Remove this consumed message from the outstanding Message Set. - outstandingMsgSet.remove(message); - - // For consume response to server, there is a config param on how many - // messages to consume and buffer up before sending the consume request. - // We just need to keep a count of the number of messages consumed - // and the largest/latest msg ID seen so far in this batch. Messages - // should be delivered in order and without gaps. Do this only if - // auto-sending of consume messages is enabled. - if (responseHandler.getConfiguration().isAutoSendConsumeMessageEnabled() - && numConsumedMessagesInBuffer >= responseHandler.getConfiguration().getConsumedMessagesBufferSize()) { - // Send the consume request and reset the consumed messages buffer - // variables. We will use the same Channel created from the - // subscribe request for the TopicSubscriber. - if (logger.isDebugEnabled()) - logger - .debug("Consumed message buffer limit reached so send the Consume Request to the server with lastMessageSeqId: " - + lastMessageSeqId); - responseHandler.getSubscriber().doConsume(origSubData, subscribeChannel, lastMessageSeqId); - numConsumedMessagesInBuffer = 0; - lastMessageSeqId = null; - } - - // Check if we throttled message consumption previously when the - // outstanding message limit was reached. For now, only turn the - // delivery back on if there are no more outstanding messages to - // consume. We could make this a configurable parameter if needed. - if (!subscribeChannel.isReadable() && outstandingMsgSet.size() == 0) { - if (logger.isDebugEnabled()) - logger - .debug("Message consumption has caught up so okay to turn off throttling of messages on the subscribe channel for topic: " - + origSubData.topic.toStringUtf8() - + ", subscriberId: " - + origSubData.subscriberId.toStringUtf8()); - subscribeChannel.setReadable(true); - } - } - - /** - * Setter used for Subscribe flows when delivery for the subscription is - * started. This is used to register the MessageHandler needed to consumer - * the subscribed messages for the topic. - * - * @param messageHandler - * MessageHandler to register for this ResponseHandler instance. - */ - public void setMessageHandler(MessageHandler messageHandler) { - if (logger.isDebugEnabled()) - logger.debug("Setting the messageHandler for topic: " + origSubData.topic.toStringUtf8() - + ", subscriberId: " + origSubData.subscriberId.toStringUtf8()); - this.messageHandler = messageHandler; - // Once the MessageHandler is registered, see if we have any queued up - // subscription messages sent to us already from the server. If so, - // consume those first. Do this only if the MessageHandler registered is - // not null (since that would be the HedwigSubscriber.stopDelivery - // call). - if (messageHandler != null && subscribeMsgQueue != null && subscribeMsgQueue.size() > 0) { - if (logger.isDebugEnabled()) - logger.debug("Consuming " + subscribeMsgQueue.size() + " queued up messages for topic: " - + origSubData.topic.toStringUtf8() + ", subscriberId: " - + origSubData.subscriberId.toStringUtf8()); - for (Message message : subscribeMsgQueue) { - asyncMessageConsume(message); - } - // Now we can remove the queued up messages since they are all - // consumed. - subscribeMsgQueue.clear(); - } - } - - /** - * Getter for the MessageHandler that is set for this subscribe channel. - * - * @return The MessageHandler for consuming messages - */ - public MessageHandler getMessageHandler() { - return messageHandler; - } -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/UnsubscribeResponseHandler.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/UnsubscribeResponseHandler.java deleted file mode 100644 index 182518e5d81..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/handlers/UnsubscribeResponseHandler.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.handlers; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.ResponseHandler; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; - -public class UnsubscribeResponseHandler { - - private static Logger logger = Logger.getLogger(UnsubscribeResponseHandler.class); - - private final ResponseHandler responseHandler; - - public UnsubscribeResponseHandler(ResponseHandler responseHandler) { - this.responseHandler = responseHandler; - } - - // Main method to handle Unsubscribe Response messages from the server. - public void handleUnsubscribeResponse(PubSubResponse response, PubSubData pubSubData, Channel channel) - throws Exception { - if (logger.isDebugEnabled()) - logger.debug("Handling an Unsubscribe response: " + response + ", pubSubData: " + pubSubData + ", host: " - + HedwigClient.getHostFromChannel(channel)); - switch (response.getStatusCode()) { - case SUCCESS: - // For successful Unsubscribe requests, we can now safely close the - // Subscribe Channel and any cached data for that TopicSubscriber. - responseHandler.getSubscriber().closeSubscription(pubSubData.topic, pubSubData.subscriberId); - // Response was success so invoke the callback's operationFinished - // method. - pubSubData.callback.operationFinished(pubSubData.context, null); - break; - case CLIENT_NOT_SUBSCRIBED: - // For Unsubscribe requests, the server says that the client was - // never subscribed to the topic. - pubSubData.callback.operationFailed(pubSubData.context, new ClientNotSubscribedException( - "Client was never subscribed to topic: " + pubSubData.topic.toStringUtf8() + ", subscriberId: " - + pubSubData.subscriberId.toStringUtf8())); - break; - case SERVICE_DOWN: - // Response was service down failure so just invoke the callback's - // operationFailed method. - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Server responded with a SERVICE_DOWN status")); - break; - case NOT_RESPONSIBLE_FOR_TOPIC: - // Redirect response so we'll need to repost the original - // Unsubscribe Request - responseHandler.handleRedirectResponse(response, pubSubData, channel); - break; - default: - // Consider all other status codes as errors, operation failed - // cases. - logger.error("Unexpected error response from server for PubSubResponse: " + response); - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Server responded with a status code of: " + response.getStatusCode())); - break; - } - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ClientChannelPipelineFactory.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ClientChannelPipelineFactory.java deleted file mode 100644 index 3d3b8bc66ad..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ClientChannelPipelineFactory.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.ChannelPipelineFactory; -import org.jboss.netty.channel.Channels; -import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; -import org.jboss.netty.handler.codec.frame.LengthFieldPrepender; -import org.jboss.netty.handler.codec.protobuf.ProtobufDecoder; -import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder; -import org.jboss.netty.handler.ssl.SslHandler; - -import org.apache.hedwig.protocol.PubSubProtocol; - -public class ClientChannelPipelineFactory implements ChannelPipelineFactory { - - private HedwigClient client; - - public ClientChannelPipelineFactory(HedwigClient client) { - this.client = client; - } - - // Retrieve a ChannelPipeline from the factory. - public ChannelPipeline getPipeline() throws Exception { - // Create a new ChannelPipline using the factory method from the - // Channels helper class. - ChannelPipeline pipeline = Channels.pipeline(); - if (client.getSslFactory() != null) { - pipeline.addLast("ssl", new SslHandler(client.getSslFactory().getEngine())); - } - pipeline.addLast("lengthbaseddecoder", new LengthFieldBasedFrameDecoder(client.getConfiguration() - .getMaximumMessageSize(), 0, 4, 0, 4)); - pipeline.addLast("lengthprepender", new LengthFieldPrepender(4)); - - pipeline.addLast("protobufdecoder", new ProtobufDecoder(PubSubProtocol.PubSubResponse.getDefaultInstance())); - pipeline.addLast("protobufencoder", new ProtobufEncoder()); - - pipeline.addLast("responsehandler", new ResponseHandler(client)); - return pipeline; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ConnectCallback.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ConnectCallback.java deleted file mode 100644 index 9929ae003cc..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ConnectCallback.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import java.net.InetSocketAddress; -import java.util.LinkedList; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.exceptions.PubSubException.CouldNotConnectException; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class ConnectCallback implements ChannelFutureListener { - - private static Logger logger = Logger.getLogger(ConnectCallback.class); - - // Private member variables - private PubSubData pubSubData; - private InetSocketAddress host; - private final HedwigClient client; - private final HedwigPublisher pub; - private final HedwigSubscriber sub; - private final ClientConfiguration cfg; - - // Constructor - public ConnectCallback(PubSubData pubSubData, InetSocketAddress host, HedwigClient client) { - super(); - this.pubSubData = pubSubData; - this.host = host; - this.client = client; - this.pub = client.getPublisher(); - this.sub = client.getSubscriber(); - this.cfg = client.getConfiguration(); - } - - public void operationComplete(ChannelFuture future) throws Exception { - // If the client has stopped, there is no need to proceed with any - // callback logic here. - if (client.hasStopped()) - return; - - // Check if the connection to the server was done successfully. - if (!future.isSuccess()) { - logger.error("Error connecting to host: " + host); - // If we were not able to connect to the host, it could be down. - ByteString hostString = ByteString.copyFromUtf8(HedwigSocketAddress.sockAddrStr(host)); - if (pubSubData.connectFailedServers != null && pubSubData.connectFailedServers.contains(hostString)) { - // We've already tried to connect to this host before so just - // invoke the operationFailed callback. - logger.error("Error connecting to host more than once so just invoke the operationFailed callback!"); - pubSubData.callback.operationFailed(pubSubData.context, new CouldNotConnectException( - "Could not connect to host: " + host)); - } else { - if (logger.isDebugEnabled()) - logger.debug("Try to connect to server: " + host + " again for pubSubData: " + pubSubData); - // Keep track of this current server that we failed to connect - // to but retry the request on the default server host/VIP. - // The topic2Host mapping might need to be updated. - if (pubSubData.connectFailedServers == null) - pubSubData.connectFailedServers = new LinkedList(); - pubSubData.connectFailedServers.add(hostString); - client.doConnect(pubSubData, cfg.getDefaultServerHost()); - } - // Finished with failure logic so just return. - return; - } - - // Now that we have connected successfully to the server, see what type - // of PubSub request this was. - if (logger.isDebugEnabled()) - logger.debug("Connection to host: " + host + " was successful for pubSubData: " + pubSubData); - if (pubSubData.operationType.equals(OperationType.PUBLISH)) { - // Publish Request so store this Channel connection in the - // HedwigPublisher Map (if it doesn't exist yet) and then - // do the publish on the cached channel mapped to the host. - // Note that due to race concurrency situations, it is - // possible that the cached channel is not the same one - // as the channel established here. If that is the case, - // this channel will be closed but we'll always publish on the - // cached channel in the HedwigPublisher.host2Channel map. - pub.storeHost2ChannelMapping(future.getChannel()); - pub.doPublish(pubSubData, pub.host2Channel.get(HedwigClient.getHostFromChannel(future.getChannel()))); - } else if (pubSubData.operationType.equals(OperationType.UNSUBSCRIBE)) { - // Unsubscribe Request so store this Channel connection in the - // HedwigPublisher Map (if it doesn't exist yet) and then do the - // unsubscribe. Unsubscribe requests will share and reuse - // the netty Channel connections that Publish requests use. - pub.storeHost2ChannelMapping(future.getChannel()); - sub.doSubUnsub(pubSubData, pub.host2Channel.get(HedwigClient.getHostFromChannel(future.getChannel()))); - } else { - // Subscribe Request. We do not store the Channel connection yet for - // Subscribes here. This will be done only when we've found the - // right server topic master. That is only determined when we - // receive a successful server ack response to the Subscribe - // request (handled in ResponseHandler). There is no need to store - // the Unsubscribe channel connection as we won't use it again. - sub.doSubUnsub(pubSubData, future.getChannel()); - } - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigClient.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigClient.java deleted file mode 100644 index 6984a596653..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigClient.java +++ /dev/null @@ -1,359 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import java.net.InetSocketAddress; -import java.util.LinkedList; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.log4j.Logger; -import org.jboss.netty.bootstrap.ClientBootstrap; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFactory; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.handlers.MessageConsumeCallback; -import org.apache.hedwig.client.ssl.SslClientContextFactory; -import org.apache.hedwig.exceptions.PubSubException.UncertainStateException; - -/** - * This is a top level Hedwig Client class that encapsulates the common - * functionality needed for both Publish and Subscribe operations. - * - */ -public class HedwigClient { - - private static final Logger logger = Logger.getLogger(HedwigClient.class); - - // Global counter used for generating unique transaction ID's for - // publish and subscribe requests - protected final AtomicLong globalCounter = new AtomicLong(); - // Static String constants - protected static final String COLON = ":"; - - // The Netty socket factory for making connections to the server. - protected final ChannelFactory socketFactory; - // Whether the socket factory is one we created or is owned by whoever - // instantiated us. - protected boolean ownChannelFactory = false; - - // PipelineFactory to create netty client channels to the appropriate server - private ClientChannelPipelineFactory pipelineFactory; - - // Concurrent Map to store the mapping from the Topic to the Host. - // This could change over time since servers can drop mastership of topics - // for load balancing or failover. If a server host ever goes down, we'd - // also want to remove all topic mappings the host was responsible for. - // The second Map is used as the inverted version of the first one. - protected final ConcurrentMap topic2Host = new ConcurrentHashMap(); - private final ConcurrentMap> host2Topics = new ConcurrentHashMap>(); - - // Each client instantiation will have a Timer for running recurring - // threads. One such timer task thread to is to timeout long running - // PubSubRequests that are waiting for an ack response from the server. - private final Timer clientTimer = new Timer(true); - - // Boolean indicating if the client is running or has stopped. - // Once we stop the client, we should sidestep all of the connect, - // write callback and channel disconnected logic. - private boolean isStopped = false; - - private HedwigSubscriber sub; - private final HedwigPublisher pub; - private final ClientConfiguration cfg; - private final MessageConsumeCallback consumeCb; - private SslClientContextFactory sslFactory = null; - - // Base constructor that takes in a Configuration object. - // This will create its own client socket channel factory. - public HedwigClient(ClientConfiguration cfg) { - this(cfg, new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); - ownChannelFactory = true; - } - - // Constructor that takes in a Configuration object and a ChannelFactory - // that has already been instantiated by the caller. - public HedwigClient(ClientConfiguration cfg, ChannelFactory socketFactory) { - this.cfg = cfg; - this.socketFactory = socketFactory; - pub = new HedwigPublisher(this); - sub = new HedwigSubscriber(this); - pipelineFactory = new ClientChannelPipelineFactory(this); - consumeCb = new MessageConsumeCallback(this); - if (cfg.isSSLEnabled()) { - sslFactory = new SslClientContextFactory(cfg); - } - // Schedule all of the client timer tasks. Currently we only have the - // Request Timeout task. - clientTimer.schedule(new PubSubRequestTimeoutTask(), 0, cfg.getTimeoutThreadRunInterval()); - } - - // Public getters for the various components of a client. - public ClientConfiguration getConfiguration() { - return cfg; - } - - public HedwigSubscriber getSubscriber() { - return sub; - } - - // Protected method to set the subscriber. This is needed currently for hub - // versions of the client subscriber. - protected void setSubscriber(HedwigSubscriber sub) { - this.sub = sub; - } - - public HedwigPublisher getPublisher() { - return pub; - } - - public MessageConsumeCallback getConsumeCallback() { - return consumeCb; - } - - public SslClientContextFactory getSslFactory() { - return sslFactory; - } - - // We need to deal with the possible problem of a PubSub request being - // written to successfully to the server host but for some reason, the - // ack message back never comes. What could happen is that the VoidCallback - // stored in the ResponseHandler.txn2PublishData map will never be called. - // We should have a configured timeout so if that passes from the time a - // write was successfully done to the server, we can fail this async PubSub - // transaction. The caller could possibly redo the transaction if needed at - // a later time. Creating a timeout cleaner TimerTask to do this here. - class PubSubRequestTimeoutTask extends TimerTask { - /** - * Implement the TimerTask's abstract run method. - */ - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Running the PubSubRequest Timeout Task"); - // Loop through all outstanding PubSubData requests and check if - // the requestWriteTime has timed out compared to the current time. - long curTime = System.currentTimeMillis(); - long timeoutInterval = cfg.getServerAckResponseTimeout(); - - // First check the ResponseHandlers associated with cached - // channels in HedwigPublisher.host2Channel. This stores the - // channels used for Publish and Unsubscribe requests. - for (Channel channel : pub.host2Channel.values()) { - ResponseHandler responseHandler = getResponseHandlerFromChannel(channel); - for (PubSubData pubSubData : responseHandler.txn2PubSubData.values()) { - checkPubSubDataToTimeOut(pubSubData, responseHandler, curTime, timeoutInterval); - } - } - // Now do the same for the cached channels in - // HedwigSubscriber.topicSubscriber2Channel. This stores the - // channels used exclusively for Subscribe requests. - for (Channel channel : sub.topicSubscriber2Channel.values()) { - ResponseHandler responseHandler = getResponseHandlerFromChannel(channel); - for (PubSubData pubSubData : responseHandler.txn2PubSubData.values()) { - checkPubSubDataToTimeOut(pubSubData, responseHandler, curTime, timeoutInterval); - } - } - } - - private void checkPubSubDataToTimeOut(PubSubData pubSubData, ResponseHandler responseHandler, long curTime, - long timeoutInterval) { - if (curTime > pubSubData.requestWriteTime + timeoutInterval) { - // Current PubSubRequest has timed out so remove it from the - // ResponseHandler's map and invoke the VoidCallback's - // operationFailed method. - logger.error("Current PubSubRequest has timed out for pubSubData: " + pubSubData); - responseHandler.txn2PubSubData.remove(pubSubData.txnId); - pubSubData.callback.operationFailed(pubSubData.context, new UncertainStateException( - "Server ack response never received so PubSubRequest has timed out!")); - } - } - } - - // When we are done with the client, this is a clean way to gracefully close - // all channels/sockets created by the client and to also release all - // resources used by netty. - public void stop() { - logger.info("Stopping the client!"); - // Set the client boolean flag to indicate the client has stopped. - isStopped = true; - // Stop the timer and all timer task threads. - clientTimer.cancel(); - // Close all of the open Channels. - for (Channel channel : pub.host2Channel.values()) { - getResponseHandlerFromChannel(channel).channelClosedExplicitly = true; - channel.close().awaitUninterruptibly(); - } - for (Channel channel : sub.topicSubscriber2Channel.values()) { - getResponseHandlerFromChannel(channel).channelClosedExplicitly = true; - channel.close().awaitUninterruptibly(); - } - // Clear out all Maps. - topic2Host.clear(); - host2Topics.clear(); - pub.host2Channel.clear(); - sub.topicSubscriber2Channel.clear(); - // Release resources used by the ChannelFactory on the client if we are - // the owner that created it. - if (ownChannelFactory) { - socketFactory.releaseExternalResources(); - } - logger.info("Completed stopping the client!"); - } - - /** - * This is a helper method to do the connect attempt to the server given the - * inputted host/port. This can be used to connect to the default server - * host/port which is the VIP. That will pick a server in the cluster at - * random to connect to for the initial PubSub attempt (with redirect logic - * being done at the server side). Additionally, this could be called after - * the client makes an initial PubSub attempt at a server, and is redirected - * to the one that is responsible for the topic. Once the connect to the - * server is done, we will perform the corresponding PubSub write on that - * channel. - * - * @param pubSubData - * PubSub call's data wrapper object - * @param serverHost - * Input server host to connect to of type InetSocketAddress - */ - public void doConnect(PubSubData pubSubData, InetSocketAddress serverHost) { - if (logger.isDebugEnabled()) - logger.debug("Connecting to host: " + serverHost + " with pubSubData: " + pubSubData); - // Set up the ClientBootStrap so we can create a new Channel connection - // to the server. - ClientBootstrap bootstrap = new ClientBootstrap(socketFactory); - bootstrap.setPipelineFactory(pipelineFactory); - bootstrap.setOption("tcpNoDelay", true); - bootstrap.setOption("keepAlive", true); - - // Start the connection attempt to the input server host. - ChannelFuture future = bootstrap.connect(serverHost); - future.addListener(new ConnectCallback(pubSubData, serverHost, this)); - } - - /** - * Helper method to store the topic2Host mapping in the HedwigClient cache - * map. This method is assumed to be called when we've done a successful - * connection to the correct server topic master. - * - * @param pubSubData - * PubSub wrapper data - * @param channel - * Netty Channel - */ - protected void storeTopic2HostMapping(PubSubData pubSubData, Channel channel) { - // Retrieve the server host that we've connected to and store the - // mapping from the topic to this host. For all other non-redirected - // server statuses, we consider that as a successful connection to the - // correct topic master. - InetSocketAddress host = getHostFromChannel(channel); - if (topic2Host.containsKey(pubSubData.topic) && topic2Host.get(pubSubData.topic).equals(host)) { - // Entry in map exists for the topic but it is the same as the - // current host. In this case there is nothing to do. - return; - } - - // Store the relevant mappings for this topic and host combination. - if (logger.isDebugEnabled()) - logger.debug("Storing info for topic: " + pubSubData.topic.toStringUtf8() + ", old host: " - + topic2Host.get(pubSubData.topic) + ", new host: " + host); - topic2Host.put(pubSubData.topic, host); - if (host2Topics.containsKey(host)) { - host2Topics.get(host).add(pubSubData.topic); - } else { - LinkedList topicsList = new LinkedList(); - topicsList.add(pubSubData.topic); - host2Topics.put(host, topicsList); - } - } - - /** - * Helper static method to get the String Hostname:Port from a netty - * Channel. Assumption is that the netty Channel was originally created with - * an InetSocketAddress. This is true with the Hedwig netty implementation. - * - * @param channel - * Netty channel to extract the hostname and port from. - * @return String representation of the Hostname:Port from the Netty Channel - */ - public static InetSocketAddress getHostFromChannel(Channel channel) { - return (InetSocketAddress) channel.getRemoteAddress(); - } - - /** - * Helper static method to get the ResponseHandler instance from a Channel - * via the ChannelPipeline it is associated with. The assumption is that the - * last ChannelHandler tied to the ChannelPipeline is the ResponseHandler. - * - * @param channel - * Channel we are retrieving the ResponseHandler instance for - * @return ResponseHandler Instance tied to the Channel's Pipeline - */ - public static ResponseHandler getResponseHandlerFromChannel(Channel channel) { - return (ResponseHandler) channel.getPipeline().getLast(); - } - - // Public getter for entries in the topic2Host Map. - public InetSocketAddress getHostForTopic(ByteString topic) { - return topic2Host.get(topic); - } - - // If a server host goes down or the channel to it gets disconnected, - // we want to clear out all relevant cached information. We'll - // need to remove all of the topic mappings that the host was - // responsible for. - public void clearAllTopicsForHost(InetSocketAddress host) { - if (logger.isDebugEnabled()) - logger.debug("Clearing all topics for host: " + host); - // For each of the topics that the host was responsible for, - // remove it from the topic2Host mapping. - if (host2Topics.containsKey(host)) { - for (ByteString topic : host2Topics.get(host)) { - if (logger.isDebugEnabled()) - logger.debug("Removing mapping for topic: " + topic.toStringUtf8() + " from host: " + host); - topic2Host.remove(topic); - } - // Now it is safe to remove the host2Topics mapping entry. - host2Topics.remove(host); - } - } - - // Public getter to see if the client has been stopped. - public boolean hasStopped() { - return isStopped; - } - - // Public getter to get the client's Timer object. - // This is so we can reuse this and not have to create multiple Timer - // objects. - public Timer getClientTimer() { - return clientTimer; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigPublisher.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigPublisher.java deleted file mode 100644 index 458ab9fa45e..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigPublisher.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import java.net.InetSocketAddress; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.Publisher; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.handlers.PubSubCallback; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.CouldNotConnectException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PublishRequest; -import org.apache.hedwig.util.Callback; - -/** - * This is the Hedwig Netty specific implementation of the Publisher interface. - * - */ -public class HedwigPublisher implements Publisher { - - private static Logger logger = Logger.getLogger(HedwigPublisher.class); - - // Concurrent Map to store the mappings for a given Host (Hostname:Port) to - // the Channel that has been established for it previously. This channel - // will be used whenever we publish on a topic that the server is the master - // of currently. The channels used here will only be used for publish and - // unsubscribe requests. - protected final ConcurrentMap host2Channel = new ConcurrentHashMap(); - - private final HedwigClient client; - private final ClientConfiguration cfg; - - protected HedwigPublisher(HedwigClient client) { - this.client = client; - this.cfg = client.getConfiguration(); - } - - public void publish(ByteString topic, Message msg) throws CouldNotConnectException, ServiceDownException { - if (logger.isDebugEnabled()) - logger.debug("Calling a sync publish for topic: " + topic.toStringUtf8() + ", msg: " + msg); - PubSubData pubSubData = new PubSubData(topic, msg, null, OperationType.PUBLISH, null, null, null); - synchronized (pubSubData) { - PubSubCallback pubSubCallback = new PubSubCallback(pubSubData); - asyncPublish(topic, msg, pubSubCallback, null); - try { - while (!pubSubData.isDone) - pubSubData.wait(); - } catch (InterruptedException e) { - throw new ServiceDownException("Interrupted Exception while waiting for async publish call"); - } - // Check from the PubSubCallback if it was successful or not. - if (!pubSubCallback.getIsCallSuccessful()) { - // See what the exception was that was thrown when the operation - // failed. - PubSubException failureException = pubSubCallback.getFailureException(); - if (failureException == null) { - // This should not happen as the operation failed but a null - // PubSubException was passed. Log a warning message but - // throw a generic ServiceDownException. - logger.error("Sync Publish operation failed but no PubSubException was passed!"); - throw new ServiceDownException("Server ack response to publish request is not successful"); - } - // For the expected exceptions that could occur, just rethrow - // them. - else if (failureException instanceof CouldNotConnectException) { - throw (CouldNotConnectException) failureException; - } else if (failureException instanceof ServiceDownException) { - throw (ServiceDownException) failureException; - } else { - // For other types of PubSubExceptions, just throw a generic - // ServiceDownException but log a warning message. - logger.error("Unexpected exception type when a sync publish operation failed: " + failureException); - throw new ServiceDownException("Server ack response to publish request is not successful"); - } - } - } - } - - public void asyncPublish(ByteString topic, Message msg, Callback callback, Object context) { - if (logger.isDebugEnabled()) - logger.debug("Calling an async publish for topic: " + topic.toStringUtf8() + ", msg: " + msg); - // Check if we already have a Channel connection set up to the server - // for the given Topic. - PubSubData pubSubData = new PubSubData(topic, msg, null, OperationType.PUBLISH, null, callback, context); - if (client.topic2Host.containsKey(topic)) { - InetSocketAddress host = client.topic2Host.get(topic); - if (host2Channel.containsKey(host)) { - // We already have the Channel connection for the server host so - // do the publish directly. We will deal with redirect logic - // later on if that server is no longer the current host for - // the topic. - doPublish(pubSubData, host2Channel.get(host)); - } else { - // We have a mapping for the topic to host but don't have a - // Channel for that server. This can happen if the Channel - // is disconnected for some reason. Do the connect then to - // the specified server host to create a new Channel connection. - client.doConnect(pubSubData, host); - } - } else { - // Server host for the given topic is not known yet so use the - // default server host/port as defined in the configs. This should - // point to the server VIP which would redirect to a random server - // (which might not be the server hosting the topic). - client.doConnect(pubSubData, cfg.getDefaultServerHost()); - } - } - - /** - * This is a helper method to write the actual publish message once the - * client is connected to the server and a Channel is available. - * - * @param pubSubData - * Publish call's data wrapper object - * @param channel - * Netty I/O channel for communication between the client and - * server - */ - protected void doPublish(PubSubData pubSubData, Channel channel) { - // Create a PubSubRequest - PubSubRequest.Builder pubsubRequestBuilder = PubSubRequest.newBuilder(); - pubsubRequestBuilder.setProtocolVersion(ProtocolVersion.VERSION_ONE); - pubsubRequestBuilder.setType(OperationType.PUBLISH); - if (pubSubData.triedServers != null && pubSubData.triedServers.size() > 0) { - pubsubRequestBuilder.addAllTriedServers(pubSubData.triedServers); - } - long txnId = client.globalCounter.incrementAndGet(); - pubsubRequestBuilder.setTxnId(txnId); - pubsubRequestBuilder.setShouldClaim(pubSubData.shouldClaim); - pubsubRequestBuilder.setTopic(pubSubData.topic); - - // Now create the PublishRequest - PublishRequest.Builder publishRequestBuilder = PublishRequest.newBuilder(); - - publishRequestBuilder.setMsg(pubSubData.msg); - - // Set the PublishRequest into the outer PubSubRequest - pubsubRequestBuilder.setPublishRequest(publishRequestBuilder); - - // Update the PubSubData with the txnId and the requestWriteTime - pubSubData.txnId = txnId; - pubSubData.requestWriteTime = System.currentTimeMillis(); - - // Before we do the write, store this information into the - // ResponseHandler so when the server responds, we know what - // appropriate Callback Data to invoke for the given txn ID. - HedwigClient.getResponseHandlerFromChannel(channel).txn2PubSubData.put(txnId, pubSubData); - - // Finally, write the Publish request through the Channel. - if (logger.isDebugEnabled()) - logger.debug("Writing a Publish request to host: " + HedwigClient.getHostFromChannel(channel) - + " for pubSubData: " + pubSubData); - ChannelFuture future = channel.write(pubsubRequestBuilder.build()); - future.addListener(new WriteCallback(pubSubData, client)); - } - - // Synchronized method to store the host2Channel mapping (if it doesn't - // exist yet). Retrieve the hostname info from the Channel created via the - // RemoteAddress tied to it. - protected synchronized void storeHost2ChannelMapping(Channel channel) { - InetSocketAddress host = HedwigClient.getHostFromChannel(channel); - if (!host2Channel.containsKey(host)) { - if (logger.isDebugEnabled()) - logger.debug("Storing a new Channel mapping for host: " + host); - host2Channel.put(host, channel); - } else { - // If we've reached here, that means we already have a Channel - // mapping for the given host. This should ideally not happen - // and it means we are creating another Channel to a server host - // to publish on when we could have used an existing one. This could - // happen due to a race condition if initially multiple concurrent - // threads are publishing on the same topic and no Channel exists - // currently to the server. We are not synchronizing this initial - // creation of Channels to a given host for performance. - // Another possible way to have redundant Channels created is if - // a new topic is being published to, we connect to the default - // server host which should be a VIP that redirects to a "real" - // server host. Since we don't know beforehand what is the full - // set of server hosts, we could be redirected to a server that - // we already have a channel connection to from a prior existing - // topic. Close these redundant channels as they won't be used. - if (logger.isDebugEnabled()) - logger.debug("Channel mapping to host: " + host + " already exists so no need to store it."); - HedwigClient.getResponseHandlerFromChannel(channel).channelClosedExplicitly = true; - channel.close(); - } - } - - // Public getter for entries in the host2Channel Map. - // This is used for classes that need this information but are not in the - // same classpath. - public Channel getChannelForHost(InetSocketAddress host) { - return host2Channel.get(host); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigSubscriber.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigSubscriber.java deleted file mode 100644 index 0343c3ef7b0..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/HedwigSubscriber.java +++ /dev/null @@ -1,585 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import java.net.InetSocketAddress; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.client.exceptions.InvalidSubscriberIdException; -import org.apache.hedwig.client.handlers.PubSubCallback; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ClientAlreadySubscribedException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.exceptions.PubSubException.CouldNotConnectException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.ConsumeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.UnsubscribeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.protoextensions.SubscriptionStateUtils; -import org.apache.hedwig.util.Callback; - -/** - * This is the Hedwig Netty specific implementation of the Subscriber interface. - * - */ -public class HedwigSubscriber implements Subscriber { - - private static Logger logger = Logger.getLogger(HedwigSubscriber.class); - - // Concurrent Map to store the cached Channel connections on the client side - // to a server host for a given Topic + SubscriberId combination. For each - // TopicSubscriber, we want a unique Channel connection to the server for - // it. We can also get the ResponseHandler tied to the Channel via the - // Channel Pipeline. - protected final ConcurrentMap topicSubscriber2Channel = new ConcurrentHashMap(); - - protected final HedwigClient client; - protected final ClientConfiguration cfg; - - public HedwigSubscriber(HedwigClient client) { - this.client = client; - this.cfg = client.getConfiguration(); - } - - // Private method that holds the common logic for doing synchronous - // Subscribe or Unsubscribe requests. This is for code reuse since these - // two flows are very similar. The assumption is that the input - // OperationType is either SUBSCRIBE or UNSUBSCRIBE. - private void subUnsub(ByteString topic, ByteString subscriberId, OperationType operationType, - CreateOrAttach createOrAttach) throws CouldNotConnectException, ClientAlreadySubscribedException, - ClientNotSubscribedException, ServiceDownException { - if (logger.isDebugEnabled()) - logger.debug("Calling a sync subUnsub request for topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8() + ", operationType: " + operationType + ", createOrAttach: " - + createOrAttach); - PubSubData pubSubData = new PubSubData(topic, null, subscriberId, operationType, createOrAttach, null, null); - synchronized (pubSubData) { - PubSubCallback pubSubCallback = new PubSubCallback(pubSubData); - asyncSubUnsub(topic, subscriberId, pubSubCallback, null, operationType, createOrAttach); - try { - while (!pubSubData.isDone) - pubSubData.wait(); - } catch (InterruptedException e) { - throw new ServiceDownException("Interrupted Exception while waiting for async subUnsub call"); - } - // Check from the PubSubCallback if it was successful or not. - if (!pubSubCallback.getIsCallSuccessful()) { - // See what the exception was that was thrown when the operation - // failed. - PubSubException failureException = pubSubCallback.getFailureException(); - if (failureException == null) { - // This should not happen as the operation failed but a null - // PubSubException was passed. Log a warning message but - // throw a generic ServiceDownException. - logger.error("Sync SubUnsub operation failed but no PubSubException was passed!"); - throw new ServiceDownException("Server ack response to SubUnsub request is not successful"); - } - // For the expected exceptions that could occur, just rethrow - // them. - else if (failureException instanceof CouldNotConnectException) - throw (CouldNotConnectException) failureException; - else if (failureException instanceof ClientAlreadySubscribedException) - throw (ClientAlreadySubscribedException) failureException; - else if (failureException instanceof ClientNotSubscribedException) - throw (ClientNotSubscribedException) failureException; - else if (failureException instanceof ServiceDownException) - throw (ServiceDownException) failureException; - else { - logger.error("Unexpected PubSubException thrown: " + failureException.toString()); - // Throw a generic ServiceDownException but wrap the - // original PubSubException within it. - throw new ServiceDownException(failureException); - } - } - } - } - - // Private method that holds the common logic for doing asynchronous - // Subscribe or Unsubscribe requests. This is for code reuse since these two - // flows are very similar. The assumption is that the input OperationType is - // either SUBSCRIBE or UNSUBSCRIBE. - private void asyncSubUnsub(ByteString topic, ByteString subscriberId, Callback callback, Object context, - OperationType operationType, CreateOrAttach createOrAttach) { - if (logger.isDebugEnabled()) - logger.debug("Calling an async subUnsub request for topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8() + ", operationType: " + operationType + ", createOrAttach: " - + createOrAttach); - // Check if we know which server host is the master for the topic we are - // subscribing to. - PubSubData pubSubData = new PubSubData(topic, null, subscriberId, operationType, createOrAttach, callback, - context); - if (client.topic2Host.containsKey(topic)) { - InetSocketAddress host = client.topic2Host.get(topic); - if (operationType.equals(OperationType.UNSUBSCRIBE) && client.getPublisher().host2Channel.containsKey(host)) { - // For unsubscribes, we can reuse the channel connections to the - // server host that are cached for publishes. For publish and - // unsubscribe flows, we will thus use the same Channels and - // will cache and store them during the ConnectCallback. - doSubUnsub(pubSubData, client.getPublisher().host2Channel.get(host)); - } else { - // We know which server host is the master for the topic so - // connect to that first. For subscribes, we want a new channel - // connection each time for the TopicSubscriber. If the - // TopicSubscriber is already connected and subscribed, - // we assume the server will respond with an appropriate status - // indicating this. For unsubscribes, it is possible that the - // client is subscribed to the topic already but does not - // have a Channel connection yet to the server host. e.g. Client - // goes down and comes back up but client side soft state memory - // does not have the netty Channel connection anymore. - client.doConnect(pubSubData, host); - } - } else { - // Server host for the given topic is not known yet so use the - // default server host/port as defined in the configs. This should - // point to the server VIP which would redirect to a random server - // (which might not be the server hosting the topic). - client.doConnect(pubSubData, cfg.getDefaultServerHost()); - } - } - - public void subscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode) - throws CouldNotConnectException, ClientAlreadySubscribedException, ServiceDownException, - InvalidSubscriberIdException { - subscribe(topic, subscriberId, mode, false); - } - - protected void subscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode, boolean isHub) - throws CouldNotConnectException, ClientAlreadySubscribedException, ServiceDownException, - InvalidSubscriberIdException { - // Validate that the format of the subscriberId is valid either as a - // local or hub subscriber. - if (!isValidSubscriberId(subscriberId, isHub)) { - throw new InvalidSubscriberIdException("SubscriberId passed is not valid: " + subscriberId.toStringUtf8() - + ", isHub: " + isHub); - } - try { - subUnsub(topic, subscriberId, OperationType.SUBSCRIBE, mode); - } catch (ClientNotSubscribedException e) { - logger.error("Unexpected Exception thrown: " + e.toString()); - // This exception should never be thrown here. But just in case, - // throw a generic ServiceDownException but wrap the original - // Exception within it. - throw new ServiceDownException(e); - } - } - - public void asyncSubscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode, Callback callback, - Object context) { - asyncSubscribe(topic, subscriberId, mode, callback, context, false); - } - - protected void asyncSubscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode, - Callback callback, Object context, boolean isHub) { - // Validate that the format of the subscriberId is valid either as a - // local or hub subscriber. - if (!isValidSubscriberId(subscriberId, isHub)) { - callback.operationFailed(context, new ServiceDownException(new InvalidSubscriberIdException( - "SubscriberId passed is not valid: " + subscriberId.toStringUtf8() + ", isHub: " + isHub))); - return; - } - asyncSubUnsub(topic, subscriberId, callback, context, OperationType.SUBSCRIBE, mode); - } - - public void unsubscribe(ByteString topic, ByteString subscriberId) throws CouldNotConnectException, - ClientNotSubscribedException, ServiceDownException, InvalidSubscriberIdException { - unsubscribe(topic, subscriberId, false); - } - - protected void unsubscribe(ByteString topic, ByteString subscriberId, boolean isHub) - throws CouldNotConnectException, ClientNotSubscribedException, ServiceDownException, - InvalidSubscriberIdException { - // Validate that the format of the subscriberId is valid either as a - // local or hub subscriber. - if (!isValidSubscriberId(subscriberId, isHub)) { - throw new InvalidSubscriberIdException("SubscriberId passed is not valid: " + subscriberId.toStringUtf8() - + ", isHub: " + isHub); - } - // Synchronously close the subscription on the client side. Even - // if the unsubscribe request to the server errors out, we won't be - // delivering messages for this subscription to the client. The client - // can later retry the unsubscribe request to the server so they are - // "fully" unsubscribed from the given topic. - closeSubscription(topic, subscriberId); - try { - subUnsub(topic, subscriberId, OperationType.UNSUBSCRIBE, null); - } catch (ClientAlreadySubscribedException e) { - logger.error("Unexpected Exception thrown: " + e.toString()); - // This exception should never be thrown here. But just in case, - // throw a generic ServiceDownException but wrap the original - // Exception within it. - throw new ServiceDownException(e); - } - } - - public void asyncUnsubscribe(final ByteString topic, final ByteString subscriberId, final Callback callback, - final Object context) { - asyncUnsubscribe(topic, subscriberId, callback, context, false); - } - - protected void asyncUnsubscribe(final ByteString topic, final ByteString subscriberId, - final Callback callback, final Object context, boolean isHub) { - // Validate that the format of the subscriberId is valid either as a - // local or hub subscriber. - if (!isValidSubscriberId(subscriberId, isHub)) { - callback.operationFailed(context, new ServiceDownException(new InvalidSubscriberIdException( - "SubscriberId passed is not valid: " + subscriberId.toStringUtf8() + ", isHub: " + isHub))); - return; - } - // Asynchronously close the subscription. On the callback to that - // operation once it completes, post the async unsubscribe request. - asyncCloseSubscription(topic, subscriberId, new Callback() { - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - asyncSubUnsub(topic, subscriberId, callback, context, OperationType.UNSUBSCRIBE, null); - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - callback.operationFailed(context, exception); - } - }, null); - } - - // This is a helper method to determine if a subscriberId is valid as either - // a hub or local subscriber - private boolean isValidSubscriberId(ByteString subscriberId, boolean isHub) { - if ((isHub && !SubscriptionStateUtils.isHubSubscriber(subscriberId)) - || (!isHub && SubscriptionStateUtils.isHubSubscriber(subscriberId))) - return false; - else - return true; - } - - public void consume(ByteString topic, ByteString subscriberId, MessageSeqId messageSeqId) - throws ClientNotSubscribedException { - if (logger.isDebugEnabled()) - logger.debug("Calling consume for topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8() + ", messageSeqId: " + messageSeqId); - TopicSubscriber topicSubscriber = new TopicSubscriber(topic, subscriberId); - // Check that this topic subscription on the client side exists. - if (!topicSubscriber2Channel.containsKey(topicSubscriber)) { - throw new ClientNotSubscribedException( - "Cannot send consume message since client is not subscribed to topic: " + topic.toStringUtf8() - + ", subscriberId: " + subscriberId.toStringUtf8()); - } - PubSubData pubSubData = new PubSubData(topic, null, subscriberId, OperationType.CONSUME, null, null, null); - // Send the consume message to the server using the same subscribe - // channel that the topic subscription uses. - doConsume(pubSubData, topicSubscriber2Channel.get(topicSubscriber), messageSeqId); - } - - /** - * This is a helper method to write the actual subscribe/unsubscribe message - * once the client is connected to the server and a Channel is available. - * - * @param pubSubData - * Subscribe/Unsubscribe call's data wrapper object. We assume - * that the operationType field is either SUBSCRIBE or - * UNSUBSCRIBE. - * @param channel - * Netty I/O channel for communication between the client and - * server - */ - protected void doSubUnsub(PubSubData pubSubData, Channel channel) { - // Create a PubSubRequest - PubSubRequest.Builder pubsubRequestBuilder = PubSubRequest.newBuilder(); - pubsubRequestBuilder.setProtocolVersion(ProtocolVersion.VERSION_ONE); - pubsubRequestBuilder.setType(pubSubData.operationType); - if (pubSubData.triedServers != null && pubSubData.triedServers.size() > 0) { - pubsubRequestBuilder.addAllTriedServers(pubSubData.triedServers); - } - long txnId = client.globalCounter.incrementAndGet(); - pubsubRequestBuilder.setTxnId(txnId); - pubsubRequestBuilder.setShouldClaim(pubSubData.shouldClaim); - pubsubRequestBuilder.setTopic(pubSubData.topic); - - // Create either the Subscribe or Unsubscribe Request - if (pubSubData.operationType.equals(OperationType.SUBSCRIBE)) { - // Create the SubscribeRequest - SubscribeRequest.Builder subscribeRequestBuilder = SubscribeRequest.newBuilder(); - subscribeRequestBuilder.setSubscriberId(pubSubData.subscriberId); - subscribeRequestBuilder.setCreateOrAttach(pubSubData.createOrAttach); - // For now, all subscribes should wait for all cross-regional - // subscriptions to be established before returning. - subscribeRequestBuilder.setSynchronous(true); - - // Set the SubscribeRequest into the outer PubSubRequest - pubsubRequestBuilder.setSubscribeRequest(subscribeRequestBuilder); - } else { - // Create the UnSubscribeRequest - UnsubscribeRequest.Builder unsubscribeRequestBuilder = UnsubscribeRequest.newBuilder(); - unsubscribeRequestBuilder.setSubscriberId(pubSubData.subscriberId); - - // Set the UnsubscribeRequest into the outer PubSubRequest - pubsubRequestBuilder.setUnsubscribeRequest(unsubscribeRequestBuilder); - } - - // Update the PubSubData with the txnId and the requestWriteTime - pubSubData.txnId = txnId; - pubSubData.requestWriteTime = System.currentTimeMillis(); - - // Before we do the write, store this information into the - // ResponseHandler so when the server responds, we know what - // appropriate Callback Data to invoke for the given txn ID. - HedwigClient.getResponseHandlerFromChannel(channel).txn2PubSubData.put(txnId, pubSubData); - - // Finally, write the Subscribe request through the Channel. - if (logger.isDebugEnabled()) - logger.debug("Writing a SubUnsub request to host: " + HedwigClient.getHostFromChannel(channel) - + " for pubSubData: " + pubSubData); - ChannelFuture future = channel.write(pubsubRequestBuilder.build()); - future.addListener(new WriteCallback(pubSubData, client)); - } - - /** - * This is a helper method to write a consume message to the server after a - * subscribe Channel connection is made to the server and messages are being - * consumed by the client. - * - * @param pubSubData - * Consume call's data wrapper object. We assume that the - * operationType field is CONSUME. - * @param channel - * Netty I/O channel for communication between the client and - * server - * @param messageSeqId - * Message Seq ID for the latest/last message the client has - * consumed. - */ - public void doConsume(final PubSubData pubSubData, final Channel channel, final MessageSeqId messageSeqId) { - // Create a PubSubRequest - PubSubRequest.Builder pubsubRequestBuilder = PubSubRequest.newBuilder(); - pubsubRequestBuilder.setProtocolVersion(ProtocolVersion.VERSION_ONE); - pubsubRequestBuilder.setType(OperationType.CONSUME); - long txnId = client.globalCounter.incrementAndGet(); - pubsubRequestBuilder.setTxnId(txnId); - pubsubRequestBuilder.setTopic(pubSubData.topic); - - // Create the ConsumeRequest - ConsumeRequest.Builder consumeRequestBuilder = ConsumeRequest.newBuilder(); - consumeRequestBuilder.setSubscriberId(pubSubData.subscriberId); - consumeRequestBuilder.setMsgId(messageSeqId); - - // Set the ConsumeRequest into the outer PubSubRequest - pubsubRequestBuilder.setConsumeRequest(consumeRequestBuilder); - - // For Consume requests, we will send them from the client in a fire and - // forget manner. We are not expecting the server to send back an ack - // response so no need to register this in the ResponseHandler. There - // are no callbacks to invoke since this isn't a client initiated - // action. Instead, just have a future listener that will log an error - // message if there was a problem writing the consume request. - if (logger.isDebugEnabled()) - logger.debug("Writing a Consume request to host: " + HedwigClient.getHostFromChannel(channel) - + " with messageSeqId: " + messageSeqId + " for pubSubData: " + pubSubData); - ChannelFuture future = channel.write(pubsubRequestBuilder.build()); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - logger.error("Error writing a Consume request to host: " + HedwigClient.getHostFromChannel(channel) - + " with messageSeqId: " + messageSeqId + " for pubSubData: " + pubSubData); - } - } - }); - - } - - public boolean hasSubscription(ByteString topic, ByteString subscriberId) throws CouldNotConnectException, - ServiceDownException { - // The subscription type of info should be stored on the server end, not - // the client side. Eventually, the server will have the Subscription - // Manager part that ties into Zookeeper to manage this info. - // Commenting out these type of API's related to that here for now until - // this data is available on the server. Will figure out what the - // correct way to contact the server to get this info is then. - // The client side just has soft memory state for client subscription - // information. - return topicSubscriber2Channel.containsKey(new TopicSubscriber(topic, subscriberId)); - } - - public List getSubscriptionList(ByteString subscriberId) throws CouldNotConnectException, - ServiceDownException { - // Same as the previous hasSubscription method, this data should reside - // on the server end, not the client side. - return null; - } - - public void startDelivery(final ByteString topic, final ByteString subscriberId, MessageHandler messageHandler) - throws ClientNotSubscribedException { - if (logger.isDebugEnabled()) - logger.debug("Starting delivery for topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8()); - TopicSubscriber topicSubscriber = new TopicSubscriber(topic, subscriberId); - // Make sure we know about this topic subscription on the client side - // exists. The assumption is that the client should have in memory the - // Channel created for the TopicSubscriber once the server has sent - // an ack response to the initial subscribe request. - if (!topicSubscriber2Channel.containsKey(topicSubscriber)) { - logger.error("Client is not yet subscribed to topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8()); - throw new ClientNotSubscribedException("Client is not yet subscribed to topic: " + topic.toStringUtf8() - + ", subscriberId: " + subscriberId.toStringUtf8()); - } - - // Register the MessageHandler with the subscribe Channel's - // Response Handler. - Channel topicSubscriberChannel = topicSubscriber2Channel.get(topicSubscriber); - HedwigClient.getResponseHandlerFromChannel(topicSubscriberChannel).getSubscribeResponseHandler() - .setMessageHandler(messageHandler); - // Now make the TopicSubscriber Channel readable (it is set to not be - // readable when the initial subscription is done). Note that this is an - // asynchronous call. If this fails (not likely), the futureListener - // will just log an error message for now. - ChannelFuture future = topicSubscriberChannel.setReadable(true); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - logger.error("Unable to make subscriber Channel readable in startDelivery call for topic: " - + topic.toStringUtf8() + ", subscriberId: " + subscriberId.toStringUtf8()); - } - } - }); - } - - public void stopDelivery(final ByteString topic, final ByteString subscriberId) throws ClientNotSubscribedException { - if (logger.isDebugEnabled()) - logger.debug("Stopping delivery for topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8()); - TopicSubscriber topicSubscriber = new TopicSubscriber(topic, subscriberId); - // Make sure we know that this topic subscription on the client side - // exists. The assumption is that the client should have in memory the - // Channel created for the TopicSubscriber once the server has sent - // an ack response to the initial subscribe request. - if (!topicSubscriber2Channel.containsKey(topicSubscriber)) { - logger.error("Client is not yet subscribed to topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8()); - throw new ClientNotSubscribedException("Client is not yet subscribed to topic: " + topic.toStringUtf8() - + ", subscriberId: " + subscriberId.toStringUtf8()); - } - - // Unregister the MessageHandler for the subscribe Channel's - // Response Handler. - Channel topicSubscriberChannel = topicSubscriber2Channel.get(topicSubscriber); - HedwigClient.getResponseHandlerFromChannel(topicSubscriberChannel).getSubscribeResponseHandler() - .setMessageHandler(null); - // Now make the TopicSubscriber channel not-readable. This will buffer - // up messages if any are sent from the server. Note that this is an - // asynchronous call. If this fails (not likely), the futureListener - // will just log an error message for now. - ChannelFuture future = topicSubscriberChannel.setReadable(false); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - logger.error("Unable to make subscriber Channel not readable in stopDelivery call for topic: " - + topic.toStringUtf8() + ", subscriberId: " + subscriberId.toStringUtf8()); - } - } - }); - } - - public void closeSubscription(ByteString topic, ByteString subscriberId) throws ServiceDownException { - PubSubData pubSubData = new PubSubData(topic, null, subscriberId, null, null, null, null); - synchronized (pubSubData) { - PubSubCallback pubSubCallback = new PubSubCallback(pubSubData); - asyncCloseSubscription(topic, subscriberId, pubSubCallback, null); - try { - while (!pubSubData.isDone) - pubSubData.wait(); - } catch (InterruptedException e) { - throw new ServiceDownException("Interrupted Exception while waiting for asyncCloseSubscription call"); - } - // Check from the PubSubCallback if it was successful or not. - if (!pubSubCallback.getIsCallSuccessful()) { - throw new ServiceDownException("Exception while trying to close the subscription for topic: " - + topic.toStringUtf8() + ", subscriberId: " + subscriberId.toStringUtf8()); - } - } - } - - public void asyncCloseSubscription(final ByteString topic, final ByteString subscriberId, - final Callback callback, final Object context) { - if (logger.isDebugEnabled()) - logger.debug("Closing subscription asynchronously for topic: " + topic.toStringUtf8() + ", subscriberId: " - + subscriberId.toStringUtf8()); - TopicSubscriber topicSubscriber = new TopicSubscriber(topic, subscriberId); - if (topicSubscriber2Channel.containsKey(topicSubscriber)) { - // Remove all cached references for the TopicSubscriber - Channel channel = topicSubscriber2Channel.get(topicSubscriber); - topicSubscriber2Channel.remove(topicSubscriber); - // Close the subscribe channel asynchronously. - HedwigClient.getResponseHandlerFromChannel(channel).channelClosedExplicitly = true; - ChannelFuture future = channel.close(); - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - logger.error("Failed to close the subscription channel for topic: " + topic.toStringUtf8() - + ", subscriberId: " + subscriberId.toStringUtf8()); - callback.operationFailed(context, new ServiceDownException( - "Failed to close the subscription channel for topic: " + topic.toStringUtf8() - + ", subscriberId: " + subscriberId.toStringUtf8())); - } else { - callback.operationFinished(context, null); - } - } - }); - } else { - logger.warn("Trying to close a subscription when we don't have a subscribe channel cached for topic: " - + topic.toStringUtf8() + ", subscriberId: " + subscriberId.toStringUtf8()); - callback.operationFinished(context, null); - } - } - - // Public getter and setters for entries in the topic2Host Map. - // This is used for classes that need this information but are not in the - // same classpath. - public Channel getChannelForTopic(TopicSubscriber topic) { - return topicSubscriber2Channel.get(topic); - } - - public void setChannelForTopic(TopicSubscriber topic, Channel channel) { - topicSubscriber2Channel.put(topic, channel); - } - - public void removeChannelForTopic(TopicSubscriber topic) { - topicSubscriber2Channel.remove(topic); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ResponseHandler.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ResponseHandler.java deleted file mode 100644 index 599de8c2d6c..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/ResponseHandler.java +++ /dev/null @@ -1,365 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import java.net.InetSocketAddress; -import java.util.LinkedList; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelPipelineCoverage; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.jboss.netty.channel.SimpleChannelHandler; -import org.jboss.netty.handler.ssl.SslHandler; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.client.exceptions.ServerRedirectLoopException; -import org.apache.hedwig.client.exceptions.TooManyServerRedirectsException; -import org.apache.hedwig.client.handlers.PublishResponseHandler; -import org.apache.hedwig.client.handlers.SubscribeReconnectCallback; -import org.apache.hedwig.client.handlers.SubscribeResponseHandler; -import org.apache.hedwig.client.handlers.UnsubscribeResponseHandler; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.exceptions.PubSubException.UncertainStateException; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; -import org.apache.hedwig.util.HedwigSocketAddress; - -@ChannelPipelineCoverage("all") -public class ResponseHandler extends SimpleChannelHandler { - - private static Logger logger = Logger.getLogger(ResponseHandler.class); - - // Concurrent Map to store for each async PubSub request, the txn ID - // and the corresponding PubSub call's data which stores the VoidCallback to - // invoke when we receive a PubSub ack response from the server. - // This is specific to this instance of the ResponseHandler which is - // tied to a specific netty Channel Pipeline. - protected final ConcurrentMap txn2PubSubData = new ConcurrentHashMap(); - - // Boolean indicating if we closed the channel this ResponseHandler is - // attached to explicitly or not. If so, we do not need to do the - // channel disconnected logic here. - public boolean channelClosedExplicitly = false; - - private final HedwigClient client; - private final HedwigPublisher pub; - private final HedwigSubscriber sub; - private final ClientConfiguration cfg; - - private final PublishResponseHandler pubHandler; - private final SubscribeResponseHandler subHandler; - private final UnsubscribeResponseHandler unsubHandler; - - public ResponseHandler(HedwigClient client) { - this.client = client; - this.sub = client.getSubscriber(); - this.pub = client.getPublisher(); - this.cfg = client.getConfiguration(); - this.pubHandler = new PublishResponseHandler(this); - this.subHandler = new SubscribeResponseHandler(this); - this.unsubHandler = new UnsubscribeResponseHandler(this); - } - - // Public getters needed for the private members - public HedwigClient getClient() { - return client; - } - - public HedwigSubscriber getSubscriber() { - return sub; - } - - public ClientConfiguration getConfiguration() { - return cfg; - } - - public SubscribeResponseHandler getSubscribeResponseHandler() { - return subHandler; - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - // If the Message is not a PubSubResponse, just send it upstream and let - // something else handle it. - if (!(e.getMessage() instanceof PubSubResponse)) { - ctx.sendUpstream(e); - } - // Retrieve the PubSubResponse from the Message that was sent by the - // server. - PubSubResponse response = (PubSubResponse) e.getMessage(); - if (logger.isDebugEnabled()) - logger.debug("Response received from host: " + HedwigClient.getHostFromChannel(ctx.getChannel()) - + ", response: " + response); - - // Determine if this PubSubResponse is an ack response for a PubSub - // Request or if it is a message being pushed to the client subscriber. - if (response.hasMessage()) { - // Subscribed messages being pushed to the client so handle/consume - // it and return. - subHandler.handleSubscribeMessage(response); - return; - } - - // Response is an ack to a prior PubSubRequest so first retrieve the - // PubSub data for this txn. - PubSubData pubSubData = txn2PubSubData.containsKey(response.getTxnId()) ? txn2PubSubData.get(response - .getTxnId()) : null; - // Validate that the PubSub data for this txn is stored. If not, just - // log an error message and return since we don't know how to handle - // this. - if (pubSubData == null) { - logger.error("PubSub Data was not found for PubSubResponse: " + response); - return; - } - - // Now that we've retrieved the PubSubData for this specific Txn ID, we - // can remove it from the Map. - txn2PubSubData.remove(response.getTxnId()); - - // Store the topic2Host mapping if this wasn't a server redirect. We'll - // assume that if the server was able to have an open Channel connection - // to the client, and responded with an ack message other than the - // NOT_RESPONSIBLE_FOR_TOPIC one, it is the correct topic master. - if (!response.getStatusCode().equals(StatusCode.NOT_RESPONSIBLE_FOR_TOPIC)) { - client.storeTopic2HostMapping(pubSubData, ctx.getChannel()); - } - - // Depending on the operation type, call the appropriate handler. - switch (pubSubData.operationType) { - case PUBLISH: - pubHandler.handlePublishResponse(response, pubSubData, ctx.getChannel()); - break; - case SUBSCRIBE: - subHandler.handleSubscribeResponse(response, pubSubData, ctx.getChannel()); - break; - case UNSUBSCRIBE: - unsubHandler.handleUnsubscribeResponse(response, pubSubData, ctx.getChannel()); - break; - default: - // The above are the only expected PubSubResponse messages received - // from the server for the various client side requests made. - logger.error("Response received from server is for an unhandled operation type, txnId: " - + response.getTxnId() + ", operationType: " + pubSubData.operationType); - } - } - - /** - * Logic to repost a PubSubRequest when the server responds with a redirect - * indicating they are not the topic master. - * - * @param response - * PubSubResponse from the server for the redirect - * @param pubSubData - * PubSubData for the original PubSubRequest made - * @param channel - * Channel Channel we used to make the original PubSubRequest - * @throws Exception - * Throws an exception if there was an error in doing the - * redirect repost of the PubSubRequest - */ - public void handleRedirectResponse(PubSubResponse response, PubSubData pubSubData, Channel channel) - throws Exception { - if (logger.isDebugEnabled()) - logger.debug("Handling a redirect from host: " + HedwigClient.getHostFromChannel(channel) + ", response: " - + response + ", pubSubData: " + pubSubData); - // In this case, the PubSub request was done to a server that is not - // responsible for the topic. First make sure that we haven't - // exceeded the maximum number of server redirects. - int curNumServerRedirects = (pubSubData.triedServers == null) ? 0 : pubSubData.triedServers.size(); - if (curNumServerRedirects >= cfg.getMaximumServerRedirects()) { - // We've already exceeded the maximum number of server redirects - // so consider this as an error condition for the client. - // Invoke the operationFailed callback and just return. - if (logger.isDebugEnabled()) - logger.debug("Exceeded the number of server redirects (" + curNumServerRedirects + ") so error out."); - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - new TooManyServerRedirectsException("Already reached max number of redirects: " - + curNumServerRedirects))); - return; - } - - // We will redirect and try to connect to the correct server - // stored in the StatusMsg of the response. First store the - // server that we sent the PubSub request to for the topic. - ByteString triedServer = ByteString.copyFromUtf8(HedwigSocketAddress.sockAddrStr(HedwigClient - .getHostFromChannel(channel))); - if (pubSubData.triedServers == null) - pubSubData.triedServers = new LinkedList(); - pubSubData.shouldClaim = true; - pubSubData.triedServers.add(triedServer); - - // Now get the redirected server host (expected format is - // Hostname:Port:SSLPort) from the server's response message. If one is - // not given for some reason, then redirect to the default server - // host/VIP to repost the request. - String statusMsg = response.getStatusMsg(); - InetSocketAddress redirectedHost; - if (statusMsg != null && statusMsg.length() > 0) { - if (cfg.isSSLEnabled()) { - redirectedHost = new HedwigSocketAddress(statusMsg).getSSLSocketAddress(); - } else { - redirectedHost = new HedwigSocketAddress(statusMsg).getSocketAddress(); - } - } else { - redirectedHost = cfg.getDefaultServerHost(); - } - - // Make sure the redirected server is not one we've already attempted - // already before in this PubSub request. - if (pubSubData.triedServers.contains(ByteString.copyFromUtf8(HedwigSocketAddress.sockAddrStr(redirectedHost)))) { - logger.error("We've already sent this PubSubRequest before to redirectedHost: " + redirectedHost - + ", pubSubData: " + pubSubData); - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - new ServerRedirectLoopException("Already made the request before to redirected host: " - + redirectedHost))); - return; - } - - // Check if we already have a Channel open to the redirected server - // host. - boolean redirectedHostChannelExists = pub.host2Channel.containsKey(redirectedHost) ? true : false; - if (pubSubData.operationType.equals(OperationType.SUBSCRIBE) || !redirectedHostChannelExists) { - // We don't have an existing channel to the redirected host OR this - // is a redirected Subscribe request. For Subscribe requests, we - // always want to create a new unique Channel connection to the - // topic master server for the TopicSubscriber. - client.doConnect(pubSubData, redirectedHost); - } else { - // For Publish and Unsubscribe requests, we can just post the - // request again directly on the existing cached redirected host - // channel. - if (pubSubData.operationType.equals(OperationType.PUBLISH)) { - pub.doPublish(pubSubData, pub.host2Channel.get(redirectedHost)); - } else if (pubSubData.operationType.equals(OperationType.UNSUBSCRIBE)) { - sub.doSubUnsub(pubSubData, pub.host2Channel.get(redirectedHost)); - } - } - } - - // Logic to deal with what happens when a Channel to a server host is - // disconnected. - @Override - public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - // If this channel was closed explicitly by the client code, - // we do not need to do any of this logic. This could happen - // for redundant Publish channels created or redirected subscribe - // channels that are not used anymore or when we shutdown the - // client and manually close all of the open channels. - // Also don't do any of the disconnect logic if the client has stopped. - if (channelClosedExplicitly || client.hasStopped()) - return; - - // Make sure the host retrieved is not null as there could be some weird - // channel disconnect events happening during a client shutdown. - // If it is, just return as there shouldn't be anything we need to do. - InetSocketAddress host = HedwigClient.getHostFromChannel(ctx.getChannel()); - logger.warn("Channel was disconnected to host: " + host); - if (host == null) - return; - - // If this Channel was used for Publish and Unsubscribe flows, just - // remove it from the HewdigPublisher's host2Channel map. We will - // re-establish a Channel connection to that server when the next - // publish/unsubscribe request to a topic that the server owns occurs. - PubSubData origSubData = subHandler.getOrigSubData(); - - // Now determine what type of operation this channel was used for. - if (origSubData == null) { - // Only remove the Channel from the mapping if this current - // disconnected channel is the same as the cached entry. - // Due to race concurrency situations, it is possible to - // create multiple channels to the same host for publish - // and unsubscribe requests. - if (pub.host2Channel.containsKey(host) && pub.host2Channel.get(host).equals(ctx.getChannel())) { - if (logger.isDebugEnabled()) - logger.debug("Disconnected channel for host: " + host - + " was for Publish/Unsubscribe requests so remove all references to it."); - pub.host2Channel.remove(host); - client.clearAllTopicsForHost(host); - } - } else { - // Subscribe channel disconnected so first close and clear all - // cached Channel data set up for this topic subscription. - sub.closeSubscription(origSubData.topic, origSubData.subscriberId); - client.clearAllTopicsForHost(host); - // Since the connection to the server host that was responsible - // for the topic died, we are not sure about the state of that - // server. Resend the original subscribe request data to the default - // server host/VIP. Also clear out all of the servers we've - // contacted or attempted to from this request as we are starting a - // "fresh" subscribe request. - origSubData.clearServersList(); - // Set a new type of VoidCallback for this async call. We need this - // hook so after the subscribe reconnect has completed, delivery for - // that topic subscriber should also be restarted (if it was that - // case before the channel disconnect). - origSubData.callback = new SubscribeReconnectCallback(origSubData, client, subHandler.getMessageHandler()); - origSubData.context = null; - if (logger.isDebugEnabled()) - logger.debug("Disconnected subscribe channel so reconnect with origSubData: " + origSubData); - client.doConnect(origSubData, cfg.getDefaultServerHost()); - } - - // Finally, all of the PubSubRequests that are still waiting for an ack - // response from the server need to be removed and timed out. Invoke the - // operationFailed callbacks on all of them. Use the - // UncertainStateException since the server did receive the request but - // we're not sure of the state of the request since the ack response was - // never received. - for (PubSubData pubSubData : txn2PubSubData.values()) { - if (logger.isDebugEnabled()) - logger.debug("Channel disconnected so invoking the operationFailed callback for pubSubData: " - + pubSubData); - pubSubData.callback.operationFailed(pubSubData.context, new UncertainStateException( - "Server ack response never received before server connection disconnected!")); - } - txn2PubSubData.clear(); - } - - // Logic to deal with what happens when a Channel to a server host is - // connected. This is needed if the client is using an SSL port to - // communicate with the server. If so, we need to do the SSL handshake here - // when the channel is first connected. - @Override - public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - // No need to initiate the SSL handshake if we are closing this channel - // explicitly or the client has been stopped. - if (cfg.isSSLEnabled() && !channelClosedExplicitly && !client.hasStopped()) { - if (logger.isDebugEnabled()) { - logger.debug("Initiating the SSL handshake"); - } - ctx.getPipeline().get(SslHandler.class).handshake(e.getChannel()); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { - e.getCause().printStackTrace(); - e.getChannel().close(); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/WriteCallback.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/WriteCallback.java deleted file mode 100644 index 2ee5ed60061..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/netty/WriteCallback.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.netty; - -import java.net.InetSocketAddress; -import java.util.LinkedList; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.data.PubSubData; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class WriteCallback implements ChannelFutureListener { - - private static Logger logger = Logger.getLogger(WriteCallback.class); - - // Private member variables - private PubSubData pubSubData; - private final HedwigClient client; - private final ClientConfiguration cfg; - - // Constructor - public WriteCallback(PubSubData pubSubData, HedwigClient client) { - super(); - this.pubSubData = pubSubData; - this.client = client; - this.cfg = client.getConfiguration(); - } - - public void operationComplete(ChannelFuture future) throws Exception { - // If the client has stopped, there is no need to proceed - // with any callback logic here. - if (client.hasStopped()) - return; - - // When the write operation to the server is done, we just need to check - // if it was successful or not. - InetSocketAddress host = HedwigClient.getHostFromChannel(future.getChannel()); - if (!future.isSuccess()) { - logger.error("Error writing on channel to host: " + host); - // On a write failure for a PubSubRequest, we also want to remove - // the saved txnId to PubSubData in the ResponseHandler. These - // requests will not receive an ack response from the server - // so there is no point storing that information there anymore. - HedwigClient.getResponseHandlerFromChannel(future.getChannel()).txn2PubSubData.remove(pubSubData.txnId); - - // If we were not able to write on the channel to the server host, - // the host could have died or something is wrong with the channel - // connection where we can connect to the host, but not write to it. - ByteString hostString = (host == null) ? null : ByteString.copyFromUtf8(HedwigSocketAddress.sockAddrStr(host)); - if (pubSubData.writeFailedServers != null && pubSubData.writeFailedServers.contains(hostString)) { - // We've already tried to write to this server previously and - // failed, so invoke the operationFailed callback. - logger.error("Error writing to host more than once so just invoke the operationFailed callback!"); - pubSubData.callback.operationFailed(pubSubData.context, new ServiceDownException( - "Error while writing message to server: " + hostString)); - } else { - if (logger.isDebugEnabled()) - logger.debug("Try to send the PubSubRequest again to the default server host/VIP for pubSubData: " - + pubSubData); - // Keep track of this current server that we failed to write to - // but retry the request on the default server host/VIP. - if (pubSubData.writeFailedServers == null) - pubSubData.writeFailedServers = new LinkedList(); - pubSubData.writeFailedServers.add(hostString); - client.doConnect(pubSubData, cfg.getDefaultServerHost()); - } - } else { - // Now that the write to the server is done, we have to wait for it - // to respond. The ResponseHandler will take care of the ack - // response from the server before we can determine if the async - // PubSub call has really completed successfully or not. - if (logger.isDebugEnabled()) - logger.debug("Successfully wrote to host: " + host + " for pubSubData: " + pubSubData); - } - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslClientContextFactory.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslClientContextFactory.java deleted file mode 100644 index ee488ba56f8..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslClientContextFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.ssl; - -import javax.net.ssl.SSLContext; - -import org.apache.hedwig.client.conf.ClientConfiguration; - -public class SslClientContextFactory extends SslContextFactory { - - public SslClientContextFactory(ClientConfiguration cfg) { - try { - // Create the SSL context. - ctx = SSLContext.getInstance("TLS"); - ctx.init(null, getTrustManagers(), null); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - @Override - protected boolean isClient() { - return true; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslContextFactory.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslContextFactory.java deleted file mode 100644 index 16fa136a4a0..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/client/ssl/SslContextFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client.ssl; - -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -public abstract class SslContextFactory { - - protected SSLContext ctx; - - public SSLContext getContext() { - return ctx; - } - - protected abstract boolean isClient(); - - public SSLEngine getEngine() { - SSLEngine engine = ctx.createSSLEngine(); - engine.setUseClientMode(isClient()); - return engine; - } - - protected TrustManager[] getTrustManagers() { - return new TrustManager[] { new X509TrustManager() { - // Always trust, even if invalid. - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - // Always trust. - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - // Always trust. - } - } }; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/conf/AbstractConfiguration.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/conf/AbstractConfiguration.java deleted file mode 100644 index 12b3ec8e70c..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/conf/AbstractConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.conf; - -import java.net.URL; - -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.PropertiesConfiguration; - -public abstract class AbstractConfiguration { - protected CompositeConfiguration conf; - - protected AbstractConfiguration() { - conf = new CompositeConfiguration(); - } - - /** - * You can load configurations in precedence order. The first one takes - * precedence over any loaded later. - * - * @param confURL - */ - public void loadConf(URL confURL) throws ConfigurationException { - Configuration loadedConf = new PropertiesConfiguration(confURL); - conf.addConfiguration(loadedConf); - - } -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Callback.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Callback.java deleted file mode 100644 index 8c4299377f6..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Callback.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import org.apache.hedwig.exceptions.PubSubException; - -/** - * This class is used for callbacks for asynchronous operations - * - */ -public interface Callback { - - /** - * This method is called when the asynchronous operation finishes - * - * @param ctx - * @param resultOfOperation - */ - public abstract void operationFinished(Object ctx, T resultOfOperation); - - /** - * This method is called when the operation failed due to some reason. The - * reason for failure is passed in. - * - * @param ctx - * The context for the callback - * @param exception - * The reason for the failure of the scan - */ - public abstract void operationFailed(Object ctx, PubSubException exception); - -} \ No newline at end of file diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/CallbackUtils.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/CallbackUtils.java deleted file mode 100644 index 319d9a42887..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/CallbackUtils.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.CompositeException; - -public class CallbackUtils { - - /** - * A callback that waits for all of a number of events to fire. If any fail, - * then fail the final callback with a composite exception. - * - * TODO: change this to use any Exception and make CompositeException - * generic, not a PubSubException. - * - * @param expected - * Number of expected callbacks. - * @param cb - * The final callback to call. - * @param ctx - * @param logger - * May be null. - * @param level - * Required iff logger != null. - * @param successMsg - * If not null, then this is logged on success. - * @param failureMsg - * If not null, then this is logged on failure. - * @param eagerErrorHandler - * If not null, then this will be executed after the first - * failure (but before the final failure callback). Useful for - * releasing resources, etc. as soon as we know the composite - * operation is doomed. - * @return - */ - public static Callback multiCallback(final int expected, final Callback cb, final Object ctx, - final Logger logger, final Level level, final Object successMsg, final Object failureMsg, - Runnable eagerErrorHandler) { - if (expected == 0) { - cb.operationFinished(ctx, null); - return null; - } else { - return new Callback() { - - final AtomicInteger done = new AtomicInteger(); - final LinkedBlockingQueue exceptions = new LinkedBlockingQueue(); - - private void tick() { - if (done.incrementAndGet() == expected) { - if (exceptions.isEmpty()) { - cb.operationFinished(ctx, null); - } else { - cb.operationFailed(ctx, new CompositeException(exceptions)); - } - } - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - if (logger != null && failureMsg != null) - logger.log(level, failureMsg, exception); - exceptions.add(exception); - tick(); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - if (logger != null && successMsg != null) - logger.log(level, successMsg); - tick(); - } - - }; - } - } - - /** - * A callback that waits for all of a number of events to fire. If any fail, - * then fail the final callback with a composite exception. - */ - public static Callback multiCallback(int expected, Callback cb, Object ctx) { - return multiCallback(expected, cb, ctx, null, null, null, null, null); - } - - /** - * A callback that waits for all of a number of events to fire. If any fail, - * then fail the final callback with a composite exception. - */ - public static Callback multiCallback(int expected, Callback cb, Object ctx, Runnable eagerErrorHandler) { - return multiCallback(expected, cb, ctx, null, null, null, null, eagerErrorHandler); - } - - private static Callback nop = new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - } - - }; - - /** - * A do-nothing callback. - */ - public static Callback nop() { - return nop; - } - - /** - * Logs what happened before continuing the callback chain. - */ - public static Callback logger(final Logger logger, final Level successLevel, final Level failureLevel, final Object successMsg, - final Object failureMsg, final Callback cont) { - return new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - logger.log(failureLevel, failureMsg, exception); - if (cont != null) - cont.operationFailed(ctx, exception); - } - - @Override - public void operationFinished(Object ctx, T resultOfOperation) { - logger.log(successLevel, successMsg); - if (cont != null) - cont.operationFinished(ctx, resultOfOperation); - } - - }; - } - - /** - * Logs what happened (no continuation). - */ - public static Callback logger(Logger logger, Level successLevel, Level failureLevel, Object successMsg, Object failureMsg) { - return logger(logger, successLevel, failureLevel, successMsg, failureMsg, nop()); - } - - /** - * Return a Callback that just calls the given Callback cb with the - * bound result. - */ - public static Callback curry(final Callback cb, final T result) { - return new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - cb.operationFailed(ctx, exception); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - cb.operationFinished(ctx, result); - } - - }; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/ConcurrencyUtils.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/ConcurrencyUtils.java deleted file mode 100644 index 8f5f1cae9c5..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/ConcurrencyUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CyclicBarrier; - -public class ConcurrencyUtils { - - public static > void put(V queue, U value) { - try { - queue.put(value); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static T take(BlockingQueue queue) { - try { - return queue.take(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static void await(CyclicBarrier barrier) { - try { - barrier.await(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Either.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Either.java deleted file mode 100644 index b8ae82ffbb9..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Either.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -public class Either { - - private T x; - private U y; - - private Either(T x, U y) { - this.x = x; - this.y = y; - } - - public static Either of(T x, U y) { - return new Either(x, y); - } - - public static Either left(T x) { - return new Either(x, null); - } - - public static Either right(U y) { - return new Either(null, y); - } - - public T left() { - return x; - } - - public U right() { - return y; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/FileUtils.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/FileUtils.java deleted file mode 100644 index 98e41e1b0c7..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/FileUtils.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.io.File; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -import org.apache.log4j.Logger; - -public class FileUtils { - - static DirDeleterThred dirDeleterThread; - static Logger log = Logger.getLogger(FileUtils.class); - - static { - dirDeleterThread = new DirDeleterThred(); - Runtime.getRuntime().addShutdownHook(dirDeleterThread); - } - - public static File createTempDirectory(String prefix) throws IOException { - return createTempDirectory(prefix, null); - } - - public static File createTempDirectory(String prefix, String suffix) throws IOException { - File tempDir = File.createTempFile(prefix, suffix); - if (!tempDir.delete()) { - throw new IOException("Could not delete temp file: " + tempDir.getAbsolutePath()); - } - - if (!tempDir.mkdir()) { - throw new IOException("Could not create temp directory: " + tempDir.getAbsolutePath()); - } - - dirDeleterThread.addDirToDelete(tempDir); - return tempDir; - - } - - static class DirDeleterThred extends Thread { - List dirsToDelete = new LinkedList(); - - public synchronized void addDirToDelete(File dir) { - dirsToDelete.add(dir); - } - - @Override - public void run() { - synchronized (this) { - for (File dir : dirsToDelete) { - deleteDirectory(dir); - } - } - } - - protected void deleteDirectory(File dir) { - if (dir.isFile()) { - if (!dir.delete()) { - log.error("Could not delete " + dir.getAbsolutePath()); - } - return; - } - - File[] files = dir.listFiles(); - if (files == null) { - return; - } - - for (File f : files) { - deleteDirectory(f); - } - - if (!dir.delete()) { - log.error("Could not delete directory: " + dir.getAbsolutePath()); - } - - } - - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/HedwigSocketAddress.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/HedwigSocketAddress.java deleted file mode 100644 index a6cf89e8fbb..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/HedwigSocketAddress.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.net.InetSocketAddress; - -/** - * This is a data wrapper class that is basically an InetSocketAddress with one - * extra piece of information for the SSL port (optional). This is used by - * Hedwig so we can encapsulate both regular and SSL port information in one - * data structure. Hedwig hub servers can be configured to listen on the - * standard regular port and additionally on an optional SSL port. The String - * representation of a HedwigSocketAddress is: :: - */ -public class HedwigSocketAddress { - - // Member fields that make up this class. - private final String hostname; - private final int port; - private final int sslPort; - - private final InetSocketAddress socketAddress; - private final InetSocketAddress sslSocketAddress; - - // Constants used by this class. - public static final String COLON = ":"; - private static final int NO_SSL_PORT = -1; - - // Constructor that takes in both a regular and SSL port. - public HedwigSocketAddress(String hostname, int port, int sslPort) { - this.hostname = hostname; - this.port = port; - this.sslPort = sslPort; - socketAddress = new InetSocketAddress(hostname, port); - if (sslPort != NO_SSL_PORT) - sslSocketAddress = new InetSocketAddress(hostname, sslPort); - else - sslSocketAddress = null; - } - - // Constructor that only takes in a regular port. - public HedwigSocketAddress(String hostname, int port) { - this(hostname, port, NO_SSL_PORT); - } - - // Constructor from a String "serialized" version of this class. - public HedwigSocketAddress(String addr) { - String[] parts = addr.split(COLON); - this.hostname = parts[0]; - this.port = Integer.parseInt(parts[1]); - if (parts.length > 2) - this.sslPort = Integer.parseInt(parts[2]); - else - this.sslPort = NO_SSL_PORT; - socketAddress = new InetSocketAddress(hostname, port); - if (sslPort != NO_SSL_PORT) - sslSocketAddress = new InetSocketAddress(hostname, sslPort); - else - sslSocketAddress = null; - } - - // Public getters - public String getHostname() { - return hostname; - } - - public int getPort() { - return port; - } - - public int getSSLPort() { - return sslPort; - } - - // Method to return an InetSocketAddress for the regular port. - public InetSocketAddress getSocketAddress() { - return socketAddress; - } - - // Method to return an InetSocketAddress for the SSL port. - // Note that if no SSL port (or an invalid value) was passed - // during object creation, this call will throw an IllegalArgumentException - // (runtime exception). - public InetSocketAddress getSSLSocketAddress() { - return sslSocketAddress; - } - - // Method to determine if this object instance is SSL enabled or not - // (contains a valid SSL port). - public boolean isSSLEnabled() { - return sslPort != NO_SSL_PORT; - } - - // Return the String "serialized" version of this object. - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(hostname).append(COLON).append(port).append(COLON).append(sslPort); - return sb.toString(); - } - - // Implement an equals method comparing two HedwigSocketAddress objects. - @Override - public boolean equals(Object obj) { - if (!(obj instanceof HedwigSocketAddress)) - return false; - HedwigSocketAddress that = (HedwigSocketAddress) obj; - return (this.hostname.equals(that.hostname) && (this.port == that.port) && (this.sslPort == that.sslPort)); - } - - // Static helper method to return the string representation for an - // InetSocketAddress. The HedwigClient can only operate in SSL or non-SSL - // mode. So the server hosts it connects to will just be an - // InetSocketAddress instead of a HedwigSocketAddress. This utility method - // can be used so we can store these server hosts as strings (ByteStrings) - // in various places (e.g. list of server hosts we've connected to - // or wrote to unsuccessfully). - public static String sockAddrStr(InetSocketAddress addr) { - return addr.getAddress().getHostAddress() + ":" + addr.getPort(); - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Option.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Option.java deleted file mode 100644 index 6a347823f2c..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Option.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -public class Option { - - private T x; - - public static Option of(T x) { - return new Option(x); - } - - public static Option of() { - return new Option(); - } - - public Option() { - } - - public Option(T x) { - this.x = x; - } - - public T get() { - return x; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Pair.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Pair.java deleted file mode 100644 index f0582b5e021..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/Pair.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -public class Pair { - - private T x; - private U y; - - public Pair(T x, U y) { - this.x = x; - this.y = y; - } - - public static Pair of(T x, U y) { - return new Pair(x, y); - } - - public T first() { - return x; - } - - public U second() { - return y; - } - -} diff --git a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/PathUtils.java b/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/PathUtils.java deleted file mode 100644 index b9890575cd8..00000000000 --- a/src/contrib/hedwig/client/src/main/java/org/apache/hedwig/util/PathUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class PathUtils { - - /** Generate all prefixes for a path. "/a/b/c" -> ["/a","/a/b","/a/b/c"] */ - public static List prefixes(String path) { - List prefixes = new ArrayList(); - String prefix = ""; - for (String comp : path.split("/+")) { - // Skip the first (empty) path component. - if (!comp.equals("")) { - prefix += "/" + comp; - prefixes.add(prefix); - } - } - return prefixes; - } - - /** Return true iff prefix is a prefix of path. */ - public static boolean isPrefix(String prefix, String path) { - String[] as = prefix.split("/+"), bs = path.split("/+"); - if (as.length > bs.length) - return false; - for (int i = 0; i < as.length; i++) - if (!as[i].equals(bs[i])) - return false; - return true; - } - - /** Like File.getParent but always uses the / separator. */ - public static String parent(String path) { - return new File(path).getParent().replace("\\", "/"); - } - -} diff --git a/src/contrib/hedwig/client/src/main/resources/log4j.properties b/src/contrib/hedwig/client/src/main/resources/log4j.properties deleted file mode 100644 index 377afb46485..00000000000 --- a/src/contrib/hedwig/client/src/main/resources/log4j.properties +++ /dev/null @@ -1,32 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# -log4j.rootLogger=INFO, A1 -log4j.logger.org.apache.zookeeper = ERROR -log4j.logger.org.apache.bookkeeper.client.QuorumOpMonitor = ERROR -log4j.logger.org.apache.bookkeeper.proto.BookieClient = ERROR - -# A1 is set to be a ConsoleAppender. -log4j.appender.A1=org.apache.log4j.ConsoleAppender - -# A1 uses PatternLayout. -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -log4j.appender.A1.layout.ConversionPattern=%d %-4r [%t] %-5p %c %x - %m%n - diff --git a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/client/AppTest.java b/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/client/AppTest.java deleted file mode 100644 index 19cba7a0c40..00000000000 --- a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/client/AppTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest extends TestCase { - /** - * Create the test case - * - * @param testName - * name of the test case - */ - public AppTest(String testName) { - super(testName); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() { - return new TestSuite(AppTest.class); - } - - /** - * Rigourous Test :-) - */ - public void testApp() { - assertTrue(true); - } -} diff --git a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestFileUtils.java b/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestFileUtils.java deleted file mode 100644 index 8803d871e0c..00000000000 --- a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestFileUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.io.File; - -import org.junit.Test; - -import junit.framework.TestCase; - -public class TestFileUtils extends TestCase { - - @Test - public void testCreateTmpDirectory() throws Exception { - String prefix = "abc"; - String suffix = "def"; - File dir = FileUtils.createTempDirectory(prefix, suffix); - assertTrue(dir.isDirectory()); - assertTrue(dir.getName().startsWith(prefix)); - assertTrue(dir.getName().endsWith(suffix)); - FileUtils.dirDeleterThread.start(); - FileUtils.dirDeleterThread.join(); - assertFalse(dir.exists()); - } - -} diff --git a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestHedwigSocketAddress.java b/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestHedwigSocketAddress.java deleted file mode 100644 index f8573d13b67..00000000000 --- a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestHedwigSocketAddress.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.net.InetSocketAddress; - -import junit.framework.TestCase; - -import org.junit.Test; - -public class TestHedwigSocketAddress extends TestCase { - - // Common values used by tests - private String hostname = "localhost"; - private int port = 4080; - private int sslPort = 9876; - private int invalidPort = -9999; - private String COLON = ":"; - - @Test - public void testCreateWithSSLPort() throws Exception { - HedwigSocketAddress addr = new HedwigSocketAddress(hostname, port, sslPort); - assertTrue(addr.getSocketAddress().equals(new InetSocketAddress(hostname, port))); - assertTrue(addr.getSSLSocketAddress().equals(new InetSocketAddress(hostname, sslPort))); - } - - @Test - public void testCreateWithNoSSLPort() throws Exception { - HedwigSocketAddress addr = new HedwigSocketAddress(hostname, port); - assertTrue(addr.getSocketAddress().equals(new InetSocketAddress(hostname, port))); - assertTrue(addr.getSSLSocketAddress() == null); - } - - @Test - public void testCreateFromStringWithSSLPort() throws Exception { - HedwigSocketAddress addr = new HedwigSocketAddress(hostname+COLON+port+COLON+sslPort); - assertTrue(addr.getSocketAddress().equals(new InetSocketAddress(hostname, port))); - assertTrue(addr.getSSLSocketAddress().equals(new InetSocketAddress(hostname, sslPort))); - } - - @Test - public void testCreateFromStringWithNoSSLPort() throws Exception { - HedwigSocketAddress addr = new HedwigSocketAddress(hostname+COLON+port); - assertTrue(addr.getSocketAddress().equals(new InetSocketAddress(hostname, port))); - assertTrue(addr.getSSLSocketAddress() == null); - } - - @Test - public void testCreateWithInvalidRegularPort() throws Exception { - boolean success = false; - try { - new HedwigSocketAddress(hostname+COLON+invalidPort); - } - catch (IllegalArgumentException e) { - success = true; - } - assertTrue(success); - } - - @Test - public void testCreateWithInvalidSSLPort() throws Exception { - boolean success = false; - try { - new HedwigSocketAddress(hostname, port, invalidPort); - } - catch (IllegalArgumentException e) { - success = true; - } - assertTrue(success); - } - - @Test - public void testToStringConversion() throws Exception { - HedwigSocketAddress addr = new HedwigSocketAddress(hostname, port, sslPort); - HedwigSocketAddress addr2 = new HedwigSocketAddress(addr.toString()); - assertTrue(addr.getSocketAddress().equals(addr2.getSocketAddress())); - assertTrue(addr.getSSLSocketAddress().equals(addr2.getSSLSocketAddress())); - addr.toString().equals(addr2.toString()); - } - - @Test - public void testIsSSLEnabledFlag() throws Exception { - HedwigSocketAddress sslAddr = new HedwigSocketAddress(hostname, port, sslPort); - assertTrue(sslAddr.isSSLEnabled()); - HedwigSocketAddress addr = new HedwigSocketAddress(hostname, port); - assertFalse(addr.isSSLEnabled()); - } - -} diff --git a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestPathUtils.java b/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestPathUtils.java deleted file mode 100644 index db1e61fe076..00000000000 --- a/src/contrib/hedwig/client/src/test/java/org/apache/hedwig/util/TestPathUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.util; - -import java.util.Arrays; - -import junit.framework.TestCase; - -import org.junit.Test; - -public class TestPathUtils extends TestCase { - - @Test - public void testPrefixes() { - assertEquals(Arrays.asList(new String[] { "/a", "/a/b", "/a/b/c" }), PathUtils.prefixes("/a/b/c")); - assertEquals(Arrays.asList(new String[] { "/a", "/a/b", "/a/b/c" }), PathUtils.prefixes("///a///b///c")); - - } - - @Test - public void testIsPrefix() { - String[] paths = new String[] { "/", "/a", "/a/b" }; - for (int i = 0; i < paths.length; i++) { - for (int j = 0; j <= i; j++) { - assertTrue(PathUtils.isPrefix(paths[j], paths[i])); - assertTrue(PathUtils.isPrefix(paths[j], paths[i] + "/")); - assertTrue(PathUtils.isPrefix(paths[j] + "/", paths[i])); - assertTrue(PathUtils.isPrefix(paths[j] + "/", paths[i] + "/")); - } - for (int j = i + 1; j < paths.length; j++) { - assertFalse(PathUtils.isPrefix(paths[j], paths[i])); - assertFalse(PathUtils.isPrefix(paths[j], paths[i] + "/")); - assertFalse(PathUtils.isPrefix(paths[j] + "/", paths[i])); - assertFalse(PathUtils.isPrefix(paths[j] + "/", paths[i] + "/")); - } - } - } - -} diff --git a/src/contrib/hedwig/conf/hw_client_sample.conf b/src/contrib/hedwig/conf/hw_client_sample.conf deleted file mode 100644 index a7948e59fed..00000000000 --- a/src/contrib/hedwig/conf/hw_client_sample.conf +++ /dev/null @@ -1,7 +0,0 @@ -# The default Hedwig server host to contact (this ideally should be a VIP -# that fronts all of the Hedwig server hubs). -default_server_host=hwServer1:4080:9876 -# This parameter is a boolean flag indicating if communication with the -# server should be done via SSL for encryption. The Hedwig server hubs also -# need to be SSL enabled for this to work. -ssl_enabled=false diff --git a/src/contrib/hedwig/conf/hw_server_sample.conf b/src/contrib/hedwig/conf/hw_server_sample.conf deleted file mode 100644 index 590adfd0e9d..00000000000 --- a/src/contrib/hedwig/conf/hw_server_sample.conf +++ /dev/null @@ -1,10 +0,0 @@ -# The ZooKeeper server host(s) for the Hedwig Server to use. -zk_host=zkServer1:2181 -# The number of milliseconds of each tick in ZooKeeper. -zk_timeout=2000 -# The port at which the clients will connect. -server_port=4080 -# The SSL port at which the clients will connect (only if SSL is enabled). -ssl_server_port=9876 -# Flag indicating if the server should also operate in SSL mode. -ssl_enabled=false diff --git a/src/contrib/hedwig/doc/build.txt b/src/contrib/hedwig/doc/build.txt deleted file mode 100644 index 83c129ad2d9..00000000000 --- a/src/contrib/hedwig/doc/build.txt +++ /dev/null @@ -1,146 +0,0 @@ -% Building Hedwig -% Yang Zhang - -Pre-requisites -============== - -For the core itself: - -- JDK 6: . Ensure `$JAVA_HOME` is correctly set. -- Maven 2: . -- Protocol Buffers 2.3.0: . -- Zookeeper 3.4.0: . See below. -- Bookkeeper 3.4.0: . See below. - -Hedwig has been tested on Windows XP, Linux 2.6, and OS X. - -For the deployment and distributed support scripts in `hw.bash`: - -- Ant: , if you want to build Zookeeper. -- Bash: . -- Coreutils: . -- Expect: , if you want `unbuffer`. -- Findutils: . -- OpenSSH: . -- Python 2.6: . - -Protocol Buffers ----------------- - -Hedwig requires the use of the Java runtime libraries of Protocol Buffers 2.3.0. -These libraries need to be installed into your local maven repository. (Maven allows -multiple versions to be installed.) To install protocol buffels to your local -repository, you have to download the tarball and follow the README.txt -instructions. Note that you must first install the C++ package which contains the -compiler (protoc) before you can build the java libraries. That will install the -library jar's in the local maven repository where Hedwig is currently configured -to point to. - -Zookeeper and Bookkeeper ------------------------- - -Hedwig currently requires the version of Bookkeeper maintained in Apache's current -trunk SVN respository (version 3.4.0). This is not a released version yet but certain -features needed for BookKeeper are only available there. - -Hedwig also depends on ZK testing code for its own testing code. - -Since Hedwig is a Maven project, all these dependencies must be made available -as Maven artifacts. However, neither ZK nor BK are currently Mavenized. -Hedwig provides some bash scripts to ease the installation of ZK, ZK tests, and -BK, all as Maven artifacts. - -Currently, we have included the necessary ZooKeeper and BookKeeper jars in the Hedwig -source itself in the $HEDWIG_DIR/server/lib directory. There is no need to retrieve -them directly from the Apache download site as they are non-released trunk versions. - -#Not relevant right now since we already have the ZK jars already in the Hedwig source. -To fetch and build ZK 3.4.0 (and its tests) in the current directory, run: - - $HEDWIG_DIR/scripts/hw.bash get-zk - -#Not relevant right now, but when we start using the apache version of BK, to -build the local version of BK: - - $HEDWIG_DIR/scripts/hw.bash get-bk - -The $HEDWIG_DIR/server/lib directory contains all of the the class and source jars for -ZK, ZK tests, and BK. To install these, go to that directory and run the following -command to install them into your local maven repository: - - $HEDWIG_DIR/scripts/hw.bash install-zk-bk - -Command-Line Instructions -========================= - -From the main Hedwig directory, run `mvn package`. This will produce the -executable jars for both the client and server, as well as a server "assembly -jar" containing all dependencies as well for easier deployment. - -See the User's Guide for instructions on running and usage. - -Eclipse Instructions -==================== - -To check out, build, and develop using Eclipse: - -1. Install the Subclipse plugin. Update site: - . - -2. Install the Maven plugin. Update site: - . From the list of packages available - from this site, select everything under the "Maven Integration" category, - and from the optional components select the ones with the word "SCM" in them. - -3. Go to Preferences > Team > SVN. For the SVN interface, choose "Pure Java". - -4. Choose File > New > Project... > Maven > Checkout Maven Projects from SCM. - -5. For the SCM URL type, choose SVN. For the URL, enter - SVN URL. Maven will automatically - create a top-level Eclipse project for each of the 4 Maven modules - (recommended). If you want fewer top-level projects, uncheck the option of - having a project for each module (under Advanced). - -6. Right-click on the `protocol` project and choose Run As > Maven - generate-sources. This will generate the Java and C++ code for Protocol - Buffers. - -7. Refresh the workspace to pick up the generated code and add - `hedwig/protocol/target/generated-sources/java` as a source folder. (6 & 7 - should really be doable automatically, but I haven't figured out how.) - -You are now ready to run and debug the client and server code. See the User's -Guide for instructions on running and usage. - -Utilities -========= - -Removing Conflicting Files in Jars ----------------------------------- - -The Maven assembly plugin that produces the fat assembly jar may end up putting -into the jar files with the same conflicting paths from multiple dependencies. -This makes working with the files from certain tools (like `jar`) a bit jarring. -In our case, these files are not things like class files, but rather README and -LICENSE files, so we can safely remove conflicts by choosing an arbitrary winner. -To do so, run: - - $HEDWIG_DIR/scripts/hw.bash strip-jar - -Adjusting Logging ------------------ - -The logging level is something that is baked into the jar in the -`log4j.properties` resource. However, it would be wasteful to go through a -Maven build cycle to update and adjust this. If you're working from a source -tree, it's also annoying to have to edit a source file to adjust the logging. - -We have a little script for tweaking the logging level. After running -`strip-jar`, run: - - $HEDWIG_DIR/scripts/hw.bash set-logging WARN - -To see what the current logging level is: - - $HEDWIG_DIR/scripts/hw.bash get-logging diff --git a/src/contrib/hedwig/doc/dev.txt b/src/contrib/hedwig/doc/dev.txt deleted file mode 100644 index e40d1a4bf8a..00000000000 --- a/src/contrib/hedwig/doc/dev.txt +++ /dev/null @@ -1,338 +0,0 @@ -% Developer's Guide - -Style -===== - -We have provided an Eclipse Formatter file `formatter.xml` with all the -formatting conventions currently used in the project. Highlights include no -tabs, 4-space indentation, and 120-char width. Please respect this so as to -reduce the amount of formatting-related noise produced in commits. - -Static Analysis -=============== - -We would like to use static analysis tools PMD and FindBugs to maintain code -quality. However, we have not yet arrived at a consensus on what rules to -adhere to, and what to ignore. - -Netty Notes -=========== - -The asynchronous network IO infrastructure that Hedwig uses is [Netty]. Here -are some notes on Netty's concurrency architecture and its filter pipeline -design. - -[Netty]: http://www.jboss.org/netty - -Concurrency Architecture ------------------------- - -After calling `ServerBootstrap.bind()`, Netty starts a boss thread -(`NioServerSocketPipelineSink.Boss`) that just accepts new connections and -registers them with one of the workers from the `NioWorker` pool in round-robin -fashion (pool size defaults to CPU count). Each worker runs its own select -loop over just the set of keys that have been registered with it. Workers -start lazily on demand and run only so long as there are interested fd's/keys. -All selected events are handled in the same thread and sent up the pipeline -attached to the channel (this association is established by the boss as soon as -a new connection is accepted). - -All workers, and the boss, run via the executor thread pool; hence, the -executor must support at least two simultaneous threads. - -Handler Pipeline ----------------- - -A pipeline implements the intercepting filter pattern. A pipeline is a -sequence of handlers. Whenever a packet is read from the wire, it travels up -the stream, stopping at each handler that can handle upstream events. -Vice-versa for writes. Between each filter, control flows back through the -centralized pipeline, and a linked list of contexts keeps track of where we are -in the pipeline (one context object per handler). - -Distributed Performance Evaluation -================================== - -We've included some scripts to repeatedly run varying configurations of Hedwig -on a distributed testbed and collect the resulting data. The experiments use -the `org.apache.hedwig.client.App` client application and are driven by -`scripts/hw.bash` (via the `app` command). - -Currently, we have two types of experiments: subscription benchmarks and -publishing benchmarks. - -Subscription Benchmarks ------------------------ - -The subscription benchmark compares synchronous and asynchronous subscriptions. -Because the synchronicity of subscriptions is a server configuration parameter, -the servers must be restarted to change this. The benchmarks varies the -maximum number of outstanding subscription requests. - -To run the subscription benchmark with wilbur6 as the subscriber and wilbur1 as -its default hub: - - hosts=wilbur6 scripts/hw.bash sub-exp wilbur1 - -This produces log files into the `sub` directory, which may then be analyzed -using the analysis scripts. - -Publishing Benchmarks ---------------------- - -The publishing benchmark measures the throughput and latency of publishing -messages within a LAN and across a WAN. It varies the following parameters: - -- maximum number of outstanding publish requests -- number of publishers -- number of (local) receivers - -We vary each dimension separately (and have default settings) to avoid a -combinatorial explosion in the number of configurations to explore. - -First, start a (standalone) instance: - - scripts/hw.bash start-region '' $hwhost $zkhost $bk1host $bk2host $bk3host - -To run this over `$host1` through `$host3`, with the number of -publishers/subscribers varying linearly over this set: - - npars="20 40 60 80 100" scripts/hw.bash pub-exps "$host1 $host2 $host3" $hwhost $zkhost - -This will vary the number of outstanding publish requests as specified in -`npars`. - -You may also optionally run this experiment with a second subscribing region: - - scripts/hw.bash start-zk-bks $zkhost $bk1host $bk2host $bk3host - npars="..." scripts/hw.bash pub-exps "$host1 $host2 $host3" $hwhost $zkhost $rrecv $rhwhost $rzkhost - -where the final three extra arguments specify the client receiver, Hedwig, and -Zookeeper hosts, in that order. - -This command will produce files into `./pub/`, which can then be process using -`analyze.py`. - -Analysis and Visualization -========================== - -`scripts/analyze.py` produces plots from the collected experimental data. It -has just a few immediate dependencies. In the following, the -indentation signifies nested dependencies, like an upside-down tree: - - component AAA that component AA requires - component AAB that component AA requires - component AA that component A requires - component ABA that component AB requires - component ABB that component AB requires - component AB that component A requires - component A that analysis tools depend on - component BAA that component BA requires - component BAB that component BA requires - component BA that component B requires - component BBA that component BB requires - component BBB that component BB requires - component BB that component B requires - component B that analysis tools depend on - -The reason the tree is upside-down is so that you can treat this whole thing as -a chunk of bash script. - -[toast] is a utility that makes it a breeze to install all this software, but -you do need to make sure your environment is set up correctly (e.g. -`PKG_CONFIG_PATH` must point to `~/.toast/armed/lib/pkgconfig/`). - -Setup: - - wget -O- http://toastball.net/toast/toast|perl -x - arm toast - - toast arm "http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2" - - toast arm numpy - - toast arm libpng - - toast arm pixman - - toast arm freetype - - toast arm 'ftp://xmlsoft.org/libxml2/libxml2-2.7.3.tar.gz' - - toast arm fontconfig - - toast arm cairo - - toast arm pycairo - - hg clone https://yang@bitbucket.org/yang/pycha/ - pycha/setup.bash -d -p $path_to_install_to - - svn co https://assorted.svn.sourceforge.net/svnroot/assorted/python-commons/trunk/ python-commons/ - python-commons/setup.bash -d -p $path_to_install_to - -To analyze the publishing experiments, change to the `pub` data directory and -run: - - scripts/analyze.py pub - -To analyze the subscription experiments, change to the `sub` data directory -and run: - - scripts/analyze.py sub - -[toast]: http://toastball.net/toast/ - -Debugging -========= - -You can attach an Eclipse debugger (or any debugger) to a Java process running -on a remote host, as long as it has been started with the appropriate JVM -flags. (See the Building Hedwig document to set up your Eclipse environment.) -To launch something using `hw.bash` with debugger attachment enabled, prefix -the command with `attach=true`, e.g.: - - attach=true scripts/hw.bash start-regions myregions.cfg - -Profiling -========= - -The scripts we have provided include ways for you to launch with YourKit -profiling enabled. - -To deploy YourKit onto a number of machines: - - hosts="..." scripts/hw.bash setup-yjp $path_to_yjp - -where the path points to the [YourKit Linux zip archive] (which is freely -available and doesn't require any license to use). - -Now when using the scripts to run distributed experiments, to profile anything -with YourKit, prefix the command with `use_yjp=true`. E.g.: - - use_yjp=true scripts/hw.bash start-regions regions.cfg - -Now you may start on your local machine the YourKit GUI and connect to the -hosts that you're interested in. - -Note that you may want to disable the default set of filters in YourKit. - -[YourKit Linux zip archive]: http://www.yourkit.com/download/yjp-8.0.15.zip - -Pseudocode -========== - -This summarizes the control flow through the system. - - publishhandler - topicmgr.getowner - (maybe) claim the topic, calling back into persmgr.acquiredtopic - read /hedwig/standalone/topics/TOPIC (which should initially be empty) - for each line, parse as "STARTSEQ\tLEDGERID" # TODO how is this written? - ledger = bk.openledger(ledgerid) - lastid = ledger.getlast - if lastid > 0, lrs[startseq] = persmgr.ledger2lr[ledgerid] = new LedgerRange(ledger, ledgerid, startseq, startseq + lastid # TODO what are ledger ranges? - create new ledger for topic - # TODO read - lr = new LedgerRange(ledger, ledgerid, lastid, -1) - lrs[lastid] = lr - persmgr.topic2ranges[topic] = lrs - add region info to pub req and send that to persmgr.persistmessage - entryid = persmgr.topic2ranges[topic].last.ledger.addentry(the pub'd data) - update persmgr.topic2lastseq[topic]: - .local = persmgr.ledger2lr[ledger id].startseq + entryid - .regions = maxes of orig seq and incoming pub seq - - subscribehandler - topicmgr.getowner... - delivmgr.startservingsubscription(topic, endpoint, ishubsubscriber) - delivmgr.endpoint2sub[endpoint] = new subscriber(lastseq = persmgr.getcurrentseqidfortopic(topic).local) - delivmgr.topic2ptr2subs[topic][ptr].add(sub) - sub.delivernextmessage - sub.curseq = persmgr.getseqidafterskipping(topic, sub.lastseq, skip = 1) - msg = persmgr.scansinglemessage(topic, seq = sub.curseq) - if persmgr.topic2lastseq[topic].local >= seq - lr = persmgr.topic2ranges[topic].floor(seq) - return lr.ledger.read(first = last = seq - lr.startseq) - if failed, then retry in 1 s - endpoint.send(msg) - movedeliveryptr - delivmgr.topic2ptr2subs[topic][sub.lastseq].remove(sub) - delivmgr.topic2ptr2subs[topic][sub.curseq].add(sub) - previd = sub.lastseq, sub.lastseq = sub.curseq - sub.delivernextmessage... - -ReadAhead Cache -================ - -The delivery manager class is responsible for pushing published messages from -the hubs to the subscribers. The most common case is that all subscribers are -connected and either caught up, or close to the tail end of the topic. In this -case, we don't want the delivery manager to be polling bookkeeper for any newly -arrived messages on the topic; new messages should just be pushed to the -delivery manager. However, there is also the uncommon case when a subscriber is -behind, and messages must be pulled from Bookkeeper. - -Since all publishes go through the hub, it is possible to cache the recently -published messages in the hub, and then the delivery manager won't have to make -the trip to bookkeeper to get the messages but instead get them from local -process memory. - -These ideas of push, pull, and caching are unified in the following way: -- A hub has a cache of messages - -- When the delivery manager wants to deliver a message, it asks the cache for - it. There are 3 cases: - - The message is available in the cache, in which case it is given to the - delivery manager - - The message is not present in the cache and the seq-id of the message is - beyond the last message published on that topic (this happens if the - subscriber is totally caught up for that topic). In this case, a stub is put - in the cache in order to notify the delivery manager when that message does - happen to be published. - - The message is not in the cache but has been published to the topic. In this - case, a stub is put in the cache, and a read is issued to bookkeeper. - -- Whenever a message is published, it is cached. If there is a stub already in - the cache for that message, the delivery manager is notified. - -- Whenever a message is read from bookkeeper, it is cached. There must be a stub - for that message (since reads to bookkeeper are issued only after putting a - stub), so the delivery manager is notified. - -- The cache does readahead, i.e., if a message requested by the delivery manager - is not in the cache, a stub is established not only for that message, but also - for the next n messages where n is configurable (default 10). On a cache hit, - we look ahead n/2 messages, and if that message is not present, we establish - another n/2 stubs. In short, we always ensure that the next n stubs are always - established. - -- Over time, the cache will grow in size. There are 2 pruning mechanisms: - - - Once all subscribers have consumed up to a particular seq-id, they notify - the cache, and all messages up to that seq-id are pruned from the cache. - - If the above pruning is not working (e.g., because some subscribers are - down), the cache will eventually hit its size limit which is configurable - (default, half of maximum jvm heap size). At this point, messages are just - pruned in FIFO order. We use the size of the blobs in the message for - estimating the cache size. The assumption is that that size will dominate - over fixed, object-level size overheads. - - Stubs are not purged because according to the above simplification, they are - of 0 size. - -Scalability Bottlenecks Down the Road -===================================== - -- Currently each topic subscription is served on a different channel. The number - of channels will become a bottleneck at higher channels. We should switch to - an architecture, where multiple topic subscriptions between the same client, - hub pair should be served on the same channel. We can have commands to start, - stop subscriptions sent all the way to the server (right now these are local). -- Publishes for a topic are serialized through a hub, to get ordering - guarantees. Currently, all subscriptions to that topic are served from the - same hub. If we start having large number of subscribers to heavy-volume - topics, the outbound bandwidth at the hub, or the CPU at that hub might become - the bottleneck. In that case, we can setup other regions through which the - messages are routed (this hierarchical scheme) reduces bandwidth requirements - at any single node. It should be possible to do this entirely through - configuration. - diff --git a/src/contrib/hedwig/doc/doc.txt b/src/contrib/hedwig/doc/doc.txt deleted file mode 100644 index 36f2c0bae97..00000000000 --- a/src/contrib/hedwig/doc/doc.txt +++ /dev/null @@ -1,17 +0,0 @@ -% Meta-Documentation - -In the documentation directory, you'll find: - -- `build.txt`: Building Hedwig, or how to set up Hedwig -- `user.txt`: User's Guide, or how to program against the Hedwig API and how to - run it -- `dev.txt`: Developer's Guide, or Hedwig internals and hacking details - -These documents are all written in the [Pandoc] dialect of [Markdown]. This -makes them readable as plain text files, but also capable of generating HTML or -LaTeX documentation. - -[Pandoc]: http://johnmacfarlane.net/pandoc/ -[Markdown]: http://daringfireball.net/projects/markdown/ - -Documents are wrapped at 80 chars and use 2-space indentation. diff --git a/src/contrib/hedwig/doc/user.txt b/src/contrib/hedwig/doc/user.txt deleted file mode 100644 index 242e97695ec..00000000000 --- a/src/contrib/hedwig/doc/user.txt +++ /dev/null @@ -1,252 +0,0 @@ -% User's Guide -% Yang Zhang - -Design -====== - -In Hedwig, clients publish messages associated with a topic, and they subscribe -to a topic to receive all messages published with that topic. Clients are -associated with (publish to and subscribe from) a Hedwig _instance_ (also -referred to as a _region_), which consists of a number of servers called -_hubs_. The hubs partition up topic ownership among themselves, and all -publishes and subscribes to a topic must be done to its owning hub. When a -client doesn't know the owning hub, it tries a default hub, which may redirect -the client. - -Running a Hedwig instance requires a Zookeeper server and at least three -Bookkeeper servers. - -An instance is designed to run within a datacenter. For wide-area messaging -across datacenters, specify in the server configuration the set of default -servers for each of the other instances. Dissemination among instances -currently takes place over an all-to-all topology. Local subscriptions cause -the hub to subscribe to all other regions on this topic, so that the local -region receives all updates to it. Future work includes allowing the user to -overlay alternative topologies. - -Because all messages on a topic go through a single hub per region, all -messages within a region are ordered. This means that, for a given topic, -messages are delivered in the same order to all subscribers within a region, -and messages from any particular region are delivered in the same order to all -subscribers globally, but messages from different regions may be delivered in -different orders to different regions. Providing global ordering is -prohibitively expensive in the wide area. However, in Hedwig clients such as -PNUTS, the lack of global ordering is not a problem, as PNUTS serializes all -updates to a table row at a single designated master for that row. - -Topics are independent; Hedwig provides no ordering across different topics. - -Version vectors are associated with each topic and serve as the identifiers for -each message. Vectors consist of one component per region. A component value -is the region's local sequence number on the topic, and is incremented each -time a hub persists a message (published either locally or remotely) to BK. - -TODO: More on how version vectors are to be used, and on maintaining -vector-maxes. - -Entry Points -============ - -The main class for running the server is -`org.apache.hedwig.server.netty.PubSubServer`. It takes a single argument, -which is a [Commons Configuration] file. Currently, for configuration, the -source is the documentation. See -`org.apache.hedwig.server.conf.ServerConfiguration` for server configuration -parameters. - -The client is a library intended to be consumed by user applications. It takes -a Commons Configuration object, for which the source/documentation is in -`org.apache.hedwig.client.conf.ClientConfiguration`. - -We have provided a simple client application, `org.apache.hedwig.client.App`, -that can drive a number of benchmarks. This also takes a single configuration -file argument, which is fed to the client library. - -We've provided a number of scripts to faciliate running servers and clients -in a variety of configurations, including over distributed hosts. These are -all consolidated in `scripts/hw.bash`. Although the snippets in this -documentation run the script from the hedwig main directory, you can run it -from any location. Apologies in advance for these being bash scripts; time -permitting, a more robust and maintainable support/tooling infrastructure would -be ideal. - -[Commons Configuration]: http://commons.apache.org/configuration/ - -Deployment -========== - -When ssh-ing into a new host, you are requested to verify and accept the host -key. In order to automatically accept the host keys for many new hosts -(dangerous), use: - - hosts="$host1 $host2 ..." scripts/hw.bash warmup - -The `hosts` variable is set here to the list of hosts that you would like to -warm up. - -To set up JDK6 on some hosts, use: - - hosts="..." scripts/hw.bash setup-java $path_to_modified_jdk6 - -The argument must point to a JDK6 binary self-extracting executable, but with -the `more` command that displays the License agreement replaced with -`cat`. Unfortunately, this step must be performed manually. This script will -extract the JDK directly into the home directory and update `$PATH` in -`~/.bashrc` (in an idempotent fashion). - -Because the current implementation uses a single socket per subscription, the -Hedwig launching scripts all require a high `ulimit` on the number of open file -descriptors. Non-root users can only use up to the limit specified in -`/etc/security/limits.conf`; to raise this to 1024^2, run: - - hosts="..." scripts/hw.bash setup-limits - -This uses `ssh` so that you need to enter your password for `sudo` just -once. - -For most of the commands presented in the next section, you may prefix the -command with: - - push_jar=true ... - -to first push the assembly jar (assumed to be available in `server/target/`) to -all hosts. - -Running Servers -=============== - -To start three BK bookies on ports 3181-3183 on localhost (directories must all -exist): - - scripts/hw.bash bk 3181 $bk1_journal_dir $bk1_ledger_dir & - scripts/hw.bash bk 3182 $bk2_journal_dir $bk2_ledger_dir & - scripts/hw.bash bk 3183 $bk3_journal_dir $bk3_ledger_dir & - -To start a ZK on port 2181 (directory must exist): - - scripts/hw.bash zk 2181 /path/for/zk/ & - -To register the BKs with the ZK (so that Hedwig knows where to find the -bookies): - - scripts/hw.bash setup-bk localhost:2181 `hostname`:3181 `hostname`:3182 `hostname`:3183 - -Everything up to this point may be done using a single command over a set of -hosts, with ZK on port 9877 and BK on port 9878. The following function takes -2 arguments. The first is the ZK host. The second is a string list of BK hosts: - - scripts/hw.bash start-zk-bks $zkhost "$bk1host $bk2host $bk3host ..." - -Note that the hosts may be SSH profile aliases in your `~/.ssh/config`; the -script will parse this file and look up their hostnames where necessary. This -applies for the hosts specified in the other commands. - -Also, the scripts use the `bk-journal` and `bk-ledger` functions in `hw.bash` -to determine where to place the BK journal and ledger, given a hostname. - -To start a Hedwig server locally: - - scripts/hw.bash hw server.conf & - -To start Hedwig servers on some hosts "$hw1host $hw2host $hw3host ..." on port 9876, -using $zkhost as the ZK server: - - scripts/hw.bash start-hw '' "$hw1host $hw2host $hw3host ..." $zkhost - -Above, the first empty string argument is the list of default servers to each -of the other regions. You may run multiple connected instances of Hedwig this way. -E.g., to start three regions each with a single Hedwig hub that talk to each other, -and using the hw.bash default server ports of 9875 (non-SSL) and 9876 (SSL): - - scripts/hw.bash start-hw "$hw2host:9875:9876 $hw3host:9875:9876" "$hw1host" $zk1host - scripts/hw.bash start-hw "$hw1host:9875:9876 $hw3host:9875:9876" "$hw2host" $zk2host - scripts/hw.bash start-hw "$hw1host:9875:9876 $hw2host:9875:9876" "$hw3host" $zk3host - -Everything up to this point may be done using a single command over a set of -hosts: - - scripts/hw.bash start-region '' "$hw1host $hw2host $hw3host ..." $zkhost "$bk1host $bk2host $bk3host ..." - -The first three arguments are the same as for `start-hw`. - -You may start multiple regions as well: - - scripts/hw.bash start-regions regions.cfg - -"regions.cfg" is a list of all regions, one per line, with each region having -the following format: - - region=, hub=, default=, zk=, bk= - -This will create all of the regions with an all-to-all topology. Each region -is connected to the default hub server of every other region. The ", " delimiter -is used to separate out the different parts of a region along with the hard-coded -parameter names. There also needs to be a newline after the last region line. -Here is an example file specifying three regions: - - region=wilbur, hub=wilbur90 wilbur91, default=wilbur90, zk=wilbur93, bk=wilbur93 wilbur94 wilbur95 - region=re1, hub=sherpa7 sherpa8, default=sherpa7, zk=sherpa9, bk=sherpa9 sherpa10 sherpa11 - region=peanuts, hub=peanuts1 peanuts2, default=peanuts2, zk=peanuts3, bk=peanuts3 peanuts4 peanuts5 - -Running the Client -================== - -To run the test client: - - JAVAFLAGS="..." scripts/hw.bash hwc $conf_path - -where `$conf_path` is a client configuration file. - -To run the test client on some other hosts: - - hosts="..." JAVAFLAGS="..." scripts/hw.bash app $hwhost - -This will generate a simple configuration file assuming $hwhost is listening on -the default SSL and non-SSL ports which are specified as global variables in hw.bash. -Currently these are 9875 for non-SSL and 9876 for SSL. - -Client usage is currently documented in the source. To run a subscription -benchmark, set `JAVAFLAGS` to: - - -Dmode=sub -Dcount=10000 -Dnpar=100 -Dstart=5 -Dnwarmups=30 - -This will first create 30 warm-up subscriptions to topics "warmup-5" through -"warmup-34", then 10,000 benchmarked subscriptions to topics "topic-5" through -"topic-10,004". It will have a pipeline depth of 100 requests, meaning that -there will be at most 100 outstanding (unresponded) messages in flight at any -moment. - -To run a publishing benchmark, set `JAVAFLAGS` to: - - -Dmode=pub -Dcount=10000 -Dnpar=100 -Dstart=5 - -This will publish 10,000 messages to topic "topic-5", with a pipeline depth of -100 requests. - -At the end, the programs will print throughput and latency information. - -Utilities -========= - -To kill all the user's Java processes running on some machines, use: - - hosts="..." scripts/hw.bash dkill - -To check if any processes are running and are using ports of interest (esp. -9876-9878): - - hosts="..." scripts/hw.bash dstatus - -Add an argument to `dstatus` (may be anything) to get a more detailed listing. - -To check if there's anything consuming the CPU on some machines: - - hosts="..." scripts/hw.bash tops - -To run an arbitrary command on multiple hosts in parallel: - - hosts="..." scripts/hw.bash parssh $command - -To do this in sequence: - - hosts="..." xargs= scripts/hw.bash parssh $command diff --git a/src/contrib/hedwig/formatter.xml b/src/contrib/hedwig/formatter.xml deleted file mode 100644 index f828df1c1e0..00000000000 --- a/src/contrib/hedwig/formatter.xml +++ /dev/null @@ -1,286 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/hedwig/pom.xml b/src/contrib/hedwig/pom.xml deleted file mode 100644 index f7230a1ed03..00000000000 --- a/src/contrib/hedwig/pom.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - 4.0.0 - org.apache.hedwig - 1.0-SNAPSHOT - hedwig - pom - hedwig - - client - server - protocol - - - - - - - maven-compiler-plugin - - 1.6 - 1.6 - - - - - - - - org.apache.maven.plugins - maven-jxr-plugin - - - org.codehaus.mojo - findbugs-maven-plugin - 2.1 - - - org.apache.maven.plugins - maven-pmd-plugin - 2.3 - - true - 1.6 - - - - - diff --git a/src/contrib/hedwig/protocol/Makefile b/src/contrib/hedwig/protocol/Makefile deleted file mode 100644 index 45a5353a4fe..00000000000 --- a/src/contrib/hedwig/protocol/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -TARGET_DIR = target/generated-sources/cpp -PROTO_DIR = src/main/protobuf - -INCLUDES = -I$(TARGET_DIR) - -CXX = g++ -CXXFLAGS = -g $(INCLUDES) - -#-----File Dependencies---------------------- -PROTO = PubSubProtocol.proto -SRC = $(TARGET_DIR)/$/$(PROTO_DIR)/$(addsuffix .pb.cc, $(basename $(PROTO))) -OBJ = $(addsuffix .o, $(basename $(SRC))) - -all: $(OBJ) - -$(SRC): - mkdir -p $(TARGET_DIR); protoc --cpp_out=$(TARGET_DIR) $(PROTO_DIR)/$(PROTO) - -$(OBJ): $(SRC) - $(CXX) $(CXXFLAGS) -c $< -o $(OBJ) - - -clean: - rm -rf $(TARGET_DIR) - - diff --git a/src/contrib/hedwig/protocol/pom.xml b/src/contrib/hedwig/protocol/pom.xml deleted file mode 100644 index 1627c29543d..00000000000 --- a/src/contrib/hedwig/protocol/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - 4.0.0 - - org.apache.hedwig - hedwig - 1.0-SNAPSHOT - - org.apache.hedwig - protocol - jar - 1.0-SNAPSHOT - protocol - http://maven.apache.org - - - com.google.protobuf - protobuf-java - 2.3.0 - compile - - - junit - junit - 4.8.1 - test - - - - - - install - - - maven-antrun-plugin - - - generate-sources - generate-sources - - - - - - - - - - - target/generated-sources/java - - - run - - - - - - - diff --git a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/exceptions/PubSubException.java b/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/exceptions/PubSubException.java deleted file mode 100644 index 56a24064564..00000000000 --- a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/exceptions/PubSubException.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.exceptions; - -import java.util.Collection; - -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; - -@SuppressWarnings("serial") -public abstract class PubSubException extends Exception { - protected StatusCode code; - - protected PubSubException(StatusCode code, String msg) { - super(msg); - this.code = code; - } - - protected PubSubException(StatusCode code, Exception e) { - super(e); - this.code = code; - } - - public static PubSubException create(StatusCode code, String msg) { - if (code == StatusCode.CLIENT_ALREADY_SUBSCRIBED) { - return new ClientAlreadySubscribedException(msg); - } else if (code == StatusCode.CLIENT_NOT_SUBSCRIBED) { - return new ClientNotSubscribedException(msg); - } else if (code == StatusCode.MALFORMED_REQUEST) { - return new MalformedRequestException(msg); - } else if (code == StatusCode.NO_SUCH_TOPIC) { - return new NoSuchTopicException(msg); - } else if (code == StatusCode.NOT_RESPONSIBLE_FOR_TOPIC) { - return new ServerNotResponsibleForTopicException(msg); - } else if (code == StatusCode.SERVICE_DOWN) { - return new ServiceDownException(msg); - } else if (code == StatusCode.COULD_NOT_CONNECT) { - return new CouldNotConnectException(msg); - } - /* - * Insert new ones here - */ - else if (code == StatusCode.UNCERTAIN_STATE) { - return new UncertainStateException(msg); - } - // Finally the catch all exception (for unexpected error conditions) - else { - return new UnexpectedConditionException("Unknow status code:" + code.getNumber() + ", msg: " + msg); - } - } - - public StatusCode getCode() { - return code; - } - - public static class ClientAlreadySubscribedException extends PubSubException { - public ClientAlreadySubscribedException(String msg) { - super(StatusCode.CLIENT_ALREADY_SUBSCRIBED, msg); - } - } - - public static class ClientNotSubscribedException extends PubSubException { - public ClientNotSubscribedException(String msg) { - super(StatusCode.CLIENT_NOT_SUBSCRIBED, msg); - } - } - - public static class MalformedRequestException extends PubSubException { - public MalformedRequestException(String msg) { - super(StatusCode.MALFORMED_REQUEST, msg); - } - } - - public static class NoSuchTopicException extends PubSubException { - public NoSuchTopicException(String msg) { - super(StatusCode.NO_SUCH_TOPIC, msg); - } - } - - public static class ServerNotResponsibleForTopicException extends PubSubException { - // Note the exception message serves as the name of the responsible host - public ServerNotResponsibleForTopicException(String responsibleHost) { - super(StatusCode.NOT_RESPONSIBLE_FOR_TOPIC, responsibleHost); - } - } - - public static class TopicBusyException extends PubSubException { - public TopicBusyException(String msg) { - super(StatusCode.TOPIC_BUSY, msg); - } - } - - public static class ServiceDownException extends PubSubException { - public ServiceDownException(String msg) { - super(StatusCode.SERVICE_DOWN, msg); - } - - public ServiceDownException(Exception e) { - super(StatusCode.SERVICE_DOWN, e); - } - } - - public static class CouldNotConnectException extends PubSubException { - public CouldNotConnectException(String msg) { - super(StatusCode.COULD_NOT_CONNECT, msg); - } - } - - /* - * Insert new ones here - */ - public static class UncertainStateException extends PubSubException { - public UncertainStateException(String msg) { - super(StatusCode.UNCERTAIN_STATE, msg); - } - } - - // The catch all exception (for unexpected error conditions) - public static class UnexpectedConditionException extends PubSubException { - public UnexpectedConditionException(String msg) { - super(StatusCode.UNEXPECTED_CONDITION, msg); - } - } - - // The composite exception (for concurrent operations). - public static class CompositeException extends PubSubException { - private final Collection exceptions; - public CompositeException(Collection exceptions) { - super(StatusCode.COMPOSITE, "composite exception"); - this.exceptions = exceptions; - } - public Collection getExceptions() { - return exceptions; - } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(super.toString()).append('\n'); - for (PubSubException exception : exceptions) - builder.append(exception).append('\n'); - return builder.toString(); - } - } - - public static class ClientNotSubscribedRuntimeException extends RuntimeException { - } - -} diff --git a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/MessageIdUtils.java b/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/MessageIdUtils.java deleted file mode 100644 index 83a3fbf222f..00000000000 --- a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/MessageIdUtils.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.protoextensions; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.exceptions.PubSubException.UnexpectedConditionException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.RegionSpecificSeqId; - -public class MessageIdUtils { - - public static String msgIdToReadableString(MessageSeqId seqId) { - StringBuilder sb = new StringBuilder(); - sb.append("local:"); - sb.append(seqId.getLocalComponent()); - - String separator = ";"; - for (RegionSpecificSeqId regionId : seqId.getRemoteComponentsList()) { - sb.append(separator); - sb.append(regionId.getRegion().toStringUtf8()); - sb.append(':'); - sb.append(regionId.getSeqId()); - } - return sb.toString(); - } - - public static Map inMapForm(MessageSeqId msi) { - Map map = new HashMap(); - - for (RegionSpecificSeqId lmsid : msi.getRemoteComponentsList()) { - map.put(lmsid.getRegion(), lmsid); - } - - return map; - } - - public static boolean areEqual(MessageSeqId m1, MessageSeqId m2) { - - if (m1.getLocalComponent() != m2.getLocalComponent()) { - return false; - } - - if (m1.getRemoteComponentsCount() != m2.getRemoteComponentsCount()) { - return false; - } - - Map m2map = inMapForm(m2); - - for (RegionSpecificSeqId lmsid1 : m1.getRemoteComponentsList()) { - RegionSpecificSeqId lmsid2 = m2map.get(lmsid1.getRegion()); - if (lmsid2 == null) { - return false; - } - if (lmsid1.getSeqId() != lmsid2.getSeqId()) { - return false; - } - } - - return true; - - } - - public static Message mergeLocalSeqId(Message.Builder messageBuilder, long localSeqId) { - MessageSeqId.Builder msidBuilder = MessageSeqId.newBuilder(messageBuilder.getMsgId()); - msidBuilder.setLocalComponent(localSeqId); - messageBuilder.setMsgId(msidBuilder); - return messageBuilder.build(); - } - - public static Message mergeLocalSeqId(Message orginalMessage, long localSeqId) { - return mergeLocalSeqId(Message.newBuilder(orginalMessage), localSeqId); - } - - /** - * Compares two seq numbers represented as lists of longs. - * - * @param l1 - * @param l2 - * @return 1 if the l1 is greater, 0 if they are equal, -1 if l2 is greater - * @throws UnexpectedConditionException - * If the lists are of unequal length - */ - public static int compare(List l1, List l2) throws UnexpectedConditionException { - if (l1.size() != l2.size()) { - throw new UnexpectedConditionException("Seq-ids being compared have different sizes: " + l1.size() - + " and " + l2.size()); - } - - for (int i = 0; i < l1.size(); i++) { - long v1 = l1.get(i); - long v2 = l2.get(i); - - if (v1 == v2) { - continue; - } - - return v1 > v2 ? 1 : -1; - } - - // All components equal - return 0; - } - - /** - * Returns the element-wise vector maximum of the two vectors id1 and id2, - * if we imagine them to be sparse representations of vectors. - */ - public static void takeRegionMaximum(MessageSeqId.Builder newIdBuilder, MessageSeqId id1, MessageSeqId id2) { - Map id2Map = MessageIdUtils.inMapForm(id2); - - for (RegionSpecificSeqId rrsid1 : id1.getRemoteComponentsList()) { - ByteString region = rrsid1.getRegion(); - - RegionSpecificSeqId rssid2 = id2Map.get(region); - - if (rssid2 == null) { - newIdBuilder.addRemoteComponents(rrsid1); - continue; - } - - newIdBuilder.addRemoteComponents((rrsid1.getSeqId() > rssid2.getSeqId()) ? rrsid1 : rssid2); - - // remove from map - id2Map.remove(region); - } - - // now take the remaining components in the map and add them - for (RegionSpecificSeqId rssid2 : id2Map.values()) { - newIdBuilder.addRemoteComponents(rssid2); - } - - } -} diff --git a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/PubSubResponseUtils.java b/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/PubSubResponseUtils.java deleted file mode 100644 index 8660544be6f..00000000000 --- a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/PubSubResponseUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.protoextensions; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; - -public class PubSubResponseUtils { - - /** - * Change here if bumping up the version number that the server sends back - */ - protected static ProtocolVersion serverVersion = ProtocolVersion.VERSION_ONE; - - static PubSubResponse.Builder getBasicBuilder(StatusCode status) { - return PubSubResponse.newBuilder().setProtocolVersion(serverVersion).setStatusCode(status); - } - - public static PubSubResponse getSuccessResponse(long txnId) { - return getBasicBuilder(StatusCode.SUCCESS).setTxnId(txnId).build(); - } - - public static PubSubResponse getResponseForException(PubSubException e, long txnId) { - return getBasicBuilder(e.getCode()).setStatusMsg(e.getMessage()).setTxnId(txnId).build(); - } -} diff --git a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/SubscriptionStateUtils.java b/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/SubscriptionStateUtils.java deleted file mode 100644 index a22e8c12823..00000000000 --- a/src/contrib/hedwig/protocol/src/main/java/org/apache/hedwig/protoextensions/SubscriptionStateUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.protoextensions; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.SubscriptionState; - -public class SubscriptionStateUtils { - - // For now, to differentiate hub subscribers from local ones, the - // subscriberId will be prepended with a hard-coded prefix. Local - // subscribers will validate that the subscriberId used cannot start with - // this prefix. This is only used internally by the hub subscribers. - public static final String HUB_SUBSCRIBER_PREFIX = "__"; - - public static String toString(SubscriptionState state) { - StringBuilder sb = new StringBuilder(); - sb.append("consumeSeqId: " + MessageIdUtils.msgIdToReadableString(state.getMsgId())); - return sb.toString(); - } - - public static boolean isHubSubscriber(ByteString subscriberId) { - return subscriberId.toStringUtf8().startsWith(HUB_SUBSCRIBER_PREFIX); - } - -} diff --git a/src/contrib/hedwig/protocol/src/main/protobuf/PubSubProtocol.proto b/src/contrib/hedwig/protocol/src/main/protobuf/PubSubProtocol.proto deleted file mode 100644 index e44d9818d8b..00000000000 --- a/src/contrib/hedwig/protocol/src/main/protobuf/PubSubProtocol.proto +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -option java_package = "org.apache.hedwig.protocol"; -option optimize_for = SPEED; -package Hedwig; - -enum ProtocolVersion{ - VERSION_ONE = 1; -} - -/* - * this is the structure that will be serialized - */ -message Message { - required bytes body = 1; - optional bytes srcRegion = 2; - optional MessageSeqId msgId = 3; -} - -message RegionSpecificSeqId { - required bytes region = 1; - required uint64 seqId = 2; -} - -message MessageSeqId{ - optional uint64 localComponent = 1; - repeated RegionSpecificSeqId remoteComponents = 2; -} - -enum OperationType{ - PUBLISH = 0; - SUBSCRIBE = 1; - CONSUME = 2; - UNSUBSCRIBE = 3; - - //the following two are only used for the hedwig proxy - START_DELIVERY = 4; - STOP_DELIVERY = 5; -} - -/* A PubSubRequest is just a union of the various request types, with - * an enum telling us which type it is. The same can also be done through - * extensions. We need one request type that we will deserialize into on - * the server side. - */ -message PubSubRequest{ - - required ProtocolVersion protocolVersion = 1; - required OperationType type = 2; - repeated bytes triedServers = 3; - required uint64 txnId = 4; - optional bool shouldClaim = 5; - required bytes topic = 6; - //any authentication stuff and other general stuff here - - - /* one entry for each type of request */ - optional PublishRequest publishRequest = 52; - optional SubscribeRequest subscribeRequest = 53; - optional ConsumeRequest consumeRequest = 54; - optional UnsubscribeRequest unsubscribeRequest = 55; - optional StopDeliveryRequest stopDeliveryRequest = 56; - optional StartDeliveryRequest startDeliveryRequest = 57; -} - - - -message PublishRequest{ - required Message msg = 2; -} - -message SubscribeRequest{ - required bytes subscriberId = 2; - - enum CreateOrAttach{ - CREATE = 0; - ATTACH = 1; - CREATE_OR_ATTACH = 2; - }; - optional CreateOrAttach createOrAttach = 3 [default = CREATE_OR_ATTACH]; - - // wait for cross-regional subscriptions to be established before returning - optional bool synchronous = 4 [default = false]; -} - -message ConsumeRequest{ - required bytes subscriberId = 2; - required MessageSeqId msgId = 3; - //the msgId is cumulative: all messages up to this id are marked as consumed -} - -message UnsubscribeRequest{ - required bytes subscriberId = 2; -} - - -message StopDeliveryRequest{ - required bytes subscriberId = 2; -} - -message StartDeliveryRequest{ - required bytes subscriberId = 2; -} - -message PubSubResponse{ - required ProtocolVersion protocolVersion = 1; - required StatusCode statusCode = 2; - required uint64 txnId = 3; - - - optional string statusMsg = 4; - //in case of a status code of NOT_RESPONSIBLE_FOR_TOPIC, the status - //message will contain the name of the host actually responsible - //for the topic - - //the following fields are sent in delivered messages - optional Message message = 5; - optional bytes topic = 6; - optional bytes subscriberId = 7; -} - - -enum StatusCode{ - SUCCESS = 0; - - //client-side errors (4xx) - MALFORMED_REQUEST = 401; - NO_SUCH_TOPIC = 402; - CLIENT_ALREADY_SUBSCRIBED = 403; - CLIENT_NOT_SUBSCRIBED = 404; - COULD_NOT_CONNECT = 405; - TOPIC_BUSY = 406; - - //server-side errors (5xx) - NOT_RESPONSIBLE_FOR_TOPIC = 501; - SERVICE_DOWN = 502; - UNCERTAIN_STATE = 503; - - //For all unexpected error conditions - UNEXPECTED_CONDITION = 600; - - COMPOSITE = 700; -} - -//What follows is not the server client protocol, but server-internal structures that are serialized in ZK -//They should eventually be moved into the server - -message SubscriptionState { - required MessageSeqId msgId = 1; -} - -message LedgerRange{ - required uint64 ledgerId = 1; - optional MessageSeqId endSeqIdIncluded = 2; -} - -message LedgerRanges{ - repeated LedgerRange ranges = 1; -} - diff --git a/src/contrib/hedwig/scripts/README.txt b/src/contrib/hedwig/scripts/README.txt deleted file mode 100644 index 346da2ccfb4..00000000000 --- a/src/contrib/hedwig/scripts/README.txt +++ /dev/null @@ -1,39 +0,0 @@ -To start up a Hedwig server, you can make use of the scripts in this directory. -The hw.bash script is used to setup a Hedwig region cluster on remote boxes. -It contains methods that allow the user to start up a ZooKeeper server -(currently only a single quorum) and also any number of Bookkeeper servers. -It can also startup any number of Hedwig server hubs that point to this -ZooKeeper/Bookkeeper setup. - -To simplify and generalize things, the hwServer.sh script is used to start -up a single Hedwig server hub on the current local machine. It assumes that -the ZooKeeper and Bookkeeper servers are setup and running already. The order -of operations prior to starting up the Hedwig server hub(s) is: - -1. Startup a quorum of ZooKeeper servers (could be a single one). -2. Using a ZooKeeper client to connect to the servers, create the following -ZK nodes to be used by Bookkeeper as directory path nodes. - /ledgers and /ledgers/available -3. Startup Bookkeeper servers pointing them to this ZooKeeper quorum. -4. For each machine you want to run a Hedwig server hub on, the Hedwig code -needs to be there. Compile/build it from the top level with the command: - mvn install -Dmaven.test.skip=true -In the server/target directory, this creates the following fat jar that -contains all dependencies the Hedwig server needs to run: - server-1.0-SNAPSHOT-jar-with-dependencies.jar -5. Define your Hedwig server configuration before you start the server. The -default location and file for this is: - conf/hw_server.conf -However, you can override this by setting the appropriate environment -variables or passing in your config file directly when invoking the -hwServer.sh script. A sample config file is available for you at: - conf/hw_server_sample.conf -The important config parameter is the "zk_host" one which will point the -Hedwig server hub to the ZooKeeper quorum which manages and coordinates all of -the hubs. -6. Run the hwServer.sh script to start up the server: - scripts/hwServer.sh start -OR scripts/hwServer.sh start -7. Stop or restart the Hedwig server hub using the following commands: - scripts/hwServer.sh stop - scripts/hwServer.sh restart diff --git a/src/contrib/hedwig/scripts/analyze.py b/src/contrib/hedwig/scripts/analyze.py deleted file mode 100755 index 6a468c9c406..00000000000 --- a/src/contrib/hedwig/scripts/analyze.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env python - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import with_statement -import sys, os, glob, re, collections, math, subprocess, unittest -from numpy import * -from commons import seqs, startup, strs, structs -import cairo -from pycha.bar import * - -def sub(): - - # - # Parse/aggregate. - # - - tputs = collections.defaultdict(list) - lats = collections.defaultdict(list) - for fname in glob.glob('sync-*-count-*-npar-*-rep-*.out'): - m = re.match(r'sync-(\d+)-count-(\d+)-npar-(\d+)-rep-(\d+)\.out', fname) - sync, count, npar, rep = map(int, m.groups()) - with file(fname) as f: - m = re.search(r'finished subs, tput = ([\d\.]+) ops/s, avg latency = (\d+)', f.readlines()[-2]) - tput, lat = map(float, m.groups()) - tputs[sync, count, npar].append(tput) - lats[sync, count, npar].append(lat) - for d in tputs, lats: - for k in d: - d[k] = array(d[k]).mean(), array(d[k]).std() - print k, d[k] - - # - # Plot. - # - - for title, ylabel, fname, d in [ ('Subscription throughput over three trials', 'Subscriptions per second', 'tput', tputs), - ('Subscription latency over three trials', 'Round-trip time in ms', 'lat', lats) ]: - means = dict((k, v[0]) for k,v in d.iteritems()) - sdevs = dict((k, v[1]) for k,v in d.iteritems()) - - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 400) - syncs, counts, npars = [ sorted(set(x[i] for x in means)) - for i in xrange(3) ] - print syncs, counts, npars - dataset = [ ( '%d topics, %s' % - ( count, 'synchronous' if sync else 'asynchronous' ), - [ ( npar/10, means[sync, count, npar], sdevs[sync, count, npar] ) - for npar in npars ] ) - for count in counts - for sync in syncs ] - options = {'legend.position': - {'top': None, 'left': None, 'bottom': 100, 'right': 20}, - 'axis.x.ticks': [{'v': x, 'label': max(1,10*x)} - for i,(x,y,e) in enumerate(dataset[0][1])], - 'axis.x.label': 'Number of outstanding subscription requests', - 'axis.y.label': ylabel, - 'padding.left': 50, - 'title': title, - 'background.color': '#f0f0f0'} - chart = VerticalBarChart(surface, structs.sparse_dict(options)) - chart.addDataset(dataset) - chart.render() - surface.write_to_png(fname + '.png') - -def pub(): - - def helper(do_pubs): - - def subhelper(keyname, pat, xlabel): - - # - # Parse/aggregate. - # - - print 'Analyzing', keyname, 'for', 'publishers' if do_pubs else 'receivers' - print '========================' - print - - tputs = collections.defaultdict(list) - lats = collections.defaultdict(list) - fnames = [ ( fname, tuple(map(int, m.groups())) ) - for fname, m in filter( lambda m: m[1] is not None, - ( ( fname, re.match(pat, fname) ) - for fname in os.listdir('.') ) ) ] - tup2fname = dict( (tup, fname) for fname, tup in fnames ) - keys, reps, nodes = map(lambda xs: sorted(set(xs)), - zip(*(tup for fname, tup in fnames))) - - raw_table = [] - print '== raw data ==' - raw_table.append( [ keyname, 'rep', 'node', 'tput' ] + ( ['lat'] if do_pubs else [] ) + ['sum/mean tput', 'mean lat'] ) - for key in keys: - for rep in reps: - tmptputs = [] - tmplats = [] - for node in nodes: - if (key, rep, node) in tup2fname: - with file(tup2fname[key, rep, node]) as f: - try: - if do_pubs: - m = re.search(r'finished acked pubs, tput = ([\d\.]+) ops/s, avg latency = (\d+)', f.readlines()[-2]) - tput, lat = map(float, m.groups()) - else: - m = re.search(r'finished recvs, tput = ([\d\.]+) ops/s', f.read()) - [tput] = map(float, m.groups()) - except AttributeError: - print >> sys.stderr, "While processing", tup2fname[key, rep, node] - raise - raw_table.append( [ key, rep, node, tput ] + ( [lat] if do_pubs else [] ) + ['',''] ) - tmptputs.append(tput) - if do_pubs: tmplats.append(lat) - if keyname == 'npubs': tputs[key].append(sum(tmptputs)) - else: tputs[key].append(array(tmptputs).mean()) - if do_pubs: lats[key].append(array(tmplats).mean()) - if len(nodes) > 1: - raw_table.append( [''] * (len(raw_table[0]) - 2) + [tputs[key][-1]] + ( [lats[key][-1]] if do_pubs else [] ) ) - print strs.show_table_by_rows(raw_table) - print - - print '== aggregated over reps ==' - agg_table = [] - agg_table.append( ( keyname, 'mean', 'sd' ) ) - for d in tputs, lats: - for k in d: - d[k] = array(d[k]).mean(), array(d[k]).std() - agg_table.append( ( k, d[k][0], d[k][1] ) ) - print strs.show_table_by_rows(agg_table) - print - - # - # Plot. - # - - if do_pubs: - plots = [ ('Publishing throughput over three trials', 'Publishes per second', '%s-pub-tput' % keyname, tputs), - ('Publishing latency over three trials', 'Round-trip time in ms', '%s-pub-lat' % keyname, lats) ] - else: - plots = [ ('Receiving throughput over three trials', 'Receives per second', '%s-recv-tput' % keyname, tputs) ] - for title, ylabel, fname, d in plots: - means = dict((k, v[0]) for k,v in d.iteritems()) - sdevs = dict((k, v[1]) for k,v in d.iteritems()) - - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 400) - dataset = [ ( 'main', - [ ( i, means[key], sdevs[key] ) - for i, key in enumerate(keys) ] ) ] - options = {'legend.position': - {'top': None, 'left': None, 'bottom': 100, 'right': 20}, - 'axis.x.ticks': [{'v': x, 'label': k} - for k,(x,y,e) in zip(keys, dataset[0][1])], - 'axis.x.label': xlabel, - 'axis.y.label': ylabel, - 'padding.left': 50, - 'title': title, - 'background.color': '#f0f0f0'} - chart = VerticalBarChart(surface, structs.sparse_dict(options)) - chart.addDataset(dataset) - chart.render() - surface.write_to_png(fname + '.png') - - print - print - print - - nodetype = 'pub' if do_pubs else 'recv' - mode_npar = seqs.mode( - int(m.group(1)) for m in - [re.search('npar-(\d+)', fname) for fname in os.listdir('.')] - if m is not None ) - subhelper('nrecvs', 'nrecvs-(\d+)-npubs-1-npar-%s-rep-(\d+)-%s-(\d+)' % (mode_npar, nodetype), - 'Number of receivers') - subhelper('npubs', 'nrecvs-1-npubs-(\d+)-npar-%s-rep-(\d+)-%s-(\d+)' % (mode_npar, nodetype), - 'Number of publishers') - subhelper('npar', 'nrecvs-1-npubs-1-npar-(\d+)-rep-(\d+)-%s-(0)' % nodetype, - 'Number of outstanding publish requests') - - helper(True) - helper(False) - -def main(argv): - if argv[1] == 'sub': sub() - elif argv[1] == 'pub': pub() - else: return unittest.main() - -startup.run_main() - -# vim: et sw=2 ts=2 diff --git a/src/contrib/hedwig/scripts/hw.bash b/src/contrib/hedwig/scripts/hw.bash deleted file mode 100755 index d31d73f4ebd..00000000000 --- a/src/contrib/hedwig/scripts/hw.bash +++ /dev/null @@ -1,734 +0,0 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This file is a collection of script functions. To use, just run this file -# with the function you want to execute as the first argument and arguments to -# the function following that. -# -# For instance, to install the prereq jars: -# -# ./hw.bash install-zk -# -# Or to run BookKeeper: -# -# ./hw.bash bk 3181 /tmp/bk/{journal,ledgers} - -# Note: unbuffer can cause all sorts of funky problems, especially when dealing -# with high volumes of output from multiple sources! Problems aren't just -# about causing your terminal to get garbled; they may be as severe as killing -# this script itself. - -set -o errexit # -o nounset - -dark_blue="\e[0;34m"; bright_blue="\e[1;34m" -dark_green="\e[0;32m"; bright_green="\e[1;32m" -dark_cyan="\e[0;36m"; bright_cyan="\e[1;36m" -dark_red="\e[0;31m"; bright_red="\e[1;31m" -dark_magenta="\e[0;35m"; bright_magenta="\e[1;35m" -brown="\e[0;33m"; yellow="\e[1;33m" -black="\e[0;30m"; dark_gray="\e[1;30m"; -bold_white="\e[1;37m" white="\e[0;37m" -normal_color="\e[0m" - -# Default server ports used -PROFILER_PORT=9874 -SERVER_PORT=9875 -SSL_SERVER_PORT=9876 -ZOOKEEPER_PORT=9877 -BOOKKEEPER_PORT=9878 - -: ${script:=$0} -serverdir="$(readlink -f "$(dirname "$script")/../server/")" || serverdir= -cmd="$(basename "$script")" -jarbase=server-1.0-SNAPSHOT-jar-with-dependencies.jar -if [[ -f "$(dirname "$script")/$jarbase" ]] -then jar="$(readlink -f "$(dirname "$script")/$jarbase")" -else jar="$serverdir/target/$jarbase" -fi -already_pushed=false -: ${push_jar:=false} ${push_script:=true} ${use_yjp=false} ${unbuffer:=false} -: ${loglevel:=} ${dbg:=false} ${attach:=false} ${logconf:=} ${asserts:=false} -if $unbuffer -then unbuffercmd='unbuffer -p' -else unbuffercmd= -fi -if $dbg -then set -x -fi -if $asserts -then JAVAFLAGS=-ea -fi - -# -# General utilities. -# - -trace() { echo "$@" ; eval "$@" ; } - -# Add the given prefix (first arg) to all subsequent words (latter args). -prefix-words() { - local prefix="$1" - shift - for i in "$@" - do echo "$prefix" "$i" - done -} - -quote(){ - "$(dirname "$script")/quote" "$@" -} - -# Retrieve the substring of a string with a given prefix and suffix. -# For example, substr "Hello World!" "ell" "rld" returns "o Wo". -# Everything before the prefix and after the suffix (inclusive) -# is stripped off and the remaining substring is returned. -substr() { - if [ $# == 3 ] - then - nopref="${1#${1%${2}*}${2}}" - echo "${nopref%${3}*}" - else - echo "Usage: substr string prefix suffix" - fi -} - -# Must test connectivity via ssh because we may be firewalled. -wait-connect() { - local prof="${1%:*}" port="${1#*:}" - local host="$(ssh-hosts $prof)" - while ! echo | ssh "$prof" nc -w 1 localhost "$port" - do sleep 1 - done -} - -# -# Java runners. -# - -# Launch with yourkit profiler. -java-yjp() { - if $use_yjp - then LD_LIBRARY_PATH="${YJP:-$HOME/yjp-8.0.15}/bin/linux-x86-32/" \ - java -agentlib:yjpagent $JAVAFLAGS "$@" - else java $JAVAFLAGS "$@" - fi -} - -with-attach() { - if $attach - then JAVAFLAGS="-agentlib:jdwp=transport=dt_socket,server=y,address=$PROFILER_PORT $JAVAFLAGS" "$@" - else "$@" - fi -} - -with-logging() { - if [[ $loglevel ]] ; then - logconf=" -log4j.rootLogger=$loglevel, A1 -log4j.logger.org.apache.zookeeper = ERROR -log4j.logger.org.apache.bookkeeper.client.QuorumOpMonitor = ERROR -log4j.logger.org.apache.bookkeeper.proto.BookieClient = ERROR - -# A1 is set to be a ConsoleAppender. -log4j.appender.A1=org.apache.log4j.ConsoleAppender - -# A1 uses PatternLayout. -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -log4j.appender.A1.layout.ConversionPattern=%d %-4r [%t] %-5p %c %x - %m%n -" - fi - if [[ "$logconf" ]] ; then - mkdir -p /tmp/$USER/logging/ - local logdir="$(mktemp -d -p /tmp/$USER/logging/)" - echo "$logconf" > "$logdir/log4j.properties" - CLASSPATH="$logdir:$CLASSPATH" "$@" - else - "$@" - fi -} - -try-ulimit() { ulimit -n $((1024**2)) || true ; } -j() { CLASSPATH="$jar" with-logging with-attach java-yjp "$@" ; } -bk() { j org.apache.bookkeeper.proto.BookieServer "$@" ; } -#bk() { j com.yahoo.pubsub.client.benchmark.FakeBookie "$@" ; } -zk() { j org.apache.zookeeper.server.quorum.QuorumPeerMain "$@" ; } -zkc() { j org.apache.zookeeper.ZooKeeperMain -server "$@" ; } -hw() { try-ulimit; j org.apache.hedwig.server.netty.PubSubServer "$@" ; } -hwc() { try-ulimit; hwc-soft "$@" ; } -hwc-soft() { j org.apache.hedwig.client.benchmark.HedwigBenchmark "$@" ; } - -ssh-zkc() { - local server="$1" port="$2" - ssh "$server" "hedwig/hw.bash zkc localhost:$port" -} - -# -# Setup -# - -# Get/build the ZK dependencies that must be manually installed and place those -# jars in the current directory. -get-zk() { - local stagedir="$(pwd)" dstdir="$(pwd)" ver=3.2.0 - local unpacked="$stagedir/zookeeper-$ver/" - local url="http://archive.apache.org/dist/hadoop/zookeeper/zookeeper-$ver/zookeeper-$ver.tar.gz" - - if [[ ! -d "$unpacked" ]] - then - echo $url - wget -q -O - "$url" | tar xzf - -C "$stagedir" - fi - ant -q -buildfile "$unpacked/build.xml" compile-test - cp -r "$unpacked/src/java/test/"{config,data}/ "$unpacked/build/testclasses/" - jar cf zookeeper-test-$ver.jar -C "$(readlink -f "$unpacked/build/testclasses/")" . - cp "$unpacked/zookeeper-$ver.jar" . - cp "$unpacked/contrib/bookkeeper/zookeeper-$ver-bookkeeper.jar" bookkeeper-$ver.jar - jar cf zookeeper-test-$ver-sources.jar -C "$(readlink -f "$unpacked/src/java/test/")" . - jar cf zookeeper-$ver-sources.jar -C "$(readlink -f "$unpacked/src/java/main/")" . - jar cf bookkeeper-$ver-sources.jar -C "$(readlink -f "$unpacked/src/contrib/bookkeeper/src/java/")" . -} - -get-bk() { - local svn="$serverdir/../Zookeeper/" svnver=3.3.0-SNAPSHOT - ant -q -buildfile "$svn/build.xml" compile-test - ant -q -buildfile "$svn/src/contrib/bookkeeper/build.xml" - jar cf bookkeeper-$svnver-sources.jar -C "$(readlink -f "$svn/src/contrib/bookkeeper/src/java/")" . - cp "$svn/build/contrib/bookkeeper/zookeeper-dev-bookkeeper.jar" bookkeeper-$svnver.jar -} - -# Install the jars from the current directory, as obtained by get-zk. -# For now, we will use the checked in ZK/BK jars in the server/lib directory. -# When an official ZK/BK release for those changes is done, then we can -# modify the get-zk function to get the latest code. -install-zk-bk() { - for pkg in zookeeper zookeeper-test bookkeeper ; do - local grp="${pkg%-*}" ver=SNAPSHOT - for srcs in '' -sources - do trace mvn -q install:install-file -Dfile="$pkg-$ver$srcs.jar" \ - -DgroupId=org.apache.$grp -DartifactId=$pkg -Dpackaging=jar \ - -Dversion=$ver ${srcs:+-Dclassifier=sources} - done - done -} - -setup-java() { - # wget 'http://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/VerifyItem-Start/jdk-7-ea-linux-i586.bin?BundledLineItemUUID=dXBIBe.m0.UAAAEiZUUKrYfz&OrderID=BnZIBe.mencAAAEiU0UKrYfz&ProductID=O29IBe.py.oAAAEhK1kP50GU&FileName=/jdk-7-ea-linux-i586.bin' - local jdk="$1" - parscp "$jdk" ^:/tmp/jdk6 - parssh " - echo yes | /tmp/jdk6 > /tmp/java-install-log && - if ! fgrep jdk1.6.0 ~/.bashrc > /dev/null - then echo 'export PATH=~/jdk1.6.0_14/bin/:\$PATH' >> ~/.bashrc - fi - " -} - -setup-yjp() { - local pkg="$1" - parscp "$pkg" ^:/tmp/yjp.zip - parssh " - yes A | unzip -q /tmp/yjp.zip - if ! fgrep YJP= ~/.bashrc > /dev/null - then echo 'export YJP=~/yjp-8.0.15/' >> ~/.bashrc - fi - " -} - -# Usage: setup-bk ZKSERVER ZKSERVER_PORT -# -# Create the /ledgers and /ledgers/available ZK nodes on the given ZK server. -# The bookie servers will register themselves once they are up on ZK but they -# need these nodes to exist first. -setup-bk() { - local server="$1" port="$2" - shift 2 - ssh-zkc "$server" "$port" << EOF || true -create /ledgers 0 -create /ledgers/available 0 -EOF -} - -# Get rid of duplicate files in a jar. -strip-jar() { - local jar="${1:-$jar}" tmpdir=/tmp/$USER/jar - rm -rf "$tmpdir" - mkdir -p "$tmpdir" - ( - cd "$tmpdir" - jar xf "$jar" - jar cf "$jar" . - ) -} - -# Inspect the current logging level. -get-logging() { - local jar="${1:-$jar}" tmpdir=/tmp/$USER/jar - mkdir -p "$tmpdir" - ( - cd "$tmpdir" - jar xf "$jar" log4j.properties - grep rootLogger= log4j.properties - ) -} - -# Adjust the log level but without modifying the original source tree or going -# through the full rebuild process. -set-logging() { - local level="$1" tmpdir=/tmp/$USER/jarlog - mkdir -p "$tmpdir" - ( - cd "$tmpdir" - jar xf "$jar" log4j.properties - sed -i "s/\(rootLogger\)=[[:alpha:]]\+/\1=$level/" log4j.properties - jar uf "$jar" log4j.properties - ) -} - -# -# General testbed tools. -# - -hosts() { - if [[ ! "$hosts" ]] - then echo '$hosts not set' 1>&2 ; return 1 - fi - echo $hosts | sed 's/[[:space:]]\+/\n/g' | sort -u -} - -hostargs() { "$@" $hosts ; } -tagssh() { - local prof="$1" - shift - { - ssh "$prof" "export use_yjp=$use_yjp loglevel=$loglevel asserts=$asserts logconf=$(quote "$logconf"); attach=$attach; $@" 2>&1 && - echo -e "$bright_green[SUCCESS]$normal_color" || - echo -e "$bright_red[FAILURE: $?]$normal_color" && false - } | $unbuffercmd sed "s/^/$prof: /g" -} -parssh() { hosts | xargs ${xargs--P0} -i^ "$script" tagssh ^ "$@" ; } -parscp() { hosts | xargs ${xargs--P0} -i^ scp "$@" ; } - -# Resolves profile names to actual hostnames according to the user's .ssh/config. -ssh-hosts() { - python -c " -from __future__ import with_statement -import sys, os -hosts = {} -with file(os.environ['HOME'] + '/.ssh/config') as f: - for line in f: - words = line.split('#')[0].split() - if len(words) == 2: - if words[0] == 'Host': key = words[1] - if words[0] == 'HostName': hosts[key] = words[1] -for profile in sys.argv[1:]: - parts = profile.split(':', 1) - key, rest = parts[0], ('' if len(parts) == 1 else ':' + parts[1]) - print hosts.get(key, key) + rest, -" "$@" -} - -# -# Hedwig testbed tools. -# - -# Usage: hosts='wilbur2 wilbur3 wilbur4' ./hw.bash push -push() { - if ! $already_pushed ; then - if $push_jar || $push_script - then parssh 'mkdir -p hedwig/' - fi - if $push_jar && $push_script - then parscp -q "$script" "$jar" ^:hedwig/ - elif $push_jar && ! $push_script - then parscp -q "$jar" ^:hedwig/ - elif ! $push_jar && $push_script - then parscp -q "$script" ^:hedwig/ - else : - fi - already_pushed=true - fi -} - -# Kill processes and garbage-collect the log4j temp directories. -dkill() { parssh 'pkill java; rm -rf /tmp/$USER/logging/' ; } - -# Pass in any argument to get a long listing. -lstatus() { - for port in 2181 3181 4080 {9874..9878} ; do - if /usr/sbin/lsof -i tcp:$port 2>&1 | fgrep 'LISTEN' > /dev/null ; then - if (( $# > 0 )) ; then - echo "$port:" - ps u $(/usr/sbin/lsof -t -i tcp:$port) | cat - else - echo -n "$port " - fi - fi - done - (( $# > 0 )) || echo -} - -# Pass in any argument to get a long listing. -dstatus() { - push - xargs= parssh "hedwig/hw.bash lstatus $@" -} - -# See if anything is running on each machine. -tops() { - xargs= parssh ' - echo - hostname - echo ===== - top -b -n 1 | fgrep -A3 COMMAND - ' -} - -# Familiarize this machine with the given hosts' keys. -warmup() { - hosts | xargs -P0 -i^ ssh -o StrictHostKeyChecking=no ^ hostname -} - -# Add yourself to nopasswd sudoers for all hosts in $hosts. -setup-sudo() { - local cmd='sudo su - -c " - if ! fgrep \"$USER ALL=(yahoo) NOPASSWD:ALL\" /etc/sudoers >& /dev/null - then echo -e \"$USER ALL=(ALL) ALL\n$USER ALL=(yahoo) NOPASSWD:ALL\" >> /etc/sudoers - fi"' - python -c ' -import getpass, os, pexpect, sys -pw = getpass.getpass() -for host in os.environ["hosts"].split(): - c = pexpect.spawn(sys.argv[1], [host] + sys.argv[2:]) - i = c.expect(["Password:", pexpect.EOF]) - if i == 0: c.sendline(pw); c.read() - filtered = filter(lambda x: pw not in x, c.before.split("\n")) - sys.stdout.write("\n".join(filtered).lstrip("\n")) - ' ssh "$cmd" -} - -setup-limits() { - ssh ' - if ! sudo fgrep "* hard nofile $((1024**2))" /etc/security/limits.conf >& /dev/null - then sudo su - -c "echo \"* hard nofile $((1024**2))\" >> /etc/security/limits.conf" - fi - ' -} - -mkhomes() { - ssh 'sudo mkdir -p /home/yang && sudo chown -R yang /home/yang' -} - -# -# Distributed launchers. -# - -# Given a hostname (*not* an ssh profile), figure out how to utilize the -# machine's disks for BK. -bk-journal() { - case "$@" in - * ) echo '"/d1/$USER/bk/journal"' ;; - esac -} -bk-ledger() { - case "$@" in - * ) echo '"/home/$USER/bk/ledger"' ;; - esac -} -bk-paths() { - echo "$(bk-journal "$@") $(bk-ledger "$@")" -} - -# Start ZK on the first arg host and BKs on the second argument which is -# a string list of hosts. -start-zk-bks() { - hosts="$*" push jar - local zk="$1" abks=( $2 ) - shift - tagssh $zk " - rm -rf /tmp/$USER/zk/ - mkdir -p /tmp/$USER/zk/ - cat > /tmp/$USER/zoo.cfg << EOF -tickTime=2000 -dataDir=/tmp/$USER/zk/ -clientPort=$ZOOKEEPER_PORT -EOF - hedwig/hw.bash eval 'set -x; zk /tmp/$USER/zoo.cfg' - " & - wait-connect $zk:$ZOOKEEPER_PORT - setup-bk $(ssh-hosts $zk) $ZOOKEEPER_PORT - hosts="$*" parssh " - rm -rf $(bk-paths "$1") - mkdir -p $(bk-paths "$1") - hedwig/hw.bash eval $(quote "set -x; bk $BOOKKEEPER_PORT $( ssh-hosts $zk ):$ZOOKEEPER_PORT $(bk-paths "$1")") - " & - for bk in "${abks[@]}" ; do - wait-connect $bk:$BOOKKEEPER_PORT - done -} - -# The first argument is a string list of remote region default Hedwig servers -# in a multi-region setup (if any). The second argument is a string list of -# Hedwig hubs to start for this local region. The third argument is the single -# ZooKeeper server host the hubs should connect to. -start-hw() { - local allhws="$1" ahws=( $2 ) zk="$3" - if [[ $region ]] - then regionconf="region=$region" - fi - shift - hosts="$@" push - for hw in "${ahws[@]}" ; do - tagssh $hw " - mkdir -p /tmp/$USER/ - cat > /tmp/$USER/hw.conf << EOF -zk_host=$( ssh-hosts $zk ):$ZOOKEEPER_PORT -regions=$( ssh-hosts $allhws ) -server_port=$SERVER_PORT -ssl_server_port=$SSL_SERVER_PORT -ssl_enabled=true -inter_region_ssl_enabled=true -cert_name=/server.p12 -password=eUySvp2phM2Wk -$regionconf -$extraconf -EOF - hedwig/hw.bash eval 'set -x; hw /tmp/$USER/hw.conf' - " & - wait-connect $hw:$SERVER_PORT - wait-connect $hw:$SSL_SERVER_PORT - done -} - -# The arguments are similar to those for start-hw() above. -# The additional 4th argument is a string list of BookKeeper servers to start up. -start-region() { - local allhws="$1" hws="$2" zk="$3" bks="$4" - shift - hosts="$*" push jar - start-zk-bks "$zk" "$bks" - start-hw "$allhws" "$hws" "$zk" -} - -# Start multiple regions from a file configuration. The format that is expected -# is to have each region on a separate line with the following format: -# region=, hub=, default=, zk=, bk= -# This will create all of the regions with an all-to-all topology. Each region -# is connected to the default hub server of every other region. -start-regions() { - local cfg="$1" - local regionPref="region=" hubPref=", hub=" defaultPref=", default=" zkPref=", zk=" bkPref=", bk=" - while read line ; do - local otherhws= - while read subline ; do - local profile="$(substr "$subline" "$defaultPref" "$zkPref")" - if [[ $profile != "$(substr "$line" "$defaultPref" "$zkPref")" ]] - then otherhws="$otherhws $profile:$SERVER_PORT:$SSL_SERVER_PORT" - fi - done < "$cfg" - local curRegion="$(substr "$line" "$regionPref" "$hubPref")" - local curHub="$(substr "$line" "$hubPref" "$defaultPref")" - local curZk="$(substr "$line" "$zkPref" "$bkPref")" - local curBk="$(substr "$line" "$bkPref" "")" - hosts="$curHub $curZk $curBk" push jar - region="$curRegion" start-region "$otherhws" "$curHub" "$curZk" "$curBk" & - done < "$cfg" - wait -} - -app() { - local hw="$1" # the server to connect to - push - parssh " - set -o errexit -x - mkdir -p /tmp/\$USER/ - echo $(quote "default_server_host=$(ssh-hosts "$hw"):$SERVER_PORT:$SSL_SERVER_PORT") > /tmp/\$USER/hwc.conf - JAVAFLAGS=$(quote "$JAVAFLAGS") hedwig/hw.bash hwc /tmp/\$USER/hwc.conf - " -} - -# -# Experiments. -# - -sub-exp() { - : ${start:=0} - push - for sync in ${syncs:-true false} ; do - for count in ${counts:-1000} ; do - for npar in ${npars:-1 10 20 30 40 50} ; do - if (( $npar <= $count )) ; then - for rep in {1..3} ; do - echo JAVAFLAGS="-Dmode=sub -Dsynchronous=$sync -Dcount=$count -Dnpar=$npar -Dstart=$start $JAVAFLAGS" app "$@" - JAVAFLAGS="-Dmode=sub -Dcount=$count -Dnpar=$npar -Dstart=$start $JAVAFLAGS" app "$@" >& ${outfile:-sub/sync-$sync-count-$count-npar-$npar-rep-$rep.out} - let start+=$count - done - fi - done - done - done -} - -wait-sub() { - local zk="$1" topic="$2" subid="$3" region="$4" - while ! echo "ls /hedwig/$region/topics/$topic/subscribers/$subid" | \ - ssh-zkc "$zk" $ZOOKEEPER_PORT 2>&1 | \ - tee "${dbgfile:-/dev/null}" | \ - grep '^\[\]$' > /dev/null - do sleep 1 - done -} - -# Note: this code waits for subscribers to show up in ZK, so when running this -# multiple times on the same servers, adjust `start` to use a different topic -# each time; otherwise, you'll immediately see the subscribers from last time, -# thus causing the script to not wait for the current session's subscribers. -# Alternatively, adjust recvid. -# -# Params: -# -# recvs: the list of local receivers -# pubs: the list of publishers -# hw: the local hedwig node -# zk: the local zookeeper node (used to wait for receivers to join) -# -# Optional group of params: -# -# rrecv: the remote receiver -# rhw: the remote hedwig node -# rzk: the remote zookeeper node -pub-exp() { - (( $# >= 4 )) - local recvs="$1" pubs="$2" hw="$3" zk="$4" - # Optional remote args. - if (( $# > 4 )) - then local remote=true rrecv="$5" rhw="$6" rzk="$7" - else local remote=false - fi - : ${start:=0} ${count:=100000} ${recvid:=0} ${dir="pub"} - hosts="$recvs $pubs $rrecv" push - # Convert to arrays. - local arecvs=( $recvs ) apubs=( $pubs ) - mkdir -p "$dir" - - #rregion="$(ssh $rhw cat /tmp/$USER/hw.conf)" - region=$hw rregion=$rhw - - # Default to only using all recvs (rather than iterating over subsets). - for nrecvs in ${nrecvss:-${#arecvs[*]}} ; do - # Ditto for publishers. - for npubs in ${npubss:-${#apubs[*]}} ; do - # Default to only using a single value of npar. - for npar in ${npars:-100} ; do - # Default to repeating each trial thrice. - for rep in $(seq ${nreps:-3}) ; do - - echo -n "nrecvs=$nrecvs npubs=$npubs npar=$npar rep=$rep" - - local outbase="$dir/nrecvs-$nrecvs-npubs-$npubs-npar-$npar-rep-$rep" - - # Skip if already done. - if [[ -f "$outbase"* ]] - then echo '...skipped' ; continue - else echo - fi - - if $remote ; then - # Start remote receiver. - hosts=$rrecv JAVAFLAGS="-Dmode=recv -DsubId=recv-$recvid -Dstart=$start -Dcount=$((count/npubs*npubs)) $JAVAFLAGS" app "$rhw" >& "${outfile:-$outbase-rrecv.out}" & - fi - - # Start all receivers. - for ((irecv = 0; irecv < nrecvs; irecv++)) ; do - hosts="${arecvs[$irecv]}" JAVAFLAGS="-Dmode=recv -DsubId=recv-$((recvid+irecv)) -Dstart=$start -Dcount=$((count/npubs*npubs)) $JAVAFLAGS" app "$hw" >& "${outfile:-$outbase-recv-$irecv.out}" & - done - - # Wait till subscribed. - sleep 1 - for ((irecv = 0; irecv < nrecvs; irecv++)) - do wait-sub $zk topic-$start recv-$((recvid+irecv)) $region - done - if $remote ; then - # Wait till remote subscribed. - wait-sub $rzk topic-$start recv-$recvid $rregion - # Wait till cross-region subscribed, since default is async subs. - # This should only happen once. - wait-sub $zk topic-$start hub-$rregion $region - fi - - # Launch all publishers. - for ((ipub = 0; ipub < npubs; ipub++)) ; do - hosts="${apubs[$ipub]}" JAVAFLAGS="-Dmode=pub -Dnpar=$npar -Dstart=$start -Dcount=$((count/npubs)) $JAVAFLAGS" app "$hw" >& "${outfile:-$outbase-pub-$ipub.out}" & - done - - # Wait for everyone to terminate. - wait - - # To avoid reusing the same subscriber ID. - let recvid+=$nrecvs - - done - done - done - done -} - -pub-exps() { - local pool="$1" hw="$2" zk="$3" rrecv="$4" rhw="$5" rzk="$6" - local apool=( $pool ) npool=${#apool[*]} - echo $apool $npool - : ${start:=0} - hosts="$pool $rrecv $rhw" push - - quote start=$start npars="${npars:-20 40 60 80 100}" pub-exp ${apool[0]} ${apool[1]} "$hw" "$zk" $rrecv $rhw $rzk - start=$start npars="${npars:-20 40 60 80 100}" pub-exp ${apool[0]} ${apool[1]} "$hw" "$zk" $rrecv $rhw $rzk - - pubs=${apool[0]} - for ((i = 1; i < npool; i++)) - do recvs="${recvs:-} ${apool[$i]}" - done - quote start=$((start+1)) nrecvss="$( seq -s' ' $(( npool - 1 )) )" pub-exp "$recvs" "$pubs" "$hw" "$zk" $rrecv $rhw $rzk - start=$((start+1)) nrecvss="$( seq -s' ' $(( npool - 1 )) )" pub-exp "$recvs" "$pubs" "$hw" "$zk" $rrecv $rhw $rzk - - recvs=${apool[0]} - for ((i = 1; i < npool; i++)) - do pubs="${pubs:-} ${apool[$i]}" - done - quote start=$((start+2)) npubss="$( seq -s' ' $(( npool - 1 )) )" pub-exp "$recvs" "$pubs" "$hw" "$zk" $rrecv $rhw $rzk - start=$((start+2)) npubss="$( seq -s' ' $(( npool - 1 )) )" pub-exp "$recvs" "$pubs" "$hw" "$zk" $rrecv $rhw $rzk -} - -# -# Post-processing -# - -# Consolidate to a directory. -sub-agg() { - local dst="$1" - mkdir -p "$dst" - for s in 0 1 ; do - for i in sync$s/*.out - do cp "$i" "$dst/sync-$s-$(basename "$i")" - done - done -} - -if [[ "$(type -t "$cmd")" == function ]] -then "$cmd" "$@" -else "$@" -fi - -# vim: et sw=2 ts=2 diff --git a/src/contrib/hedwig/scripts/hwServer.sh b/src/contrib/hedwig/scripts/hwServer.sh deleted file mode 100755 index face753a907..00000000000 --- a/src/contrib/hedwig/scripts/hwServer.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# If this scripted is run out of /usr/bin or some other system bin directory -# it should be linked to and not copied. Things like java jar files are found -# relative to the canonical path of this script. -# - -# Only follow symlinks if readlink supports it. Find the directory path -# for where this script is being executed from. -if readlink -f "$0" > /dev/null 2>&1 -then - HWBIN=`readlink -f "$0"` -else - HWBIN="$0" -fi -HWBINDIR=`dirname "$HWBIN"` - -# We use HWCFGDIR if defined, otherwise we use /etc/hedwig -# or the conf directory that is a sibling of this script's directory. -# This is to find out where the Hedwig server config file resides. -if [ "x$HWCFGDIR" = "x" ] -then - if [ -d "/etc/hedwig" ] - then - HWCFGDIR="/etc/hedwig" - else - HWCFGDIR="$HWBINDIR/../conf" - fi -fi - -# We use HWCFG as the name of the Hedwig server config file if defined, -# otherwise use the default file name "hw_server.conf". -if [ "x$HWCFG" = "x" ] -then - HWCFG="hw_server.conf" -fi -HWCFG="$HWCFGDIR/$HWCFG" - -# If a config file is passed in directly when invoking the script, -# use that instead. -if [ "x$2" != "x" ] -then - HWCFG="$HWCFGDIR/$2" -fi - -# Find the Hedwig server jar and setup the CLASSPATH. We assume it to be -# located in a standard place relative to the location of where this script -# is executed from. -HWJAR="$HWBINDIR/../server/target/server-1.0-SNAPSHOT-jar-with-dependencies.jar" -CLASSPATH="$HWJAR:$CLASSPATH" - -# Store the Hedwig server's PID (java process) in the same $HWBINDIR. -# This is used for us to stop the server later on via this same script. -if [ -z $HWPIDFILE ] - then HWPIDFILE=$HWBINDIR/hedwig_server.pid -fi - -case $1 in -start) - echo "Starting Hedwig server ... " - java -cp "$CLASSPATH" org.apache.hedwig.server.netty.PubSubServer "$HWCFG" & - /bin/echo -n $! > "$HWPIDFILE" - echo STARTED - ;; -stop) - echo "Stopping Hedwig server ... " - if [ ! -f "$HWPIDFILE" ] - then - echo "error: could not find file $HWPIDFILE" - exit 1 - else - kill -9 $(cat "$HWPIDFILE") - rm "$HWPIDFILE" - echo STOPPED - fi - ;; -restart) - shift - "$0" stop ${@} - sleep 3 - "$0" start ${@} - ;; -*) - echo "Usage: $0 {start|stop|restart}" >&2 -esac diff --git a/src/contrib/hedwig/scripts/quote b/src/contrib/hedwig/scripts/quote deleted file mode 100755 index 6fe3e176552..00000000000 --- a/src/contrib/hedwig/scripts/quote +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python -# vim:et:sw=2:ts=2 -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys - -def quote(s): - return r'"%s"' % s.replace('\\', r'\\').replace('"', r'\"').replace('$', r'\$') - -print ' '.join( map( quote, sys.argv[1:] ) ) diff --git a/src/contrib/hedwig/server/lib/README b/src/contrib/hedwig/server/lib/README deleted file mode 100644 index e9930a260f6..00000000000 --- a/src/contrib/hedwig/server/lib/README +++ /dev/null @@ -1,4 +0,0 @@ -The Zookeeper and BookKeeper jars included in this server/lib directory were -created off the zookeeper Apache trunk at revision 947756. They have new -features that haven't been put into a release version yet so just putting it -here temporarily. diff --git a/src/contrib/hedwig/server/pom.xml b/src/contrib/hedwig/server/pom.xml deleted file mode 100644 index 8cb0873e966..00000000000 --- a/src/contrib/hedwig/server/pom.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - - 4.0.0 - - org.apache.hedwig - hedwig - 1.0-SNAPSHOT - - - org.apache.hedwig.server.netty.PubSubServer - - org.apache.hedwig - server - jar - 1.0-SNAPSHOT - server - http://maven.apache.org - - - junit - junit - 4.8.1 - compile - - - org.apache.hedwig - client - 1.0-SNAPSHOT - compile - - - org.apache.derby - derby - 10.4.2.0 - runtime - - - org.apache.zookeeper - zookeeper - 3.4.0 - compile - - - org.apache.zookeeper - zookeeper-test - 3.4.0 - compile - - - org.apache.bookkeeper - bookkeeper - 3.4.0 - compile - - - - - - maven-assembly-plugin - - - jar-with-dependencies - - - - ${mainclass} - - - - - - make-assembly - package - - attached - - - - - - - maven-antrun-plugin - - - removebuilddir - clean - - - - - - - run - - - - createbuilddir - generate-test-resources - - - - - - - run - - - - - - - diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/AbstractBenchmark.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/AbstractBenchmark.java deleted file mode 100644 index c73ec2ff0b1..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/AbstractBenchmark.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.benchmark; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.log4j.Logger; - -import org.apache.hedwig.util.ConcurrencyUtils; - -public abstract class AbstractBenchmark { - - static final Logger logger = Logger.getLogger(AbstractBenchmark.class); - - AtomicLong totalLatency = new AtomicLong(); - LinkedBlockingQueue doneSignalQueue = new LinkedBlockingQueue(); - - abstract void doOps(int numOps) throws Exception; - abstract void tearDown() throws Exception; - - protected class AbstractCallback{ - AtomicInteger numDone = new AtomicInteger(0); - Semaphore outstanding; - int numOps; - boolean logging; - - public AbstractCallback(Semaphore outstanding, int numOps) { - this.outstanding = outstanding; - this.numOps = numOps; - logging = Boolean.getBoolean("progress"); - } - - public void handle(boolean success, Object ctx){ - outstanding.release(); - - if (!success){ - ConcurrencyUtils.put(doneSignalQueue, false); - return; - } - - totalLatency.addAndGet(System.currentTimeMillis() - (Long)ctx); - int numDoneInt = numDone.incrementAndGet(); - - if (logging && numDoneInt % 10000 == 0){ - logger.info("Finished " + numDoneInt + " ops"); - } - - if (numOps == numDoneInt){ - ConcurrencyUtils.put(doneSignalQueue, true); - } - } - } - - public void runPhase(String phase, int numOps) throws Exception{ - long startTime = System.currentTimeMillis(); - - doOps(numOps); - - if (!doneSignalQueue.take()){ - logger.error("One or more operations failed in phase: " + phase); - throw new RuntimeException(); - }else{ - logger.info("Phase: " + phase + " Avg latency : " + totalLatency.get() / numOps + ", tput = " + (numOps * 1000/ (System.currentTimeMillis() - startTime))); - } - } - - - - - - public void run() throws Exception{ - - int numWarmup = Integer.getInteger("nWarmup", 50000); - runPhase("warmup", numWarmup); - - logger.info("Sleeping for 10 seconds"); - Thread.sleep(10000); - //reset latency - totalLatency.set(0); - - int numOps = Integer.getInteger("nOps", 400000); - runPhase("real", numOps); - - tearDown(); - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/BookieBenchmark.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/BookieBenchmark.java deleted file mode 100644 index b9a94af0787..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/benchmark/BookieBenchmark.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.benchmark; - -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.util.concurrent.Executors; -import java.util.concurrent.Semaphore; -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.proto.BookieClient; -import org.apache.log4j.Logger; -import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback; -import org.apache.bookkeeper.util.OrderedSafeExecutor; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; - -public class BookieBenchmark extends AbstractBenchmark{ - - static final Logger logger = Logger.getLogger(BookkeeperBenchmark.class); - - BookieClient bkc; - InetSocketAddress addr; - ClientSocketChannelFactory channelFactory; - OrderedSafeExecutor executor = new OrderedSafeExecutor(1); - - - public BookieBenchmark(String bookieHostPort) throws Exception{ - channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); - bkc = new BookieClient(channelFactory, executor); - String[] hostPort = bookieHostPort.split(":"); - addr = new InetSocketAddress(hostPort[0], Integer.parseInt(hostPort[1])); - - } - - - @Override - void doOps(final int numOps) throws Exception{ - int numOutstanding = Integer.getInteger("nPars",1000); - final Semaphore outstanding = new Semaphore(numOutstanding); - - - WriteCallback callback = new WriteCallback() { - AbstractCallback handler = new AbstractCallback(outstanding, numOps); - - @Override - public void writeComplete(int rc, long ledgerId, long entryId, - InetSocketAddress addr, Object ctx) { - handler.handle(rc == BKException.Code.OK, ctx); - } - }; - - byte[] passwd = new byte[20]; - int size = Integer.getInteger("size", 1024); - byte[] data = new byte[size]; - - for (int i=0; i map = new ConcurrentHashMap(); - - public static ByteString intern(ByteString in) { - ByteString presentValueInMap = map.get(in); - if (presentValueInMap != null) { - return presentValueInMap; - } - - presentValueInMap = map.putIfAbsent(in, in); - if (presentValueInMap != null) { - return presentValueInMap; - } - - return in; - - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/ServerConfiguration.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/ServerConfiguration.java deleted file mode 100644 index dbcc3745f8b..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/ServerConfiguration.java +++ /dev/null @@ -1,268 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.common; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.net.InetAddress; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - -import org.apache.commons.configuration.ConfigurationException; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.conf.AbstractConfiguration; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class ServerConfiguration extends AbstractConfiguration { - protected final static String REGION = "region"; - protected final static String MAX_MESSAGE_SIZE = "max_message_size"; - protected final static String READAHEAD_COUNT = "readahead_count"; - protected final static String READAHEAD_SIZE = "readahead_size"; - protected final static String CACHE_SIZE = "cache_size"; - protected final static String SCAN_BACKOFF_MSEC = "scan_backoff_ms"; - protected final static String SERVER_PORT = "server_port"; - protected final static String SSL_SERVER_PORT = "ssl_server_port"; - protected final static String ZK_PREFIX = "zk_prefix"; - protected final static String ZK_HOST = "zk_host"; - protected final static String ZK_TIMEOUT = "zk_timeout"; - protected final static String READAHEAD_ENABLED = "readhead_enabled"; - protected final static String STANDALONE = "standalone"; - protected final static String REGIONS = "regions"; - protected final static String CERT_NAME = "cert_name"; - protected final static String CERT_PATH = "cert_path"; - protected final static String PASSWORD = "password"; - protected final static String SSL_ENABLED = "ssl_enabled"; - protected final static String CONSUME_INTERVAL = "consume_interval"; - protected final static String RETENTION_SECS = "retention_secs"; - protected final static String INTER_REGION_SSL_ENABLED = "inter_region_ssl_enabled"; - protected final static String MESSAGES_CONSUMED_THREAD_RUN_INTERVAL = "messages_consumed_thread_run_interval"; - - // these are the derived attributes - protected ByteString myRegionByteString = null; - protected HedwigSocketAddress myServerAddress = null; - protected List regionList = null; - - // Although this method is not currently used, currently maintaining it like - // this so that we can support on-the-fly changes in configuration - protected void refreshDerivedAttributes() { - refreshMyRegionByteString(); - refreshMyServerAddress(); - refreshRegionList(); - } - - @Override - public void loadConf(URL confURL) throws ConfigurationException { - super.loadConf(confURL); - refreshDerivedAttributes(); - } - - public int getMaximumMessageSize() { - return conf.getInt(MAX_MESSAGE_SIZE, 1258291); /* 1.2M */ - } - - public String getMyRegion() { - return conf.getString(REGION, "standalone"); - } - - protected void refreshMyRegionByteString() { - myRegionByteString = ByteString.copyFromUtf8(getMyRegion()); - } - - protected void refreshMyServerAddress() { - try { - // Use the raw IP address as the hostname - myServerAddress = new HedwigSocketAddress(InetAddress.getLocalHost().getHostAddress(), getServerPort(), - getSSLServerPort()); - } catch (UnknownHostException e) { - throw new RuntimeException(e); - } - } - - // The expected format for the regions parameter is Hostname:Port:SSLPort - // with spaces in between each of the regions. - protected void refreshRegionList() { - String regions = conf.getString(REGIONS, ""); - if (regions.isEmpty()) { - regionList = new LinkedList(); - } else { - regionList = Arrays.asList(regions.split(" ")); - } - } - - public ByteString getMyRegionByteString() { - if (myRegionByteString == null) { - refreshMyRegionByteString(); - } - return myRegionByteString; - } - - public int getReadAheadCount() { - return conf.getInt(READAHEAD_COUNT, 10); - } - - public long getReadAheadSizeBytes() { - return conf.getLong(READAHEAD_SIZE, 4 * 1024 * 1024); // 4M - } - - public long getMaximumCacheSize() { - // 2G or half of the maximum amount of memory the JVM uses - return conf.getLong(CACHE_SIZE, Math.min(2 * 1024L * 1024L * 1024L, Runtime.getRuntime().maxMemory() / 2)); - } - - // After a scan of a log fails, how long before we retry (in msec) - public long getScanBackoffPeriodMs() { - return conf.getLong(SCAN_BACKOFF_MSEC, 1000); - } - - public int getServerPort() { - return conf.getInt(SERVER_PORT, 4080); - } - - public int getSSLServerPort() { - return conf.getInt(SSL_SERVER_PORT, 9876); - } - - public String getZkPrefix() { - return conf.getString(ZK_PREFIX, "/hedwig"); - } - - public StringBuilder getZkRegionPrefix(StringBuilder sb) { - return sb.append(getZkPrefix()).append("/").append(getMyRegion()); - } - - public StringBuilder getZkTopicsPrefix(StringBuilder sb) { - return getZkRegionPrefix(sb).append("/topics"); - } - - public StringBuilder getZkTopicPath(StringBuilder sb, ByteString topic) { - return getZkTopicsPrefix(sb).append("/").append(topic.toStringUtf8()); - } - - public StringBuilder getZkHostsPrefix(StringBuilder sb) { - return getZkRegionPrefix(sb).append("/hosts"); - } - - public HedwigSocketAddress getServerAddr() { - if (myServerAddress == null) { - refreshMyServerAddress(); - } - return myServerAddress; - } - - public String getZkHost() { - return conf.getString(ZK_HOST, "localhost"); - } - - public int getZkTimeout() { - return conf.getInt(ZK_TIMEOUT, 2000); - } - - public boolean getReadAheadEnabled() { - return conf.getBoolean(READAHEAD_ENABLED, true); - } - - public boolean isStandalone() { - return conf.getBoolean(STANDALONE, false); - } - - public List getRegions() { - if (regionList == null) { - refreshRegionList(); - } - return regionList; - } - - // This is the name of the SSL certificate if available as a resource. - public String getCertName() { - return conf.getString(CERT_NAME, ""); - } - - // This is the path to the SSL certificate if it is available as a file. - public String getCertPath() { - return conf.getString(CERT_PATH, ""); - } - - // This method return the SSL certificate as an InputStream based on if it - // is configured to be available as a resource or as a file. If nothing is - // configured correctly, then a ConfigurationException will be thrown as - // we do not know how to obtain the SSL certificate stream. - public InputStream getCertStream() throws FileNotFoundException, ConfigurationException { - String certName = getCertName(); - String certPath = getCertPath(); - if (certName != null && !certName.isEmpty()) { - return getClass().getResourceAsStream(certName); - } else if (certPath != null && !certPath.isEmpty()) { - return new FileInputStream(certPath); - } else - throw new ConfigurationException("SSL Certificate configuration does not have resource name or path set!"); - } - - public String getPassword() { - return conf.getString(PASSWORD, ""); - } - - public boolean isSSLEnabled() { - return conf.getBoolean(SSL_ENABLED, false); - } - - public int getConsumeInterval() { - return conf.getInt(CONSUME_INTERVAL, 50); - } - - public int getRetentionSecs() { - return conf.getInt(RETENTION_SECS, 0); - } - - public boolean isInterRegionSSLEnabled() { - return conf.getBoolean(INTER_REGION_SSL_ENABLED, false); - } - - // This parameter is used to determine how often we run the - // SubscriptionManager's Messages Consumed timer task thread (in - // milliseconds). - public int getMessagesConsumedThreadRunInterval() { - return conf.getInt(MESSAGES_CONSUMED_THREAD_RUN_INTERVAL, 60000); - } - - /* - * Is this a valid configuration that we can run with? This code might grow - * over time. - */ - public void validate() throws ConfigurationException { - if (!getZkPrefix().startsWith("/")) { - throw new ConfigurationException(ZK_PREFIX + " must start with a /"); - } - // Validate that if Regions exist and inter-region communication is SSL - // enabled, that the Regions correspond to valid HedwigSocketAddresses, - // namely that SSL ports are present. - if (isInterRegionSSLEnabled() && getRegions().size() > 0) { - for (String hubString : getRegions()) { - HedwigSocketAddress hub = new HedwigSocketAddress(hubString); - if (hub.getSSLSocketAddress() == null) - throw new ConfigurationException("Region defined does not have required SSL port: " + hubString); - } - } - - // add other checks here - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TerminateJVMExceptionHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TerminateJVMExceptionHandler.java deleted file mode 100644 index 419b9b04d4e..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TerminateJVMExceptionHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.common; - -import org.apache.log4j.Logger; - -public class TerminateJVMExceptionHandler implements Thread.UncaughtExceptionHandler { - static Logger logger = Logger.getLogger(TerminateJVMExceptionHandler.class); - - @Override - public void uncaughtException(Thread t, Throwable e) { - logger.fatal("Uncaught exception in thread " + t.getName(), e); - System.exit(1); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TopicOpQueuer.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TopicOpQueuer.java deleted file mode 100644 index 3c4a56286f7..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/TopicOpQueuer.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.common; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Queue; -import java.util.concurrent.ScheduledExecutorService; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.util.Callback; - -public class TopicOpQueuer { - /** - * Map from topic to the queue of operations for that topic. - */ - protected HashMap> topic2ops = new HashMap>(); - - protected final ScheduledExecutorService scheduler; - - public TopicOpQueuer(ScheduledExecutorService scheduler) { - this.scheduler = scheduler; - } - - public interface Op extends Runnable { - } - - public abstract class AsynchronousOp implements Op { - final public ByteString topic; - final public Callback cb; - final public Object ctx; - - public AsynchronousOp(final ByteString topic, final Callback cb, Object ctx) { - this.topic = topic; - this.cb = new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - cb.operationFailed(ctx, exception); - popAndRunNext(topic); - } - - @Override - public void operationFinished(Object ctx, T resultOfOperation) { - cb.operationFinished(ctx, resultOfOperation); - popAndRunNext(topic); - } - }; - this.ctx = ctx; - } - } - - public abstract class SynchronousOp implements Op { - final public ByteString topic; - - public SynchronousOp(ByteString topic) { - this.topic = topic; - } - - @Override - public final void run() { - runInternal(); - popAndRunNext(topic); - } - - protected abstract void runInternal(); - - } - - protected synchronized void popAndRunNext(ByteString topic) { - Queue ops = topic2ops.get(topic); - if (!ops.isEmpty()) - ops.remove(); - if (!ops.isEmpty()) - scheduler.submit(ops.peek()); - } - - public void pushAndMaybeRun(ByteString topic, Op op) { - int size; - synchronized (this) { - Queue ops = topic2ops.get(topic); - if (ops == null) { - ops = new LinkedList(); - topic2ops.put(topic, ops); - } - ops.add(op); - size = ops.size(); - } - if (size == 1) - op.run(); - } - - public Runnable peek(ByteString topic) { - return topic2ops.get(topic).peek(); - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/UnexpectedError.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/UnexpectedError.java deleted file mode 100644 index b7a4404e4b8..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/common/UnexpectedError.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.common; - -public class UnexpectedError extends Error { - - /** - * - */ - private static final long serialVersionUID = 1L; - - public UnexpectedError(String msg) { - super(msg); - } - - public UnexpectedError(Throwable cause) { - super(cause); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/ChannelEndPoint.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/ChannelEndPoint.java deleted file mode 100644 index b5e9922737b..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/ChannelEndPoint.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.delivery; - -import java.util.HashMap; -import java.util.Map; - -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; - -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.server.common.UnexpectedError; - -public class ChannelEndPoint implements DeliveryEndPoint, ChannelFutureListener { - - Channel channel; - - public Channel getChannel() { - return channel; - } - - Map callbacks = new HashMap(); - - public ChannelEndPoint(Channel channel) { - this.channel = channel; - } - - public void close() { - channel.close(); - } - - public void send(PubSubResponse response, DeliveryCallback callback) { - ChannelFuture future = channel.write(response); - callbacks.put(future, callback); - future.addListener(this); - } - - public void operationComplete(ChannelFuture future) throws Exception { - DeliveryCallback callback = callbacks.get(future); - callbacks.remove(future); - - if (callback == null) { - throw new UnexpectedError("Could not locate callback for channel future"); - } - - if (future.isSuccess()) { - callback.sendingFinished(); - } else { - // treat all channel errors as permanent - callback.permanentErrorOnSend(); - } - - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof ChannelEndPoint) { - ChannelEndPoint channelEndPoint = (ChannelEndPoint) obj; - return channel.equals(channelEndPoint.channel); - } else { - return false; - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryCallback.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryCallback.java deleted file mode 100644 index 9ee63f154f9..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryCallback.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.delivery; - -public interface DeliveryCallback { - - public void sendingFinished(); - - public void transientErrorOnSend(); - - public void permanentErrorOnSend(); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryEndPoint.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryEndPoint.java deleted file mode 100644 index 07748012219..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryEndPoint.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.delivery; - -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; - -public interface DeliveryEndPoint { - - public void send(PubSubResponse response, DeliveryCallback callback); - - public void close(); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryManager.java deleted file mode 100644 index 805d71be830..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/DeliveryManager.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.delivery; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.server.subscriptions.MessageFilter; - -public interface DeliveryManager { - public void startServingSubscription(ByteString topic, ByteString subscriberId, MessageSeqId seqIdToStartFrom, - DeliveryEndPoint endPoint, MessageFilter filter, boolean isHubSubscriber); - - public void stopServingSubscriber(ByteString topic, ByteString subscriberId); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/FIFODeliveryManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/FIFODeliveryManager.java deleted file mode 100644 index 8eede899a91..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/delivery/FIFODeliveryManager.java +++ /dev/null @@ -1,561 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.delivery; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.apache.log4j.Logger; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.UnexpectedError; -import org.apache.hedwig.server.persistence.Factory; -import org.apache.hedwig.server.persistence.MapMethods; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.persistence.ScanCallback; -import org.apache.hedwig.server.persistence.ScanRequest; -import org.apache.hedwig.server.subscriptions.MessageFilter; - -public class FIFODeliveryManager implements Runnable, DeliveryManager { - - protected static final Logger logger = Logger.getLogger(FIFODeliveryManager.class); - - protected interface DeliveryManagerRequest { - public void performRequest(); - } - - /** - * the main queue that the single-threaded delivery manager works off of - */ - BlockingQueue requestQueue = new LinkedBlockingQueue(); - - /** - * The queue of all subscriptions that are facing a transient error either - * in scanning from the persistence manager, or in sending to the consumer - */ - Queue retryQueue = new ConcurrentLinkedQueue(); - - /** - * Stores a mapping from topic to the delivery pointers on the topic. The - * delivery pointers are stored in a sorted map from seq-id to the set of - * subscribers at that seq-id - */ - Map>> perTopicDeliveryPtrs; - - /** - * Mapping from delivery end point to the subscriber state that we are - * serving at that end point. This prevents us e.g., from serving two - * subscriptions to the same endpoint - */ - Map subscriberStates; - - private PersistenceManager persistenceMgr; - - private ServerConfiguration cfg; - - // Boolean indicating if this thread should continue running. This is used - // when we want to stop the thread during a PubSubServer shutdown. - protected boolean keepRunning = true; - - public FIFODeliveryManager(PersistenceManager persistenceMgr, ServerConfiguration cfg) { - this.persistenceMgr = persistenceMgr; - perTopicDeliveryPtrs = new HashMap>>(); - subscriberStates = new HashMap(); - new Thread(this, "DeliveryManagerThread").start(); - this.cfg = cfg; - } - - /** - * ===================================================================== Our - * usual enqueue function, stop if error because of unbounded queue, should - * never happen - * - */ - protected void enqueueWithoutFailure(DeliveryManagerRequest request) { - if (!requestQueue.offer(request)) { - throw new UnexpectedError("Could not enqueue object: " + request + " to delivery manager request queue."); - } - } - - /** - * ==================================================================== - * Public interface of the delivery manager - */ - - /** - * Tells the delivery manager to start sending out messages for a particular - * subscription - * - * @param topic - * @param subscriberId - * @param seqIdToStartFrom - * Message sequence-id from where delivery should be started - * @param endPoint - * The delivery end point to which send messages to - * @param filter - * Only messages passing this filter should be sent to this - * subscriber - * @param isHubSubscriber - * There are some seq-id intricacies. To a hub subscriber, we - * should send only a subset of the seq-id vector - */ - public void startServingSubscription(ByteString topic, ByteString subscriberId, MessageSeqId seqIdToStartFrom, - DeliveryEndPoint endPoint, MessageFilter filter, boolean isHubSubscriber) { - - ActiveSubscriberState subscriber = new ActiveSubscriberState(topic, subscriberId, seqIdToStartFrom - .getLocalComponent() - 1, endPoint, filter, isHubSubscriber); - - enqueueWithoutFailure(subscriber); - } - - public void stopServingSubscriber(ByteString topic, ByteString subscriberId) { - ActiveSubscriberState subState = subscriberStates.get(new TopicSubscriber(topic, subscriberId)); - - if (subState != null) { - stopServingSubscriber(subState); - } - } - - /** - * Due to some error or disconnection or unsusbcribe, someone asks us to - * stop serving a particular endpoint - * - * @param endPoint - */ - protected void stopServingSubscriber(ActiveSubscriberState subscriber) { - enqueueWithoutFailure(new StopServingSubscriber(subscriber)); - } - - /** - * Instructs the delivery manager to backoff on the given subscriber and - * retry sending after some time - * - * @param subscriber - */ - - public void retryErroredSubscriberAfterDelay(ActiveSubscriberState subscriber) { - - subscriber.setLastScanErrorTime(System.currentTimeMillis()); - - if (!retryQueue.offer(subscriber)) { - throw new UnexpectedError("Could not enqueue to delivery manager retry queue"); - } - } - - /** - * Instructs the delivery manager to move the delivery pointer for a given - * subscriber - * - * @param subscriber - * @param prevSeqId - * @param newSeqId - */ - public void moveDeliveryPtrForward(ActiveSubscriberState subscriber, long prevSeqId, long newSeqId) { - enqueueWithoutFailure(new DeliveryPtrMove(subscriber, prevSeqId, newSeqId)); - } - - /* - * ========================================================================== - * == End of public interface, internal machinery begins. - */ - public void run() { - while (keepRunning) { - DeliveryManagerRequest request = null; - - try { - // We use a timeout of 1 second, so that we can wake up once in - // a while to check if there is something in the retry queue. - request = requestQueue.poll(1, TimeUnit.SECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - // First retry any subscriptions that had failed and need a retry - retryErroredSubscribers(); - - if (request == null) { - continue; - } - - request.performRequest(); - - } - } - - /** - * Stop method which will enqueue a ShutdownDeliveryManagerRequest. - */ - public void stop() { - enqueueWithoutFailure(new ShutdownDeliveryManagerRequest()); - } - - protected void retryErroredSubscribers() { - long lastInterestingFailureTime = System.currentTimeMillis() - cfg.getScanBackoffPeriodMs(); - ActiveSubscriberState subscriber; - - while ((subscriber = retryQueue.peek()) != null) { - if (subscriber.getLastScanErrorTime() > lastInterestingFailureTime) { - // Not enough time has elapsed yet, will retry later - // Since the queue is fifo, no need to check later items - return; - } - - // retry now - subscriber.deliverNextMessage(); - retryQueue.poll(); - } - } - - protected void removeDeliveryPtr(ActiveSubscriberState subscriber, Long seqId, boolean isAbsenceOk, - boolean pruneTopic) { - - assert seqId != null; - - // remove this subscriber from the delivery pointers data structure - ByteString topic = subscriber.getTopic(); - SortedMap> deliveryPtrs = perTopicDeliveryPtrs.get(topic); - - if (deliveryPtrs == null && !isAbsenceOk) { - throw new UnexpectedError("No delivery pointers found while disconnecting " + "channel for topic:" + topic); - } - - if (!MapMethods.removeFromMultiMap(deliveryPtrs, seqId, subscriber) && !isAbsenceOk) { - - throw new UnexpectedError("Could not find subscriber:" + subscriber + " at the expected delivery pointer"); - } - - if (pruneTopic && deliveryPtrs.isEmpty()) { - perTopicDeliveryPtrs.remove(topic); - } - - } - - protected long getMinimumSeqId(ByteString topic) { - SortedMap> deliveryPtrs = perTopicDeliveryPtrs.get(topic); - - if (deliveryPtrs == null || deliveryPtrs.isEmpty()) { - return Long.MAX_VALUE - 1; - } - return deliveryPtrs.firstKey(); - } - - protected void addDeliveryPtr(ActiveSubscriberState subscriber, Long seqId) { - - // If this topic doesn't exist in the per-topic delivery pointers table, - // create an entry for it - SortedMap> deliveryPtrs = MapMethods.getAfterInsertingIfAbsent( - perTopicDeliveryPtrs, subscriber.getTopic(), TreeMapLongToSetSubscriberFactory.instance); - - MapMethods.addToMultiMap(deliveryPtrs, seqId, subscriber, HashMapSubscriberFactory.instance); - } - - public class ActiveSubscriberState implements ScanCallback, DeliveryCallback, DeliveryManagerRequest { - ByteString topic; - ByteString subscriberId; - long lastLocalSeqIdDelivered; - boolean connected = true; - DeliveryEndPoint deliveryEndPoint; - long lastScanErrorTime = -1; - long localSeqIdDeliveringNow; - long lastSeqIdCommunicatedExternally; - // TODO make use of these variables - MessageFilter filter; - boolean isHubSubscriber; - final static int SEQ_ID_SLACK = 10; - - public ActiveSubscriberState(ByteString topic, ByteString subscriberId, long lastLocalSeqIdDelivered, - DeliveryEndPoint deliveryEndPoint, MessageFilter filter, boolean isHubSubscriber) { - this.topic = topic; - this.subscriberId = subscriberId; - this.lastLocalSeqIdDelivered = lastLocalSeqIdDelivered; - this.deliveryEndPoint = deliveryEndPoint; - this.filter = filter; - this.isHubSubscriber = isHubSubscriber; - } - - public void setNotConnected() { - this.connected = false; - deliveryEndPoint.close(); - } - - public ByteString getTopic() { - return topic; - } - - public long getLastLocalSeqIdDelivered() { - return lastLocalSeqIdDelivered; - } - - public long getLastScanErrorTime() { - return lastScanErrorTime; - } - - public void setLastScanErrorTime(long lastScanErrorTime) { - this.lastScanErrorTime = lastScanErrorTime; - } - - protected boolean isConnected() { - return connected; - } - - public void deliverNextMessage() { - - if (!isConnected()) { - return; - } - - localSeqIdDeliveringNow = persistenceMgr.getSeqIdAfterSkipping(topic, lastLocalSeqIdDelivered, 1); - - ScanRequest scanRequest = new ScanRequest(topic, localSeqIdDeliveringNow, - /* callback= */this, /* ctx= */null); - - persistenceMgr.scanSingleMessage(scanRequest); - } - - /** - * =============================================================== - * {@link ScanCallback} methods - */ - - public void messageScanned(Object ctx, Message message) { - if (!connected) { - return; - } - - // We're using a simple all-to-all network topology, so no region - // should ever need to forward messages to any other region. - // Otherwise, with the current logic, messages will end up - // ping-pong-ing back and forth between regions with subscriptions - // to each other without termination (or in any other cyclic - // configuration). - if (isHubSubscriber && !message.getSrcRegion().equals(cfg.getMyRegionByteString())) { - sendingFinished(); - return; - } - - /** - * The method below will invoke our sendingFinished() method when - * done - */ - PubSubResponse response = PubSubResponse.newBuilder().setProtocolVersion(ProtocolVersion.VERSION_ONE) - .setStatusCode(StatusCode.SUCCESS).setTxnId(0).setMessage(message).build(); - - deliveryEndPoint.send(response, // - // callback = - this); - - } - - public void scanFailed(Object ctx, Exception exception) { - if (!connected) { - return; - } - - // wait for some time and then retry - retryErroredSubscriberAfterDelay(this); - } - - public void scanFinished(Object ctx, ReasonForFinish reason) { - // no-op - } - - /** - * =============================================================== - * {@link DeliveryCallback} methods - */ - public void sendingFinished() { - if (!connected) { - return; - } - - lastLocalSeqIdDelivered = localSeqIdDeliveringNow; - - if (lastLocalSeqIdDelivered > lastSeqIdCommunicatedExternally + SEQ_ID_SLACK){ - // Note: The order of the next 2 statements is important. We should - // submit a request to change our delivery pointer only *after* we - // have actually changed it. Otherwise, there is a race condition - // with removal of this channel, w.r.t, maintaining the deliveryPtrs - // tree map. - long prevId = lastSeqIdCommunicatedExternally; - lastSeqIdCommunicatedExternally = lastLocalSeqIdDelivered; - moveDeliveryPtrForward(this, prevId, lastLocalSeqIdDelivered); - } - deliverNextMessage(); - } - - public long getLastSeqIdCommunicatedExternally() { - return lastSeqIdCommunicatedExternally; - } - - - public void permanentErrorOnSend() { - stopServingSubscriber(this); - } - - public void transientErrorOnSend() { - retryErroredSubscriberAfterDelay(this); - } - - /** - * =============================================================== - * {@link DeliveryManagerRequest} methods - */ - public void performRequest() { - - // Put this subscriber in the channel to subscriber mapping - ActiveSubscriberState prevSubscriber = subscriberStates.put(new TopicSubscriber(topic, subscriberId), this); - - if (prevSubscriber != null) { - stopServingSubscriber(prevSubscriber); - } - - lastSeqIdCommunicatedExternally = lastLocalSeqIdDelivered; - addDeliveryPtr(this, lastLocalSeqIdDelivered); - - deliverNextMessage(); - }; - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Topic: "); - sb.append(topic.toStringUtf8()); - sb.append("DeliveryPtr: "); - sb.append(lastLocalSeqIdDelivered); - return sb.toString(); - - } - } - - protected class StopServingSubscriber implements DeliveryManagerRequest { - ActiveSubscriberState subscriber; - - public StopServingSubscriber(ActiveSubscriberState subscriber) { - this.subscriber = subscriber; - } - - @Override - public void performRequest() { - - // This will automatically stop delivery, and disconnect the channel - subscriber.setNotConnected(); - - // if the subscriber has moved on, a move request for its delivery - // pointer must be pending in the request queue. Note that the - // subscriber first changes its delivery pointer and then submits a - // request to move so this works. - removeDeliveryPtr(subscriber, subscriber.getLastSeqIdCommunicatedExternally(), // - // isAbsenceOk= - true, - // pruneTopic= - true); - } - - } - - protected class DeliveryPtrMove implements DeliveryManagerRequest { - - ActiveSubscriberState subscriber; - Long oldSeqId; - Long newSeqId; - - public DeliveryPtrMove(ActiveSubscriberState subscriber, Long oldSeqId, Long newSeqId) { - this.subscriber = subscriber; - this.oldSeqId = oldSeqId; - this.newSeqId = newSeqId; - } - - @Override - public void performRequest() { - ByteString topic = subscriber.getTopic(); - long prevMinSeqId = getMinimumSeqId(topic); - - if (subscriber.isConnected()) { - removeDeliveryPtr(subscriber, oldSeqId, // - // isAbsenceOk= - false, - // pruneTopic= - false); - - addDeliveryPtr(subscriber, newSeqId); - } else { - removeDeliveryPtr(subscriber, oldSeqId, // - // isAbsenceOk= - true, - // pruneTopic= - true); - } - - long nowMinSeqId = getMinimumSeqId(topic); - - if (nowMinSeqId > prevMinSeqId) { - persistenceMgr.deliveredUntil(topic, nowMinSeqId); - } - } - } - - protected class ShutdownDeliveryManagerRequest implements DeliveryManagerRequest { - // This is a simple type of Request we will enqueue when the - // PubSubServer is shut down and we want to stop the DeliveryManager - // thread. - public void performRequest() { - keepRunning = false; - } - } - - /** - * ==================================================================== - * - * Dumb factories for our map methods - */ - protected static class TreeMapLongToSetSubscriberFactory implements - Factory>> { - static TreeMapLongToSetSubscriberFactory instance = new TreeMapLongToSetSubscriberFactory(); - - @Override - public SortedMap> newInstance() { - return new TreeMap>(); - } - } - - protected static class HashMapSubscriberFactory implements Factory> { - static HashMapSubscriberFactory instance = new HashMapSubscriberFactory(); - - @Override - public Set newInstance() { - return new HashSet(); - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/BaseHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/BaseHandler.java deleted file mode 100644 index 82a559e957e..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/BaseHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ServerNotResponsibleForTopicException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.HedwigSocketAddress; - -public abstract class BaseHandler implements Handler{ - - protected TopicManager topicMgr; - protected ServerConfiguration cfg; - - protected BaseHandler(TopicManager tm, ServerConfiguration cfg) { - this.topicMgr = tm; - this.cfg = cfg; - } - - - public void handleRequest(final PubSubRequest request, final Channel channel) { - topicMgr.getOwner(request.getTopic(), request.getShouldClaim(), - new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())); - } - - @Override - public void operationFinished(Object ctx, HedwigSocketAddress owner) { - if (!owner.equals(cfg.getServerAddr())) { - channel.write(PubSubResponseUtils.getResponseForException( - new ServerNotResponsibleForTopicException(owner.toString()), request.getTxnId())); - return; - } - handleRequestAtOwner(request, channel); - } - }, null); - } - - public abstract void handleRequestAtOwner(PubSubRequest request, Channel channel); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ChannelDisconnectListener.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ChannelDisconnectListener.java deleted file mode 100644 index 44552bc1dd2..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ChannelDisconnectListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import org.jboss.netty.channel.Channel; - -public interface ChannelDisconnectListener { - - /** - * Act on a particular channel being disconnected - * @param channel - */ - public void channelDisconnected(Channel channel); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ConsumeHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ConsumeHandler.java deleted file mode 100644 index b840dd3b6d5..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/ConsumeHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.ConsumeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.server.subscriptions.SubscriptionManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; - -public class ConsumeHandler extends BaseHandler { - - SubscriptionManager sm; - Callback noopCallback = new NoopCallback(); - - class NoopCallback implements Callback { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - } - - public void operationFinished(Object ctx, T resultOfOperation) { - }; - } - - @Override - public void handleRequestAtOwner(PubSubRequest request, Channel channel) { - if (!request.hasConsumeRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing consume request data"); - return; - } - - ConsumeRequest consumeRequest = request.getConsumeRequest(); - - sm.setConsumeSeqIdForSubscriber(request.getTopic(), consumeRequest.getSubscriberId(), - consumeRequest.getMsgId(), noopCallback, null); - - } - - public ConsumeHandler(TopicManager tm, SubscriptionManager sm, ServerConfiguration cfg) { - super(tm, cfg); - this.sm = sm; - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/Handler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/Handler.java deleted file mode 100644 index d43d345f281..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/Handler.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; - -public interface Handler { - - /** - * Handle a request synchronously or asynchronously. After handling the - * request, the appropriate response should be written on the given channel - * - * @param request - * The request to handle - * - * @param channel - * The channel on which to write the response - */ - public void handleRequest(final PubSubRequest request, final Channel channel); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/PublishHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/PublishHandler.java deleted file mode 100644 index a3aec02b56b..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/PublishHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import org.jboss.netty.channel.Channel; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.server.persistence.PersistRequest; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; - -public class PublishHandler extends BaseHandler { - - private PersistenceManager persistenceMgr; - - public PublishHandler(TopicManager topicMgr, PersistenceManager persistenceMgr, ServerConfiguration cfg) { - super(topicMgr, cfg); - this.persistenceMgr = persistenceMgr; - } - - @Override - public void handleRequestAtOwner(final PubSubRequest request, final Channel channel) { - if (!request.hasPublishRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing publish request data"); - return; - } - - Message msgToSerialize = Message.newBuilder(request.getPublishRequest().getMsg()).setSrcRegion( - cfg.getMyRegionByteString()).build(); - - PersistRequest persistRequest = new PersistRequest(request.getTopic(), msgToSerialize, - new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())); - } - - @Override - public void operationFinished(Object ctx, Long resultOfOperation) { - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - } - }, null); - - persistenceMgr.persistMessage(persistRequest); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/SubscribeHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/SubscribeHandler.java deleted file mode 100644 index cedc13b1182..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/SubscribeHandler.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFutureListener; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ServerNotResponsibleForTopicException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.protoextensions.SubscriptionStateUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.delivery.ChannelEndPoint; -import org.apache.hedwig.server.delivery.DeliveryManager; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.subscriptions.SubscriptionManager; -import org.apache.hedwig.server.subscriptions.TrueFilter; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; - -public class SubscribeHandler extends BaseHandler implements ChannelDisconnectListener{ - static Logger logger = Logger.getLogger(SubscribeHandler.class); - - private DeliveryManager deliveryMgr; - private PersistenceManager persistenceMgr; - private SubscriptionManager subMgr; - ConcurrentHashMap sub2Channel; - ConcurrentHashMap channel2sub; - - public SubscribeHandler(TopicManager topicMgr, DeliveryManager deliveryManager, PersistenceManager persistenceMgr, - SubscriptionManager subMgr, ServerConfiguration cfg) { - super(topicMgr, cfg); - this.deliveryMgr = deliveryManager; - this.persistenceMgr = persistenceMgr; - this.subMgr = subMgr; - sub2Channel = new ConcurrentHashMap(); - channel2sub = new ConcurrentHashMap(); - } - - public void channelDisconnected(Channel channel) { - // Evils of synchronized programming: there is a race between a channel - // getting disconnected, and us adding it to the maps when a subscribe - // succeeds - synchronized (channel) { - TopicSubscriber topicSub = channel2sub.remove(channel); - if (topicSub != null) { - sub2Channel.remove(topicSub); - } - } - } - - @Override - public void handleRequestAtOwner(final PubSubRequest request, final Channel channel) { - - if (!request.hasSubscribeRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing subscribe request data"); - return; - } - - final ByteString topic = request.getTopic(); - - MessageSeqId seqId; - try { - seqId = persistenceMgr.getCurrentSeqIdForTopic(topic); - } catch (ServerNotResponsibleForTopicException e) { - channel.write(PubSubResponseUtils.getResponseForException(e, request.getTxnId())).addListener( - ChannelFutureListener.CLOSE); - return; - } - - final SubscribeRequest subRequest = request.getSubscribeRequest(); - final ByteString subscriberId = subRequest.getSubscriberId(); - - MessageSeqId lastSeqIdPublished = MessageSeqId.newBuilder(seqId).setLocalComponent(seqId.getLocalComponent()).build(); - - subMgr.serveSubscribeRequest(topic, subRequest, lastSeqIdPublished, new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())).addListener( - ChannelFutureListener.CLOSE); - } - - @Override - public void operationFinished(Object ctx, MessageSeqId resultOfOperation) { - - TopicSubscriber topicSub = new TopicSubscriber(topic, subscriberId); - - // race with channel getting disconnected while we are adding it - // to the 2 maps - synchronized (channel) { - if (!channel.isConnected()) { - // channel got disconnected while we were processing the - // subscribe request, - // nothing much we can do in this case - return; - } - - if (null != sub2Channel.putIfAbsent(topicSub, channel)) { - // there was another channel mapped to this sub - PubSubException pse = new PubSubException.TopicBusyException( - "subscription for this topic, subscriberId is already being served on a different channel"); - channel.write(PubSubResponseUtils.getResponseForException(pse, request.getTxnId())) - .addListener(ChannelFutureListener.CLOSE); - return; - } else { - // channel2sub is just a cache, so we can add to it - // without synchronization - channel2sub.put(channel, topicSub); - } - } - // First write success and then tell the delivery manager, - // otherwise the first message might go out before the response - // to the subscribe - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - - // want to start 1 ahead of the consume ptr - MessageSeqId seqIdToStartFrom = MessageSeqId.newBuilder(resultOfOperation).setLocalComponent( - resultOfOperation.getLocalComponent() + 1).build(); - deliveryMgr.startServingSubscription(topic, subscriberId, seqIdToStartFrom, - new ChannelEndPoint(channel), TrueFilter.instance(), SubscriptionStateUtils - .isHubSubscriber(subRequest.getSubscriberId())); - } - }, null); - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/UnsubscribeHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/UnsubscribeHandler.java deleted file mode 100644 index f293d7354e5..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/handlers/UnsubscribeHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import org.jboss.netty.channel.Channel; -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.UnsubscribeRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.delivery.DeliveryManager; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.server.subscriptions.SubscriptionManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; - -public class UnsubscribeHandler extends BaseHandler { - SubscriptionManager subMgr; - DeliveryManager deliveryMgr; - - public UnsubscribeHandler(TopicManager tm, ServerConfiguration cfg, SubscriptionManager subMgr, - DeliveryManager deliveryMgr) { - super(tm, cfg); - this.subMgr = subMgr; - this.deliveryMgr = deliveryMgr; - } - - @Override - public void handleRequestAtOwner(final PubSubRequest request, final Channel channel) { - if (!request.hasUnsubscribeRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing unsubscribe request data"); - return; - } - - final UnsubscribeRequest unsubRequest = request.getUnsubscribeRequest(); - final ByteString topic = request.getTopic(); - final ByteString subscriberId = unsubRequest.getSubscriberId(); - - subMgr.unsubscribe(topic, subscriberId, new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - deliveryMgr.stopServingSubscriber(topic, subscriberId); - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - - } - }, null); - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServer.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServer.java deleted file mode 100644 index cdcc07416ec..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServer.java +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.netty; - -import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.SynchronousQueue; - -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.jboss.netty.bootstrap.ServerBootstrap; -import org.jboss.netty.channel.group.ChannelGroup; -import org.jboss.netty.channel.group.DefaultChannelGroup; -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; -import org.jboss.netty.channel.socket.ServerSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; -import org.jboss.netty.logging.InternalLoggerFactory; -import org.jboss.netty.logging.Log4JLoggerFactory; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.TerminateJVMExceptionHandler; -import org.apache.hedwig.server.delivery.DeliveryManager; -import org.apache.hedwig.server.delivery.FIFODeliveryManager; -import org.apache.hedwig.server.handlers.ConsumeHandler; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.handlers.PublishHandler; -import org.apache.hedwig.server.handlers.SubscribeHandler; -import org.apache.hedwig.server.handlers.UnsubscribeHandler; -import org.apache.hedwig.server.persistence.BookkeeperPersistenceManager; -import org.apache.hedwig.server.persistence.LocalDBPersistenceManager; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.persistence.PersistenceManagerWithRangeScan; -import org.apache.hedwig.server.persistence.ReadAheadCache; -import org.apache.hedwig.server.regions.HedwigHubClientFactory; -import org.apache.hedwig.server.regions.RegionManager; -import org.apache.hedwig.server.ssl.SslServerContextFactory; -import org.apache.hedwig.server.subscriptions.AbstractSubscriptionManager; -import org.apache.hedwig.server.subscriptions.InMemorySubscriptionManager; -import org.apache.hedwig.server.subscriptions.SubscriptionManager; -import org.apache.hedwig.server.subscriptions.ZkSubscriptionManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.server.topics.TrivialOwnAllTopicManager; -import org.apache.hedwig.server.topics.ZkTopicManager; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Either; -import org.apache.hedwig.zookeeper.SafeAsyncCallback; - -public class PubSubServer { - - static Logger logger = Logger.getLogger(PubSubServer.class); - - // Netty related variables - ServerSocketChannelFactory serverChannelFactory; - ClientSocketChannelFactory clientChannelFactory; - ServerConfiguration conf; - ChannelGroup allChannels; - - // Manager components that make up the PubSubServer - PersistenceManager pm; - DeliveryManager dm; - TopicManager tm; - SubscriptionManager sm; - RegionManager rm; - - ZooKeeper zk; // null if we are in standalone mode - BookKeeper bk; // null if we are in standalone mode - - // we use this to prevent long stack chains from building up in callbacks - ScheduledExecutorService scheduler; - - protected PersistenceManager instantiatePersistenceManager(TopicManager topicMgr) throws IOException, - InterruptedException { - - PersistenceManagerWithRangeScan underlyingPM; - - if (conf.isStandalone()) { - - underlyingPM = LocalDBPersistenceManager.instance(); - - } else { - try { - bk = new BookKeeper(zk, clientChannelFactory); - } catch (KeeperException e) { - logger.error("Could not instantiate bookkeeper client", e); - throw new IOException(e); - } - underlyingPM = new BookkeeperPersistenceManager(bk, zk, topicMgr, conf, scheduler); - - } - - PersistenceManager pm = underlyingPM; - - if (conf.getReadAheadEnabled()) { - pm = new ReadAheadCache(underlyingPM, conf).start(); - } - - return pm; - } - - protected SubscriptionManager instantiateSubscriptionManager(TopicManager tm, PersistenceManager pm) { - if (conf.isStandalone()) { - return new InMemorySubscriptionManager(tm, pm, conf, scheduler); - } else { - return new ZkSubscriptionManager(zk, tm, pm, conf, scheduler); - } - - } - - protected RegionManager instantiateRegionManager(PersistenceManager pm, ScheduledExecutorService scheduler) { - return new RegionManager(pm, conf, zk, scheduler, new HedwigHubClientFactory(conf, clientChannelFactory)); - } - - protected void instantiateZookeeperClient() throws IOException { - if (!conf.isStandalone()) { - zk = new ZooKeeper(conf.getZkHost(), conf.getZkTimeout(), new Watcher() { - @Override - public void process(WatchedEvent event) { - } - }); - } - } - - protected TopicManager instantiateTopicManager() throws IOException { - TopicManager tm; - - if (conf.isStandalone()) { - tm = new TrivialOwnAllTopicManager(conf, scheduler); - } else { - try { - tm = new ZkTopicManager(zk, conf, scheduler); - } catch (PubSubException e) { - logger.error("Could not instantiate zk-topic manager", e); - throw new IOException(e); - } - } - return tm; - } - - protected Map initializeNettyHandlers(TopicManager tm, DeliveryManager dm, - PersistenceManager pm, SubscriptionManager sm) { - Map handlers = new HashMap(); - handlers.put(OperationType.PUBLISH, new PublishHandler(tm, pm, conf)); - handlers.put(OperationType.SUBSCRIBE, new SubscribeHandler(tm, dm, pm, sm, conf)); - handlers.put(OperationType.UNSUBSCRIBE, new UnsubscribeHandler(tm, conf, sm, dm)); - handlers.put(OperationType.CONSUME, new ConsumeHandler(tm, sm, conf)); - handlers = Collections.unmodifiableMap(handlers); - return handlers; - } - - protected void initializeNetty(SslServerContextFactory sslFactory, Map handlers) { - boolean isSSLEnabled = (sslFactory != null) ? true : false; - InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); - ServerBootstrap bootstrap = new ServerBootstrap(serverChannelFactory); - UmbrellaHandler umbrellaHandler = new UmbrellaHandler(allChannels, handlers, isSSLEnabled); - PubSubServerPipelineFactory pipeline = new PubSubServerPipelineFactory(umbrellaHandler, sslFactory, conf - .getMaximumMessageSize()); - - bootstrap.setPipelineFactory(pipeline); - bootstrap.setOption("child.tcpNoDelay", true); - bootstrap.setOption("child.keepAlive", true); - bootstrap.setOption("reuseAddress", true); - - // Bind and start to accept incoming connections. - allChannels.add(bootstrap.bind(isSSLEnabled ? new InetSocketAddress(conf.getSSLServerPort()) - : new InetSocketAddress(conf.getServerPort()))); - logger.info("Going into receive loop"); - } - - public void shutdown() { - // TODO: tell bk to close logs - - // Shutdown the ZooKeeper and BookKeeper clients only if we are - // not in stand-alone mode. - try { - if (zk != null) - zk.close(); - if (bk != null) - bk.halt(); - } catch (InterruptedException e) { - logger.error("Error while closing ZooKeeper client!"); - } - - // Stop the RegionManager. - rm.stop(); - - // Stop the DeliveryManager and ReadAheadCache threads (if - // applicable). - // TODO: It'd be cleaner and more general to modify the interfaces to - // include a stop method. If the specific implementation starts threads, - // then the stop method should take care of that clean up. - if (pm instanceof ReadAheadCache) { - ((ReadAheadCache) pm).stop(); - } - if (dm instanceof FIFODeliveryManager) { - ((FIFODeliveryManager) dm).stop(); - } - - // Stop the SubscriptionManager if needed. - if (sm instanceof AbstractSubscriptionManager) { - ((AbstractSubscriptionManager) sm).stop(); - } - - // Close and release the Netty channels and resources - allChannels.close().awaitUninterruptibly(); - serverChannelFactory.releaseExternalResources(); - clientChannelFactory.releaseExternalResources(); - scheduler.shutdown(); - } - - /** - * Starts the hedwig server on the given port - * - * @param port - * @throws ConfigurationException - * if there is something wrong with the given configuration - * @throws IOException - * @throws InterruptedException - * @throws ConfigurationException - */ - public PubSubServer(final ServerConfiguration conf, final Thread.UncaughtExceptionHandler exceptionHandler) - throws Exception { - - // First validate the conf - this.conf = conf; - conf.validate(); - - // We need a custom thread group, so that we can override the uncaught - // exception method - ThreadGroup tg = new ThreadGroup("hedwig") { - @Override - public void uncaughtException(Thread t, Throwable e) { - exceptionHandler.uncaughtException(t, e); - } - }; - // ZooKeeper threads register their own handler. But if some work that - // we do in ZK threads throws an exception, we want our handler to be - // called, not theirs. - SafeAsyncCallback.setUncaughtExceptionHandler(exceptionHandler); - - final SynchronousQueue> queue = new SynchronousQueue>(); - - new Thread(tg, new Runnable() { - @Override - public void run() { - try { - // Since zk is needed by almost everyone,try to see if we - // need that first - scheduler = Executors.newSingleThreadScheduledExecutor(); - serverChannelFactory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors - .newCachedThreadPool()); - clientChannelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors - .newCachedThreadPool()); - - instantiateZookeeperClient(); - tm = instantiateTopicManager(); - pm = instantiatePersistenceManager(tm); - dm = new FIFODeliveryManager(pm, conf); - sm = instantiateSubscriptionManager(tm, pm); - rm = instantiateRegionManager(pm, scheduler); - sm.addListener(rm); - - allChannels = new DefaultChannelGroup("hedwig"); - // Initialize the Netty Handlers (used by the - // UmbrellaHandler) once so they can be shared by - // both the SSL and non-SSL channels. - Map handlers = initializeNettyHandlers(tm, dm, pm, sm); - // Initialize Netty for the regular non-SSL channels - initializeNetty(null, handlers); - if (conf.isSSLEnabled()) { - initializeNetty(new SslServerContextFactory(conf), handlers); - } - } catch (Exception e) { - ConcurrencyUtils.put(queue, Either.right(e)); - return; - } - - ConcurrencyUtils.put(queue, Either.of(new Object(), (Exception) null)); - } - - }).start(); - - Either either = ConcurrencyUtils.take(queue); - if (either.left() == null) { - throw either.right(); - } - } - - public PubSubServer(ServerConfiguration conf) throws Exception { - this(conf, new TerminateJVMExceptionHandler()); - } - - /** - * - * @param msg - * @param rc - * : code to exit with - */ - public static void errorMsgAndExit(String msg, Throwable t, int rc) { - logger.fatal(msg, t); - System.err.println(msg); - System.exit(rc); - } - - public final static int RC_INVALID_CONF_FILE = 1; - public final static int RC_MISCONFIGURED = 2; - public final static int RC_OTHER = 3; - - /** - * @param args - */ - public static void main(String[] args) { - - logger.info("Attempting to start Hedwig"); - ServerConfiguration conf = new ServerConfiguration(); - if (args.length > 0) { - String confFile = args[0]; - try { - conf.loadConf(new File(confFile).toURI().toURL()); - } catch (MalformedURLException e) { - String msg = "Could not open configuration file: " + confFile; - errorMsgAndExit(msg, e, RC_INVALID_CONF_FILE); - } catch (ConfigurationException e) { - String msg = "Malformed configuration file: " + confFile; - errorMsgAndExit(msg, e, RC_MISCONFIGURED); - } - logger.info("Using configuration file " + confFile); - } - try { - new PubSubServer(conf); - } catch (Throwable t) { - errorMsgAndExit("Error during startup", t, RC_OTHER); - } - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServerPipelineFactory.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServerPipelineFactory.java deleted file mode 100644 index fa71b17712b..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/PubSubServerPipelineFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.netty; - -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.ChannelPipelineFactory; -import org.jboss.netty.channel.Channels; -import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; -import org.jboss.netty.handler.codec.frame.LengthFieldPrepender; -import org.jboss.netty.handler.codec.protobuf.ProtobufDecoder; -import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder; -import org.jboss.netty.handler.ssl.SslHandler; - -import org.apache.hedwig.protocol.PubSubProtocol; -import org.apache.hedwig.server.ssl.SslServerContextFactory; - -public class PubSubServerPipelineFactory implements ChannelPipelineFactory { - - // TODO: make these conf settings - final static int MAX_WORKER_THREADS = 32; - final static int MAX_CHANNEL_MEMORY_SIZE = 10 * 1024 * 1024; - final static int MAX_TOTAL_MEMORY_SIZE = 100 * 1024 * 1024; - - private UmbrellaHandler uh; - private SslServerContextFactory sslFactory; - private int maxMessageSize; - - /** - * - * @param uh - * @param sslFactory - * may be null if ssl is disabled - * @param cfg - */ - public PubSubServerPipelineFactory(UmbrellaHandler uh, SslServerContextFactory sslFactory, int maxMessageSize) { - this.uh = uh; - this.sslFactory = sslFactory; - this.maxMessageSize = maxMessageSize; - } - - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - if (sslFactory != null) { - pipeline.addLast("ssl", new SslHandler(sslFactory.getEngine())); - } - pipeline.addLast("lengthbaseddecoder", - new LengthFieldBasedFrameDecoder(maxMessageSize, 0, 4, 0, 4)); - pipeline.addLast("lengthprepender", new LengthFieldPrepender(4)); - - pipeline.addLast("protobufdecoder", new ProtobufDecoder(PubSubProtocol.PubSubRequest.getDefaultInstance())); - pipeline.addLast("protobufencoder", new ProtobufEncoder()); - - // pipeline.addLast("executor", new ExecutionHandler( - // new OrderedMemoryAwareThreadPoolExecutor(MAX_WORKER_THREADS, - // MAX_CHANNEL_MEMORY_SIZE, MAX_TOTAL_MEMORY_SIZE))); - // - // Dependency injection. - pipeline.addLast("umbrellahandler", uh); - return pipeline; - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/UmbrellaHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/UmbrellaHandler.java deleted file mode 100644 index f302f53e5a9..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/netty/UmbrellaHandler.java +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.netty; - -import java.io.IOException; -import java.util.Map; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelPipelineCoverage; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.jboss.netty.channel.SimpleChannelHandler; -import org.jboss.netty.channel.group.ChannelGroup; -import org.jboss.netty.handler.codec.frame.CorruptedFrameException; -import org.jboss.netty.handler.codec.frame.TooLongFrameException; -import org.jboss.netty.handler.ssl.SslHandler; - -import org.apache.hedwig.exceptions.PubSubException.MalformedRequestException; -import org.apache.hedwig.protocol.PubSubProtocol; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.handlers.ChannelDisconnectListener; -import org.apache.hedwig.server.handlers.Handler; - -@ChannelPipelineCoverage("all") -public class UmbrellaHandler extends SimpleChannelHandler { - static Logger logger = Logger.getLogger(UmbrellaHandler.class); - - private Map handlers; - private ChannelGroup allChannels; - private ChannelDisconnectListener subscribeHandler; - private boolean isSSLEnabled = false; - - public UmbrellaHandler(ChannelGroup allChannels, Map handlers, - boolean isSSLEnabled) { - this.allChannels = allChannels; - this.isSSLEnabled = isSSLEnabled; - this.handlers = handlers; - subscribeHandler = (ChannelDisconnectListener) handlers.get(OperationType.SUBSCRIBE); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { - Throwable throwable = e.getCause(); - - // Add here if there are more exceptions we need to be able to tolerate. - // 1. IOException may be thrown when a channel is forcefully closed by - // the other end, or by the ProtobufDecoder when an invalid protobuf is - // received - // 2. TooLongFrameException is thrown by the LengthBasedDecoder if it - // receives a packet that is too big - // 3. CorruptedFramException is thrown by the LengthBasedDecoder when - // the length is negative etc. - if (throwable instanceof IOException || throwable instanceof TooLongFrameException - || throwable instanceof CorruptedFrameException) { - e.getChannel().close(); - if (logger.isDebugEnabled()) { - logger.debug("Uncaught exception", throwable); - } - } else { - // call our uncaught exception handler, which might decide to - // shutdown the system - Thread thread = Thread.currentThread(); - thread.getUncaughtExceptionHandler().uncaughtException(thread, throwable); - } - - } - - @Override - public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - // If SSL is NOT enabled, then we can add this channel to the - // ChannelGroup. Otherwise, that is done when the channel is connected - // and the SSL handshake has completed successfully. - if (!isSSLEnabled) { - allChannels.add(ctx.getChannel()); - } - } - - @Override - public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - if (isSSLEnabled) { - ctx.getPipeline().get(SslHandler.class).handshake(e.getChannel()).addListener(new ChannelFutureListener() { - public void operationComplete(ChannelFuture future) throws Exception { - if (future.isSuccess()) { - if (logger.isDebugEnabled()) { - logger.debug("SSL handshake has completed successfully!"); - } - allChannels.add(future.getChannel()); - } else { - future.getChannel().close(); - } - } - }); - } - } - - @Override - public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - Channel channel = ctx.getChannel(); - // subscribe handler needs to know about channel disconnects - subscribeHandler.channelDisconnected(channel); - channel.close(); - } - - public static void sendErrorResponseToMalformedRequest(Channel channel, long txnId, String msg) { - if (logger.isDebugEnabled()) { - logger.debug("Malformed request from " + channel.getRemoteAddress() + " msg, = " + msg); - } - MalformedRequestException mre = new MalformedRequestException(msg); - PubSubResponse response = PubSubResponseUtils.getResponseForException(mre, txnId); - channel.write(response); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - - if (!(e.getMessage() instanceof PubSubProtocol.PubSubRequest)) { - ctx.sendUpstream(e); - return; - } - - PubSubProtocol.PubSubRequest request = (PubSubProtocol.PubSubRequest) e.getMessage(); - - Handler handler = handlers.get(request.getType()); - Channel channel = ctx.getChannel(); - long txnId = request.getTxnId(); - - if (handler == null) { - sendErrorResponseToMalformedRequest(channel, txnId, "Request type " + request.getType().getNumber() - + " unknown"); - return; - } - - handler.handleRequest(request, channel); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/BookkeeperPersistenceManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/BookkeeperPersistenceManager.java deleted file mode 100644 index 3835fe38a98..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/BookkeeperPersistenceManager.java +++ /dev/null @@ -1,739 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.io.IOException; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; - -import org.apache.bookkeeper.client.BKException; -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; -import org.apache.bookkeeper.client.BookKeeper.DigestType; -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.data.Stat; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ServerNotResponsibleForTopicException; -import org.apache.hedwig.protocol.PubSubProtocol.LedgerRange; -import org.apache.hedwig.protocol.PubSubProtocol.LedgerRanges; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protoextensions.MessageIdUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.TopicOpQueuer; -import org.apache.hedwig.server.common.UnexpectedError; -import org.apache.hedwig.server.persistence.ScanCallback.ReasonForFinish; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.server.topics.TopicOwnershipChangeListener; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.zookeeper.SafeAsynBKCallback; -import org.apache.hedwig.zookeeper.SafeAsyncZKCallback; -import org.apache.hedwig.zookeeper.ZkUtils; - -/** - * This persistence manager uses zookeeper and bookkeeper to store messages. - * - * Information about topics are stored in zookeeper with a znode named after the - * topic that contains an ASCII encoded list with records of the following form: - * - *
- * startSeqId(included)\tledgerId\n
- * 
- * - */ - -public class BookkeeperPersistenceManager implements PersistenceManagerWithRangeScan, TopicOwnershipChangeListener { - static Logger logger = Logger.getLogger(BookkeeperPersistenceManager.class); - static byte[] passwd = "sillysecret".getBytes(); - private BookKeeper bk; - private ZooKeeper zk; - private ServerConfiguration cfg; - - static class InMemoryLedgerRange { - LedgerRange range; - long startSeqIdIncluded; // included, for the very first ledger, this - // value is 1 - LedgerHandle handle; - - public InMemoryLedgerRange(LedgerRange range, long startSeqId, LedgerHandle handle) { - this.range = range; - this.startSeqIdIncluded = startSeqId; - this.handle = handle; - } - - public InMemoryLedgerRange(LedgerRange range, long startSeqId) { - this(range, startSeqId, null); - } - - } - - static class TopicInfo { - /** - * stores the last message-seq-id vector that has been pushed to BK for - * persistence (but not necessarily acked yet by BK) - * - */ - MessageSeqId lastSeqIdPushed; - - /** - * stores the last message-id that has been acked by BK. This number is - * basically used for limiting scans to not read past what has been - * persisted by BK - */ - long lastEntryIdAckedInCurrentLedger = -1; // because BK ledgers starts - // at 0 - - /** - * stores a sorted structure of the ledgers for a topic, mapping from - * the endSeqIdIncluded to the ledger info. This structure does not - * include the current ledger - */ - TreeMap ledgerRanges = new TreeMap(); - - /** - * This is the handle of the current ledger that is being used to write - * messages - */ - InMemoryLedgerRange currentLedgerRange; - - } - - Map topicInfos = new ConcurrentHashMap(); - - TopicOpQueuer queuer; - - /** - * Instantiates a BookKeeperPersistence manager. - * - * @param bk - * a reference to bookkeeper to use. - * @param zk - * a zookeeper handle to use. - * @param zkPrefix - * the zookeeper subtree that stores the topic to ledger - * information. if this prefix does not exist, it will be - * created. - */ - public BookkeeperPersistenceManager(BookKeeper bk, ZooKeeper zk, TopicManager tm, ServerConfiguration cfg, - ScheduledExecutorService executor) { - this.bk = bk; - this.zk = zk; - this.cfg = cfg; - queuer = new TopicOpQueuer(executor); - tm.addTopicOwnershipChangeListener(this); - } - - class RangeScanOp extends TopicOpQueuer.SynchronousOp { - RangeScanRequest request; - int numMessagesRead = 0; - long totalSizeRead = 0; - TopicInfo topicInfo; - - public RangeScanOp(RangeScanRequest request) { - queuer.super(request.topic); - this.request = request; - } - - @Override - protected void runInternal() { - topicInfo = topicInfos.get(topic); - - if (topicInfo == null) { - request.callback.scanFailed(request.ctx, new PubSubException.ServerNotResponsibleForTopicException("")); - return; - } - - startReadingFrom(request.startSeqId); - - } - - protected void read(final InMemoryLedgerRange imlr, final long startSeqId, final long endSeqId) { - - if (imlr.handle == null) { - - bk.asyncOpenLedger(imlr.range.getLedgerId(), DigestType.CRC32, passwd, - new SafeAsynBKCallback.OpenCallback() { - @Override - public void safeOpenComplete(int rc, LedgerHandle ledgerHandle, Object ctx) { - if (rc == BKException.Code.OK) { - imlr.handle = ledgerHandle; - read(imlr, startSeqId, endSeqId); - return; - } - BKException bke = BKException.create(rc); - logger.error("Could not open ledger: " + imlr.range.getLedgerId() + " for topic: " - + topic); - request.callback.scanFailed(ctx, new PubSubException.ServiceDownException(bke)); - return; - } - }, request.ctx); - return; - } - - // ledger handle is not null, we can read from it - long correctedEndSeqId = Math.min(startSeqId + request.messageLimit - numMessagesRead - 1, endSeqId); - - if (logger.isDebugEnabled()) { - logger.debug("Issuing a bk read for ledger: " + imlr.handle.getId() + " from entry-id: " - + (startSeqId - imlr.startSeqIdIncluded) + " to entry-id: " - + (correctedEndSeqId - imlr.startSeqIdIncluded)); - } - - imlr.handle.asyncReadEntries(startSeqId - imlr.startSeqIdIncluded, correctedEndSeqId - - imlr.startSeqIdIncluded, new SafeAsynBKCallback.ReadCallback() { - - long expectedEntryId = startSeqId - imlr.startSeqIdIncluded; - - @Override - public void safeReadComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - if (rc != BKException.Code.OK || !seq.hasMoreElements()) { - BKException bke = BKException.create(rc); - logger.error("Error while reading from ledger: " + imlr.range.getLedgerId() + " for topic: " - + topic.toStringUtf8(), bke); - request.callback.scanFailed(request.ctx, new PubSubException.ServiceDownException(bke)); - return; - } - - LedgerEntry entry = null; - while (seq.hasMoreElements()) { - entry = seq.nextElement(); - Message message; - try { - message = Message.parseFrom(entry.getEntryInputStream()); - } catch (IOException e) { - String msg = "Unreadable message found in ledger: " + imlr.range.getLedgerId() - + " for topic: " + topic.toStringUtf8(); - logger.error(msg, e); - request.callback.scanFailed(ctx, new PubSubException.UnexpectedConditionException(msg)); - return; - } - - if (logger.isDebugEnabled()) { - logger.debug("Read response from ledger: " + lh.getId() + " entry-id: " - + entry.getEntryId()); - } - - assert expectedEntryId == entry.getEntryId() : "expectedEntryId (" + expectedEntryId - + ") != entry.getEntryId() (" + entry.getEntryId() + ")"; - assert (message.getMsgId().getLocalComponent() - imlr.startSeqIdIncluded) == expectedEntryId; - - expectedEntryId++; - request.callback.messageScanned(ctx, message); - numMessagesRead++; - totalSizeRead += message.getBody().size(); - - if (numMessagesRead >= request.messageLimit) { - request.callback.scanFinished(ctx, ReasonForFinish.NUM_MESSAGES_LIMIT_EXCEEDED); - return; - } - - if (totalSizeRead >= request.sizeLimit) { - request.callback.scanFinished(ctx, ReasonForFinish.SIZE_LIMIT_EXCEEDED); - return; - } - } - - startReadingFrom(imlr.startSeqIdIncluded + entry.getEntryId() + 1); - - } - }, request.ctx); - } - - protected void startReadingFrom(long startSeqId) { - - Map.Entry entry = topicInfo.ledgerRanges.ceilingEntry(startSeqId); - - if (entry == null) { - // None of the old ledgers have this seq-id, we must use the - // current ledger - long endSeqId = topicInfo.currentLedgerRange.startSeqIdIncluded - + topicInfo.lastEntryIdAckedInCurrentLedger; - - if (endSeqId < startSeqId) { - request.callback.scanFinished(request.ctx, ReasonForFinish.NO_MORE_MESSAGES); - return; - } - - read(topicInfo.currentLedgerRange, startSeqId, endSeqId); - } else { - read(entry.getValue(), startSeqId, entry.getValue().range.getEndSeqIdIncluded().getLocalComponent()); - } - - } - - } - - @Override - public void scanMessages(RangeScanRequest request) { - queuer.pushAndMaybeRun(request.topic, new RangeScanOp(request)); - } - - public void deliveredUntil(ByteString topic, Long seqId) { - // Nothing to do here. this is just a hint that we cannot use. - } - - public void consumedUntil(ByteString topic, Long seqId) { - TopicInfo topicInfo = topicInfos.get(topic); - if (topicInfo == null) { - logger.error("Server is not responsible for topic!"); - return; - } - for (Long endSeqIdIncluded : topicInfo.ledgerRanges.keySet()) { - if (endSeqIdIncluded <= seqId) { - // This ledger's message entries have all been consumed already - // so it is safe to delete it from BookKeeper. - long ledgerId = topicInfo.ledgerRanges.get(endSeqIdIncluded).range.getLedgerId(); - try { - bk.deleteLedger(ledgerId); - } catch (Exception e) { - // For now, just log an exception error message. In the - // future, we can have more complicated retry logic to - // delete a consumed ledger. The next time the ledger - // garbage collection job runs, we'll once again try to - // delete this ledger. - logger.error("Exception while deleting consumed ledgerId: " + ledgerId, e); - } - } else - break; - } - } - - public MessageSeqId getCurrentSeqIdForTopic(ByteString topic) throws ServerNotResponsibleForTopicException { - TopicInfo topicInfo = topicInfos.get(topic); - - if (topicInfo == null) { - throw new PubSubException.ServerNotResponsibleForTopicException(""); - } - - return topicInfo.lastSeqIdPushed; - } - - public long getSeqIdAfterSkipping(ByteString topic, long seqId, int skipAmount) { - return seqId + skipAmount; - } - - public class PersistOp extends TopicOpQueuer.SynchronousOp { - PersistRequest request; - - public PersistOp(PersistRequest request) { - queuer.super(request.topic); - this.request = request; - } - - @Override - public void runInternal() { - final TopicInfo topicInfo = topicInfos.get(topic); - - if (topicInfo == null) { - request.callback.operationFailed(request.ctx, - new PubSubException.ServerNotResponsibleForTopicException("")); - return; - } - - final long localSeqId = topicInfo.lastSeqIdPushed.getLocalComponent() + 1; - MessageSeqId.Builder builder = MessageSeqId.newBuilder(); - if (request.message.hasMsgId()) { - MessageIdUtils.takeRegionMaximum(builder, topicInfo.lastSeqIdPushed, request.message.getMsgId()); - } else { - builder.addAllRemoteComponents(topicInfo.lastSeqIdPushed.getRemoteComponentsList()); - } - builder.setLocalComponent(localSeqId); - - topicInfo.lastSeqIdPushed = builder.build(); - Message msgToSerialize = Message.newBuilder(request.message).setMsgId(topicInfo.lastSeqIdPushed).build(); - - topicInfo.currentLedgerRange.handle.asyncAddEntry(msgToSerialize.toByteArray(), - new SafeAsynBKCallback.AddCallback() { - @Override - public void safeAddComplete(int rc, LedgerHandle lh, long entryId, Object ctx) { - if (rc != BKException.Code.OK) { - BKException bke = BKException.create(rc); - logger.error("Error while persisting entry to ledger: " + lh.getId() + " for topic: " - + topic.toStringUtf8(), bke); - - // To preserve ordering guarantees, we - // should give up the topic and not let - // other operations through - request.callback.operationFailed(ctx, new PubSubException.ServiceDownException(bke)); - return; - } - - if (entryId + topicInfo.currentLedgerRange.startSeqIdIncluded != localSeqId) { - String msg = "Expected BK to assign entry-id: " - + (localSeqId - topicInfo.currentLedgerRange.startSeqIdIncluded) - + " but it instead assigned entry-id: " + entryId + " topic: " - + topic.toStringUtf8() + "ledger: " + lh.getId(); - logger.fatal(msg); - throw new UnexpectedError(msg); - } - - topicInfo.lastEntryIdAckedInCurrentLedger = entryId; - request.callback.operationFinished(ctx, localSeqId); - } - }, request.ctx); - - } - } - - public void persistMessage(PersistRequest request) { - queuer.pushAndMaybeRun(request.topic, new PersistOp(request)); - } - - public void scanSingleMessage(ScanRequest request) { - throw new RuntimeException("Not implemented"); - } - - static SafeAsynBKCallback.CloseCallback noOpCloseCallback = new SafeAsynBKCallback.CloseCallback() { - @Override - public void safeCloseComplete(int rc, LedgerHandle ledgerHandle, Object ctx) { - }; - }; - - String ledgersPath(ByteString topic) { - return cfg.getZkTopicPath(new StringBuilder(), topic).append("/ledgers").toString(); - } - - class AcquireOp extends TopicOpQueuer.AsynchronousOp { - public AcquireOp(ByteString topic, Callback cb, Object ctx) { - queuer.super(topic, cb, ctx); - } - - @Override - public void run() { - if (topicInfos.containsKey(topic)) { - // Already acquired, do nothing - cb.operationFinished(ctx, null); - return; - } - // read topic ledgers node data - final String zNodePath = ledgersPath(topic); - - zk.getData(zNodePath, false, new SafeAsyncZKCallback.DataCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, byte[] data, Stat stat) { - if (rc == Code.OK.intValue()) { - processTopicLedgersNodeData(data, stat.getVersion()); - return; - } - - if (rc == Code.NONODE.intValue()) { - // create it - final byte[] initialData = LedgerRanges.getDefaultInstance().toByteArray(); - ZkUtils.createFullPathOptimistic(zk, zNodePath, initialData, ZooDefs.Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT, new SafeAsyncZKCallback.StringCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, String name) { - if (rc != Code.OK.intValue()) { - KeeperException ke = ZkUtils.logErrorAndCreateZKException( - "Could not create ledgers node for topic: " + topic.toStringUtf8(), - path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(ke)); - return; - } - // initial version is version 1 - // (guessing) - processTopicLedgersNodeData(initialData, 0); - } - }, ctx); - return; - } - - // otherwise some other error - KeeperException ke = ZkUtils.logErrorAndCreateZKException("Could not read ledgers node for topic: " - + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(ke)); - - } - }, ctx); - } - - void processTopicLedgersNodeData(byte[] data, int version) { - - final LedgerRanges ranges; - try { - ranges = LedgerRanges.parseFrom(data); - } catch (InvalidProtocolBufferException e) { - String msg = "Ledger ranges for topic:" + topic.toStringUtf8() + " could not be deserialized"; - logger.fatal(msg, e); - cb.operationFailed(ctx, new PubSubException.UnexpectedConditionException(msg)); - return; - } - - Iterator lrIterator = ranges.getRangesList().iterator(); - TopicInfo topicInfo = new TopicInfo(); - - long startOfLedger = 1; - - while (lrIterator.hasNext()) { - LedgerRange range = lrIterator.next(); - - if (range.hasEndSeqIdIncluded()) { - // this means it was a valid and completely closed ledger - long endOfLedger = range.getEndSeqIdIncluded().getLocalComponent(); - topicInfo.ledgerRanges.put(endOfLedger, new InMemoryLedgerRange(range, startOfLedger)); - startOfLedger = endOfLedger + 1; - continue; - } - - // If it doesn't have a valid end, it must be the last ledger - if (lrIterator.hasNext()) { - String msg = "Ledger-id: " + range.getLedgerId() + " for topic: " + topic.toStringUtf8() - + " is not the last one but still does not have an end seq-id"; - logger.fatal(msg); - cb.operationFailed(ctx, new PubSubException.UnexpectedConditionException(msg)); - return; - } - - // The last ledger does not have a valid seq-id, lets try to - // find it out - recoverLastTopicLedgerAndOpenNewOne(range.getLedgerId(), version, topicInfo); - return; - } - - // All ledgers were found properly closed, just start a new one - openNewTopicLedger(version, topicInfo); - } - - /** - * Recovers the last ledger, opens a new one, and persists the new - * information to ZK - * - * @param ledgerId - * Ledger to be recovered - */ - private void recoverLastTopicLedgerAndOpenNewOne(final long ledgerId, final int expectedVersionOfLedgerNode, - final TopicInfo topicInfo) { - - bk.asyncOpenLedger(ledgerId, DigestType.CRC32, passwd, new SafeAsynBKCallback.OpenCallback() { - @Override - public void safeOpenComplete(int rc, LedgerHandle ledgerHandle, Object ctx) { - - if (rc != BKException.Code.OK) { - BKException bke = BKException.create(rc); - logger.error("While acquiring topic: " + topic.toStringUtf8() - + ", could not open unrecovered ledger: " + ledgerId, bke); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(bke)); - return; - } - - final long numEntriesInLastLedger = ledgerHandle.getLastAddConfirmed() + 1; - - if (numEntriesInLastLedger <= 0) { - // this was an empty ledger that someone created but - // couldn't write to, so just ignore it - logger.info("Pruning empty ledger: " + ledgerId + " for topic: " + topic.toStringUtf8()); - closeLedger(ledgerHandle); - openNewTopicLedger(expectedVersionOfLedgerNode, topicInfo); - return; - } - - // we have to read the last entry of the ledger to find - // out the last seq-id - - ledgerHandle.asyncReadEntries(numEntriesInLastLedger - 1, numEntriesInLastLedger - 1, - new SafeAsynBKCallback.ReadCallback() { - @Override - public void safeReadComplete(int rc, LedgerHandle lh, Enumeration seq, - Object ctx) { - if (rc != BKException.Code.OK || !seq.hasMoreElements()) { - BKException bke = BKException.create(rc); - logger.error("While recovering ledger: " + ledgerId + " for topic: " - + topic.toStringUtf8() + ", could not read last entry", bke); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(bke)); - return; - } - - Message lastMessage; - try { - lastMessage = Message.parseFrom(seq.nextElement().getEntry()); - } catch (InvalidProtocolBufferException e) { - String msg = "While recovering ledger: " + ledgerId + " for topic: " - + topic.toStringUtf8() + ", could not deserialize last message"; - logger.error(msg, e); - cb.operationFailed(ctx, new PubSubException.UnexpectedConditionException(msg)); - return; - } - - long prevLedgerEnd = topicInfo.ledgerRanges.isEmpty() ? 0 : topicInfo.ledgerRanges - .lastKey(); - LedgerRange lr = LedgerRange.newBuilder().setLedgerId(ledgerId) - .setEndSeqIdIncluded(lastMessage.getMsgId()).build(); - topicInfo.ledgerRanges.put(lr.getEndSeqIdIncluded().getLocalComponent(), - new InMemoryLedgerRange(lr, prevLedgerEnd + 1, lh)); - - logger.info("Recovered unclosed ledger: " + ledgerId + " for topic: " - + topic.toStringUtf8() + " with " + numEntriesInLastLedger + " entries"); - - openNewTopicLedger(expectedVersionOfLedgerNode, topicInfo); - } - }, ctx); - - } - - }, ctx); - } - - /** - * - * @param requiredVersionOfLedgersNode - * The version of the ledgers node when we read it, should be - * the same when we try to write - */ - private void openNewTopicLedger(final int expectedVersionOfLedgersNode, final TopicInfo topicInfo) { - final int ENSEMBLE_SIZE = 3; - final int QUORUM_SIZE = 2; - - bk.asyncCreateLedger(ENSEMBLE_SIZE, QUORUM_SIZE, DigestType.CRC32, passwd, - new SafeAsynBKCallback.CreateCallback() { - boolean processed = false; - - @Override - public void safeCreateComplete(int rc, LedgerHandle lh, Object ctx) { - if (processed) { - return; - } else { - processed = true; - } - - if (rc != BKException.Code.OK) { - BKException bke = BKException.create(rc); - logger.error("Could not create new ledger while acquiring topic: " - + topic.toStringUtf8(), bke); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(bke)); - return; - } - - topicInfo.lastSeqIdPushed = topicInfo.ledgerRanges.isEmpty() ? MessageSeqId.newBuilder() - .setLocalComponent(0).build() : topicInfo.ledgerRanges.lastEntry().getValue().range - .getEndSeqIdIncluded(); - - LedgerRange lastRange = LedgerRange.newBuilder().setLedgerId(lh.getId()).build(); - topicInfo.currentLedgerRange = new InMemoryLedgerRange(lastRange, topicInfo.lastSeqIdPushed - .getLocalComponent() + 1, lh); - - // Persist the fact that we started this new - // ledger to ZK - - LedgerRanges.Builder builder = LedgerRanges.newBuilder(); - for (InMemoryLedgerRange imlr : topicInfo.ledgerRanges.values()) { - builder.addRanges(imlr.range); - } - builder.addRanges(lastRange); - - writeTopicLedgersNode(topic, builder.build().toByteArray(), expectedVersionOfLedgersNode, - topicInfo); - return; - } - }, ctx); - } - - void writeTopicLedgersNode(final ByteString topic, byte[] data, int expectedVersion, final TopicInfo topicInfo) { - final String zNodePath = ledgersPath(topic); - - zk.setData(zNodePath, data, expectedVersion, new SafeAsyncZKCallback.StatCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, Stat stat) { - if (rc != KeeperException.Code.OK.intValue()) { - KeeperException ke = ZkUtils.logErrorAndCreateZKException( - "Could not write ledgers node for topic: " + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(ke)); - return; - } - - // Finally, all done - topicInfos.put(topic, topicInfo); - cb.operationFinished(ctx, null); - } - }, ctx); - - } - } - - /** - * acquire ownership of a topic, doing whatever is needed to be able to - * perform reads and writes on that topic from here on - * - * @param topic - * @param callback - * @param ctx - */ - @Override - public void acquiredTopic(ByteString topic, Callback callback, Object ctx) { - queuer.pushAndMaybeRun(topic, new AcquireOp(topic, callback, ctx)); - } - - public void closeLedger(LedgerHandle lh) { - // try { - // lh.asyncClose(noOpCloseCallback, null); - // } catch (InterruptedException e) { - // logger.error(e); - // Thread.currentThread().interrupt(); - // } - } - - class ReleaseOp extends TopicOpQueuer.SynchronousOp { - - public ReleaseOp(ByteString topic) { - queuer.super(topic); - } - - @Override - public void runInternal() { - TopicInfo topicInfo = topicInfos.remove(topic); - - if (topicInfo == null) { - return; - } - - for (InMemoryLedgerRange imlr : topicInfo.ledgerRanges.values()) { - if (imlr.handle != null) { - closeLedger(imlr.handle); - } - } - - if (topicInfo.currentLedgerRange != null && topicInfo.currentLedgerRange.handle != null) { - closeLedger(topicInfo.currentLedgerRange.handle); - } - } - } - - /** - * Release any resources for the topic that might be currently held. There - * wont be any subsequent reads or writes on that topic coming - * - * @param topic - */ - @Override - public void lostTopic(ByteString topic) { - queuer.pushAndMaybeRun(topic, new ReleaseOp(topic)); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheKey.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheKey.java deleted file mode 100644 index 92de5eb3aa5..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheKey.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.server.common.ByteStringInterner; - -public class CacheKey { - - ByteString topic; - long seqId; - - public CacheKey(ByteString topic, long seqId) { - this.topic = ByteStringInterner.intern(topic); - this.seqId = seqId; - } - - public ByteString getTopic() { - return topic; - } - - public long getSeqId() { - return seqId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (seqId ^ (seqId >>> 32)); - result = prime * result + ((topic == null) ? 0 : topic.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CacheKey other = (CacheKey) obj; - if (seqId != other.seqId) - return false; - if (topic == null) { - if (other.topic != null) - return false; - } else if (!topic.equals(other.topic)) - return false; - return true; - } - - @Override - public String toString() { - return "(" + topic.toStringUtf8() + "," + seqId + ")"; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheValue.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheValue.java deleted file mode 100644 index 320e86db817..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/CacheValue.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.LinkedList; -import java.util.Queue; - -import org.apache.log4j.Logger; - -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.server.common.UnexpectedError; - -/** - * This class is NOT thread safe. It need not be thread-safe because our - * read-ahead cache will operate with only 1 thread - * - */ -public class CacheValue { - - static Logger logger = Logger.getLogger(ReadAheadCache.class); - - Queue callbacks = new LinkedList(); - Message message; - long timeOfAddition = 0; - - public CacheValue() { - } - - public boolean isStub() { - return message == null; - } - - public long getTimeOfAddition() { - if (message == null) { - throw new UnexpectedError("Time of add requested from a stub"); - } - return timeOfAddition; - } - - public void setMessageAndInvokeCallbacks(Message message, long currTime) { - if (this.message != null) { - // Duplicate read for the same message coming back - return; - } - - this.message = message; - this.timeOfAddition = currTime; - ScanCallbackWithContext callbackWithCtx; - if (logger.isDebugEnabled()) { - logger.debug("Invoking " + callbacks.size() + " callbacks for " + " message added to cache"); - } - while ((callbackWithCtx = callbacks.poll()) != null) { - callbackWithCtx.getScanCallback().messageScanned(callbackWithCtx.getCtx(), message); - } - } - - public void addCallback(ScanCallback callback, Object ctx) { - if (!isStub()) { - // call the callback right away - callback.messageScanned(ctx, message); - return; - } - - callbacks.add(new ScanCallbackWithContext(callback, ctx)); - } - - public Message getMessage() { - return message; - } - - public void setErrorAndInvokeCallbacks(Exception exception) { - ScanCallbackWithContext callbackWithCtx; - while ((callbackWithCtx = callbacks.poll()) != null) { - callbackWithCtx.getScanCallback().scanFailed(callbackWithCtx.getCtx(), exception); - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/Factory.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/Factory.java deleted file mode 100644 index c1ee24ccfcd..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/Factory.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -public interface Factory { - public T newInstance(); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/LocalDBPersistenceManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/LocalDBPersistenceManager.java deleted file mode 100644 index b257d9d6a58..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/LocalDBPersistenceManager.java +++ /dev/null @@ -1,426 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.io.File; -import java.io.IOException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.sql.rowset.serial.SerialBlob; - -import org.apache.log4j.Logger; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.exceptions.PubSubException.UnexpectedConditionException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protoextensions.MessageIdUtils; -import org.apache.hedwig.server.persistence.ScanCallback.ReasonForFinish; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.FileUtils; - -public class LocalDBPersistenceManager implements PersistenceManagerWithRangeScan { - static Logger logger = Logger.getLogger(LocalDBPersistenceManager.class); - - static String connectionURL; - - static { - try { - File tempDir = FileUtils.createTempDirectory("derby", null); - - // Since derby needs to create it, I will have to delete it first - if (!tempDir.delete()) { - throw new IOException("Could not delete dir: " + tempDir.getAbsolutePath()); - } - connectionURL = "jdbc:derby:" + tempDir.getAbsolutePath() + ";create=true"; - } catch (IOException e) { - throw new RuntimeException(e); - } - - } - - private static final ThreadLocal threadLocalConnection = new ThreadLocal() { - @Override - protected Connection initialValue() { - try { - return DriverManager.getConnection(connectionURL); - } catch (SQLException e) { - logger.error("Could not connect to derby", e); - return null; - } - } - }; - static final String ID_FIELD_NAME = "id"; - static final String MSG_FIELD_NAME = "msg"; - static final String driver = "org.apache.derby.jdbc.EmbeddedDriver"; - - static final int SCAN_CHUNK = 1000; - - /** - * Having trouble restarting the database multiple times from within the - * same jvm. Hence to facilitate units tests, we are just going to have a - * version number that we will append to every table name. This version - * number will be incremented in lieu of shutting down the database and - * restarting it, so that we get different table names, and it behaves like - * a brand new database - */ - private int version = 0; - - ConcurrentMap currTopicSeqIds = new ConcurrentHashMap(); - - static LocalDBPersistenceManager instance = new LocalDBPersistenceManager(); - - public static LocalDBPersistenceManager instance() { - return instance; - } - - private LocalDBPersistenceManager() { - - try { - Class.forName(driver).newInstance(); - logger.info("Derby Driver loaded"); - } catch (java.lang.ClassNotFoundException e) { - logger.error("Derby driver not found", e); - } catch (InstantiationException e) { - logger.error("Could not instantiate derby driver", e); - } catch (IllegalAccessException e) { - logger.error("Could not instantiate derby driver", e); - } - } - - /** - * Ensures that at least the default seq-id exists in the map for the given - * topic. Checks for race conditions (.e.g, another thread inserts the - * default id before us), and returns the latest seq-id value in the map - * - * @param topic - * @return - */ - private MessageSeqId ensureSeqIdExistsForTopic(ByteString topic) { - MessageSeqId presentSeqIdInMap = currTopicSeqIds.get(topic); - - if (presentSeqIdInMap != null) { - return presentSeqIdInMap; - } - - presentSeqIdInMap = MessageSeqId.newBuilder().setLocalComponent(0).build(); - MessageSeqId oldSeqIdInMap = currTopicSeqIds.putIfAbsent(topic, presentSeqIdInMap); - - if (oldSeqIdInMap != null) { - return oldSeqIdInMap; - } - return presentSeqIdInMap; - - } - - /** - * Adjust the current seq id of the topic based on the message we are about - * to publish. The local component of the current seq-id is always - * incremented by 1. For the other components, there are two cases: - * - * 1. If the message to be published doesn't have a seq-id (locally - * published messages), the other components are left as is. - * - * 2. If the message to be published has a seq-id, we take the max of the - * current one we have, and that in the message to be published. - * - * @param topic - * @param messageToPublish - * @return The value of the local seq-id obtained after incrementing the - * local component. This value should be used as an id while - * persisting to Derby - * @throws UnexpectedConditionException - */ - private long adjustTopicSeqIdForPublish(ByteString topic, Message messageToPublish) - throws UnexpectedConditionException { - long retValue = 0; - MessageSeqId oldId; - MessageSeqId.Builder newIdBuilder = MessageSeqId.newBuilder(); - - do { - oldId = ensureSeqIdExistsForTopic(topic); - - // Increment our own component by 1 - retValue = oldId.getLocalComponent() + 1; - newIdBuilder.setLocalComponent(retValue); - - if (messageToPublish.hasMsgId()) { - // take a region-wise max - MessageIdUtils.takeRegionMaximum(newIdBuilder, messageToPublish.getMsgId(), oldId); - - } else { - newIdBuilder.addAllRemoteComponents(oldId.getRemoteComponentsList()); - } - } while (!currTopicSeqIds.replace(topic, oldId, newIdBuilder.build())); - - return retValue; - - } - - public long getSeqIdAfterSkipping(ByteString topic, long seqId, int skipAmount) { - return seqId + skipAmount; - } - - public void persistMessage(PersistRequest request) { - - Connection conn = threadLocalConnection.get(); - - Callback callback = request.getCallback(); - Object ctx = request.getCtx(); - ByteString topic = request.getTopic(); - Message message = request.getMessage(); - - if (conn == null) { - callback.operationFailed(ctx, new ServiceDownException("Not connected to derby")); - return; - } - - long seqId; - - try { - seqId = adjustTopicSeqIdForPublish(topic, message); - } catch (UnexpectedConditionException e) { - callback.operationFailed(ctx, e); - return; - } - PreparedStatement stmt; - - boolean triedCreatingTable = false; - while (true) { - try { - message.getBody(); - stmt = conn.prepareStatement("INSERT INTO " + getTableNameForTopic(topic) + " VALUES(?,?)"); - stmt.setLong(1, seqId); - stmt.setBlob(2, new SerialBlob(message.toByteArray())); - - int rowCount = stmt.executeUpdate(); - stmt.close(); - if (rowCount != 1) { - logger.error("Unexpected number of affected rows from derby"); - callback.operationFailed(ctx, new ServiceDownException("Unexpected response from derby")); - return; - } - break; - } catch (SQLException sqle) { - String theError = (sqle).getSQLState(); - if (theError.equals("42X05") && !triedCreatingTable) { - createTable(conn, topic); - triedCreatingTable = true; - continue; - } - - logger.error("Error while executing derby insert", sqle); - callback.operationFailed(ctx, new ServiceDownException(sqle)); - return; - } - } - callback.operationFinished(ctx, seqId); - } - - /* - * This method does not throw an exception because another thread might - * sneak in and create the table before us - */ - private void createTable(Connection conn, ByteString topic) { - - try { - Statement stmt = conn.createStatement(); - String tableName = getTableNameForTopic(topic); - stmt.execute("CREATE TABLE " + tableName + " (" + ID_FIELD_NAME + " BIGINT NOT NULL CONSTRAINT ID_PK_" - + tableName + " PRIMARY KEY," + MSG_FIELD_NAME + " BLOB(2M) NOT NULL)"); - } catch (SQLException e) { - logger.debug("Could not create table", e); - } - } - - public MessageSeqId getCurrentSeqIdForTopic(ByteString topic) { - return ensureSeqIdExistsForTopic(topic); - } - - public void scanSingleMessage(ScanRequest request) { - scanMessagesInternal(request.getTopic(), request.getStartSeqId(), 1, Long.MAX_VALUE, request.getCallback(), - request.getCtx(), 1); - return; - } - - public void scanMessages(RangeScanRequest request) { - scanMessagesInternal(request.getTopic(), request.getStartSeqId(), request.getMessageLimit(), request - .getSizeLimit(), request.getCallback(), request.getCtx(), SCAN_CHUNK); - return; - } - - private String getTableNameForTopic(ByteString topic) { - return (topic.toStringUtf8() + "_" + version); - } - - private void scanMessagesInternal(ByteString topic, long startSeqId, int messageLimit, long sizeLimit, - ScanCallback callback, Object ctx, int scanChunk) { - - Connection conn = threadLocalConnection.get(); - - if (conn == null) { - callback.scanFailed(ctx, new ServiceDownException("Not connected to derby")); - return; - } - - long currentSeqId; - currentSeqId = startSeqId; - - PreparedStatement stmt; - try { - try { - stmt = conn.prepareStatement("SELECT * FROM " + getTableNameForTopic(topic) + " WHERE " + ID_FIELD_NAME - + " >= ? AND " + ID_FIELD_NAME + " <= ?"); - - } catch (SQLException sqle) { - String theError = (sqle).getSQLState(); - if (theError.equals("42X05")) { - // No table, scan is over - callback.scanFinished(ctx, ReasonForFinish.NO_MORE_MESSAGES); - return; - } else { - throw sqle; - } - } - - int numMessages = 0; - long totalSize = 0; - - while (true) { - - stmt.setLong(1, currentSeqId); - stmt.setLong(2, currentSeqId + scanChunk); - - if (!stmt.execute()) { - String errorMsg = "Select query did not return a result set"; - logger.error(errorMsg); - stmt.close(); - callback.scanFailed(ctx, new ServiceDownException(errorMsg)); - return; - } - - ResultSet resultSet = stmt.getResultSet(); - - if (!resultSet.next()) { - stmt.close(); - callback.scanFinished(ctx, ReasonForFinish.NO_MORE_MESSAGES); - return; - } - - do { - - long localSeqId = resultSet.getLong(1); - - Message.Builder messageBuilder = Message.newBuilder().mergeFrom(resultSet.getBinaryStream(2)); - - // Merge in the local seq-id since that is not stored with - // the message - Message message = MessageIdUtils.mergeLocalSeqId(messageBuilder, localSeqId); - - callback.messageScanned(ctx, message); - numMessages++; - totalSize += message.getBody().size(); - - if (numMessages > messageLimit) { - stmt.close(); - callback.scanFinished(ctx, ReasonForFinish.NUM_MESSAGES_LIMIT_EXCEEDED); - return; - } else if (totalSize > sizeLimit) { - stmt.close(); - callback.scanFinished(ctx, ReasonForFinish.SIZE_LIMIT_EXCEEDED); - return; - } - - } while (resultSet.next()); - - currentSeqId += SCAN_CHUNK; - } - } catch (SQLException e) { - logger.error("SQL Exception", e); - callback.scanFailed(ctx, new ServiceDownException(e)); - return; - } catch (IOException e) { - logger.error("Message stored in derby is not parseable", e); - callback.scanFailed(ctx, new ServiceDownException(e)); - return; - } - - } - - public void deliveredUntil(ByteString topic, Long seqId) { - // noop - } - - public void consumedUntil(ByteString topic, Long seqId) { - Connection conn = threadLocalConnection.get(); - if (conn == null) { - logger.error("Not connected to derby"); - return; - } - PreparedStatement stmt; - try { - stmt = conn.prepareStatement("DELETE FROM " + getTableNameForTopic(topic) + " WHERE " + ID_FIELD_NAME - + " <= ?"); - stmt.setLong(1, seqId); - int rowCount = stmt.executeUpdate(); - logger.debug("Deleted " + rowCount + " records for topic: " + topic.toStringUtf8() + ", seqId: " + seqId); - stmt.close(); - } catch (SQLException sqle) { - String theError = (sqle).getSQLState(); - if (theError.equals("42X05")) { - logger.warn("Table for topic (" + topic + ") does not exist so no consumed messages to delete!"); - } else - logger.error("Error while executing derby delete for consumed messages", sqle); - } - } - - @Override - protected void finalize() throws Throwable { - if (driver.equals("org.apache.derby.jdbc.EmbeddedDriver")) { - boolean gotSQLExc = false; - // This is weird: on normal shutdown, it throws an exception - try { - DriverManager.getConnection("jdbc:derby:;shutdown=true").close(); - } catch (SQLException se) { - if (se.getSQLState().equals("XJ015")) { - gotSQLExc = true; - } - } - if (!gotSQLExc) { - logger.error("Database did not shut down normally"); - } else { - logger.info("Database shut down normally"); - } - } - super.finalize(); - } - - public void reset() { - // just move the namespace over to the next one - version++; - currTopicSeqIds.clear(); - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/MapMethods.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/MapMethods.java deleted file mode 100644 index f640723514d..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/MapMethods.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.Collection; -import java.util.Map; - -public class MapMethods { - - public static V getAfterInsertingIfAbsent(Map map, K key, Factory valueFactory) { - V value = map.get(key); - - if (value == null) { - value = valueFactory.newInstance(); - map.put(key, value); - } - - return value; - } - - public static > void addToMultiMap(Map map, K key, V value, - Factory valueFactory) { - Collection collection = getAfterInsertingIfAbsent(map, key, valueFactory); - - collection.add(value); - - } - - public static > boolean removeFromMultiMap(Map map, K key, V value) { - Collection collection = map.get(key); - - if (collection == null) { - return false; - } - - if (!collection.remove(value)) { - return false; - } else { - if (collection.isEmpty()) { - map.remove(key); - } - return true; - } - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistRequest.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistRequest.java deleted file mode 100644 index ee284e45e83..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistRequest.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.util.Callback; - -/** - * Encapsulates a request to persist a given message on a given topic. The - * request is completed asynchronously, callback and context are provided - * - */ -public class PersistRequest { - ByteString topic; - Message message; - Callback callback; - Object ctx; - - public PersistRequest(ByteString topic, Message message, Callback callback, Object ctx) { - this.topic = topic; - this.message = message; - this.callback = callback; - this.ctx = ctx; - } - - public ByteString getTopic() { - return topic; - } - - public Message getMessage() { - return message; - } - - public Callback getCallback() { - return callback; - } - - public Object getCtx() { - return ctx; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManager.java deleted file mode 100644 index 49f2c99f6f5..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManager.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException.ServerNotResponsibleForTopicException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; - -/** - * An implementation of this interface will persist messages in order and assign - * a seqId to each persisted message. SeqId need not be a single number in - * general. SeqId is opaque to all layers above {@link PersistenceManager}. Only - * the {@link PersistenceManager} needs to understand the format of the seqId - * and maintain it in such a way that there is a total order on the seqIds of a - * topic. - * - */ -public interface PersistenceManager { - - /** - * Executes the given persist request asynchronously. When done, the - * callback specified in the request object is called with the result of the - * operation set to the {@link LocalMessageSeqId} assigned to the persisted - * message. - */ - public void persistMessage(PersistRequest request); - - /** - * Get the seqId of the last message that has been persisted to the given - * topic. The returned seqId will be set as the consume position of any - * brand new subscription on this topic. - * - * Note that the return value may quickly become invalid because a - * {@link #persistMessage(String, PublishedMessage)} call from another - * thread succeeds. For us, the typical use case is choosing the consume - * position of a new subscriber. Since the subscriber need not receive all - * messages that are published while the subscribe call is in progress, such - * loose semantics from this method is acceptable. - * - * @param topic - * @return the seqId of the last persisted message. - * @throws ServerNotResponsibleForTopicException - */ - public MessageSeqId getCurrentSeqIdForTopic(ByteString topic) throws ServerNotResponsibleForTopicException; - - /** - * Executes the given scan request - * - */ - public void scanSingleMessage(ScanRequest request); - - /** - * Gets the next seq-id. This method should never block. - */ - public long getSeqIdAfterSkipping(ByteString topic, long seqId, int skipAmount); - - /** - * Hint that the messages until the given seqId have been delivered and wont - * be needed unless there is a failure of some kind - */ - public void deliveredUntil(ByteString topic, Long seqId); - - /** - * Hint that the messages until the given seqId have been consumed by all - * subscribers to the topic and no longer need to be stored. The - * implementation classes can decide how and if they want to garbage collect - * and delete these older topic messages that are no longer needed. - * - * @param topic - * Topic - * @param seqId - * Message local sequence ID - */ - public void consumedUntil(ByteString topic, Long seqId); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManagerWithRangeScan.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManagerWithRangeScan.java deleted file mode 100644 index 09cb96d0ee0..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/PersistenceManagerWithRangeScan.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -public interface PersistenceManagerWithRangeScan extends PersistenceManager { - /** - * Executes the given range scan request - * - * @param request - */ - public void scanMessages(RangeScanRequest request); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/RangeScanRequest.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/RangeScanRequest.java deleted file mode 100644 index 47bb0dfb8e2..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/RangeScanRequest.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import com.google.protobuf.ByteString; - -/** - * Encapsulates a request to scan messages on the given topic starting from the - * given seqId (included). A call-back {@link ScanCallback} is provided. As - * messages are scanned, the relevant methods of the {@link ScanCallback} are - * called. Two hints are provided as to when scanning should stop: in terms of - * number of messages scanned, or in terms of the total size of messages - * scanned. Scanning stops whenever one of these limits is exceeded. These - * checks, especially the one about message size, are only approximate. The - * {@link ScanCallback} used should be prepared to deal with more or less - * messages scanned. If an error occurs during scanning, the - * {@link ScanCallback} is notified of the error. - * - */ -public class RangeScanRequest { - ByteString topic; - long startSeqId; - int messageLimit; - long sizeLimit; - ScanCallback callback; - Object ctx; - - public RangeScanRequest(ByteString topic, long startSeqId, int messageLimit, long sizeLimit, ScanCallback callback, - Object ctx) { - this.topic = topic; - this.startSeqId = startSeqId; - this.messageLimit = messageLimit; - this.sizeLimit = sizeLimit; - this.callback = callback; - this.ctx = ctx; - } - - public ByteString getTopic() { - return topic; - } - - public long getStartSeqId() { - return startSeqId; - } - - public int getMessageLimit() { - return messageLimit; - } - - public long getSizeLimit() { - return sizeLimit; - } - - public ScanCallback getCallback() { - return callback; - } - - public Object getCtx() { - return ctx; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ReadAheadCache.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ReadAheadCache.java deleted file mode 100644 index 3cff829dcd6..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ReadAheadCache.java +++ /dev/null @@ -1,703 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -import org.apache.log4j.Logger; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ServerNotResponsibleForTopicException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protoextensions.MessageIdUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.UnexpectedError; -import org.apache.hedwig.util.Callback; - -public class ReadAheadCache implements PersistenceManager, Runnable { - - static Logger logger = Logger.getLogger(ReadAheadCache.class); - - protected interface CacheRequest { - public void performRequest(); - } - - /** - * The underlying persistence manager that will be used for persistence and - * scanning below the cache - */ - protected PersistenceManagerWithRangeScan realPersistenceManager; - - /** - * The structure for the cache - */ - protected Map cache = new HashMap(); - - /** - * To simplify synchronization, the cache will be maintained by a single - * cache maintainer thread. This is the queue that will hold requests that - * need to be served by this thread - */ - protected BlockingQueue requestQueue = new LinkedBlockingQueue(); - - /** - * We want to keep track of when entries were added in the cache, so that we - * can remove them in a FIFO fashion - */ - protected SortedMap> timeIndexOfAddition = new TreeMap>(); - - /** - * We also want to track the entries in seq-id order so that we can clean up - * entries after the last subscriber - */ - protected Map> orderedIndexOnSeqId = new HashMap>(); - - /** - * We maintain an estimate of the current size of the cache, so that we know - * when to evict entries. - */ - protected long presentCacheSize = 0; - - /** - * One instance of a callback that we will pass to the underlying - * persistence manager when asking it to persist messages - */ - protected PersistCallback persistCallbackInstance = new PersistCallback(); - - /** - * 2 kinds of exceptions that we will use to signal error from readahead - */ - protected NoSuchSeqIdException noSuchSeqIdExceptionInstance = new NoSuchSeqIdException(); - protected ReadAheadException readAheadExceptionInstance = new ReadAheadException(); - - protected ServerConfiguration cfg; - protected Thread cacheThread; - // Boolean indicating if this thread should continue running. This is used - // when we want to stop the thread during a PubSubServer shutdown. - protected boolean keepRunning = true; - - /** - * Constructor. Starts the cache maintainer thread - * - * @param realPersistenceManager - */ - public ReadAheadCache(PersistenceManagerWithRangeScan realPersistenceManager, ServerConfiguration cfg) { - this.realPersistenceManager = realPersistenceManager; - this.cfg = cfg; - cacheThread = new Thread(this, "CacheThread"); - } - - public ReadAheadCache start() { - cacheThread.start(); - return this; - } - - /** - * ======================================================================== - * Methods of {@link PersistenceManager} that we will pass straight down to - * the real persistence manager. - */ - - public long getSeqIdAfterSkipping(ByteString topic, long seqId, int skipAmount) { - return realPersistenceManager.getSeqIdAfterSkipping(topic, seqId, skipAmount); - } - - public MessageSeqId getCurrentSeqIdForTopic(ByteString topic) throws ServerNotResponsibleForTopicException { - return realPersistenceManager.getCurrentSeqIdForTopic(topic); - } - - /** - * ======================================================================== - * Other methods of {@link PersistenceManager} that the cache needs to take - * some action on. - * - * 1. Persist: We pass it through to the real persistence manager but insert - * our callback on the return path - * - */ - public void persistMessage(PersistRequest request) { - // make a new PersistRequest object so that we can insert our own - // callback in the middle. Assign the original request as the context - // for the callback. - - PersistRequest newRequest = new PersistRequest(request.getTopic(), request.getMessage(), - persistCallbackInstance, request); - realPersistenceManager.persistMessage(newRequest); - } - - /** - * The callback that we insert on the persist request return path. The - * callback simply forms a {@link PersistResponse} object and inserts it in - * the request queue to be handled serially by the cache maintainer thread. - * - */ - public class PersistCallback implements Callback { - - /** - * In case there is a failure in persisting, just pass it to the - * original callback - */ - public void operationFailed(Object ctx, PubSubException exception) { - PersistRequest originalRequest = (PersistRequest) ctx; - Callback originalCallback = originalRequest.getCallback(); - Object originalContext = originalRequest.getCtx(); - originalCallback.operationFailed(originalContext, exception); - } - - /** - * When the persist finishes, we first notify the original callback of - * success, and then opportunistically treat the message as if it just - * came in through a scan - */ - public void operationFinished(Object ctx, Long resultOfOperation) { - PersistRequest originalRequest = (PersistRequest) ctx; - - // Lets call the original callback first so that the publisher can - // hear success - originalRequest.getCallback().operationFinished(originalRequest.getCtx(), resultOfOperation); - - // Original message that was persisted didn't have the local seq-id. - // Lets add that in - Message messageWithLocalSeqId = MessageIdUtils.mergeLocalSeqId(originalRequest.getMessage(), - resultOfOperation); - - // Now enqueue a request to add this newly persisted message to our - // cache - CacheKey cacheKey = new CacheKey(originalRequest.getTopic(), resultOfOperation); - - enqueueWithoutFailure(new ScanResponse(cacheKey, messageWithLocalSeqId)); - } - - } - - /** - * Too complicated to deal with enqueue failures from the context of our - * callbacks. Its just simpler to quit and restart afresh. Moreover, this - * should not happen as the request queue for the cache maintainer is - * unbounded. - * - * @param obj - */ - protected void enqueueWithoutFailure(CacheRequest obj) { - if (!requestQueue.offer(obj)) { - throw new UnexpectedError("Could not enqueue object: " + obj.toString() - + " to cache request queue. Exiting."); - - } - } - - /** - * Another method from {@link PersistenceManager}. - * - * 2. Scan - Since the scan needs to touch the cache, we will just enqueue - * the scan request and let the cache maintainer thread handle it. - */ - public void scanSingleMessage(ScanRequest request) { - // Let the scan requests be serialized through the queue - enqueueWithoutFailure(new ScanRequestWrapper(request)); - } - - /** - * Another method from {@link PersistenceManager}. - * - * 3. Enqueue the request so that the cache maintainer thread can delete all - * message-ids older than the one specified - */ - public void deliveredUntil(ByteString topic, Long seqId) { - enqueueWithoutFailure(new DeliveredUntil(topic, seqId)); - } - - /** - * Another method from {@link PersistenceManager}. - * - * Since this is a cache layer on top of an underlying persistence manager, - * we can just call the consumedUntil method there. The messages older than - * the latest one passed here won't be accessed anymore so they should just - * get aged out of the cache eventually. For now, there is no need to - * proactively remove those entries from the cache. - */ - public void consumedUntil(ByteString topic, Long seqId) { - realPersistenceManager.consumedUntil(topic, seqId); - } - - /** - * ======================================================================== - * BEGINNING OF CODE FOR THE CACHE MAINTAINER THREAD - * - * 1. The run method. It simply dequeues from the request queue, checks the - * type of object and acts accordingly - */ - public void run() { - while (keepRunning) { - CacheRequest obj; - try { - obj = requestQueue.take(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - obj.performRequest(); - } - - } - - /** - * Stop method which will enqueue a ShutdownCacheRequest. - */ - public void stop() { - enqueueWithoutFailure(new ShutdownCacheRequest()); - } - - /** - * The readahead policy is simple: We check if an entry already exists for - * the message being requested. If an entry exists, it means that either - * that message is already in the cache, or a read for that message is - * outstanding. In that case, we look a little ahead (by readAheadCount/2) - * and issue a range read of readAheadCount/2 messages. The idea is to - * ensure that the next readAheadCount messages are always available. - * - * @return the range scan that should be issued for read ahead - */ - protected RangeScanRequest doReadAhead(ScanRequest request) { - ByteString topic = request.getTopic(); - Long seqId = request.getStartSeqId(); - - int readAheadCount = cfg.getReadAheadCount(); - // To prevent us from getting screwed by bad configuration - readAheadCount = Math.max(1, readAheadCount); - - RangeScanRequest readAheadRequest = doReadAheadStartingFrom(topic, seqId, readAheadCount); - - if (readAheadRequest != null) { - return readAheadRequest; - } - - // start key was already there in the cache so no readahead happened, - // lets look a little beyond - seqId = realPersistenceManager.getSeqIdAfterSkipping(topic, seqId, readAheadCount / 2); - - readAheadRequest = doReadAheadStartingFrom(topic, seqId, readAheadCount / 2); - - return readAheadRequest; - } - - /** - * This method just checks if the provided seq-id already exists in the - * cache. If not, a range read of the specified amount is issued. - * - * @param topic - * @param seqId - * @param readAheadCount - * @return The range read that should be issued - */ - protected RangeScanRequest doReadAheadStartingFrom(ByteString topic, long seqId, int readAheadCount) { - - long startSeqId = seqId; - Queue installedStubs = new LinkedList(); - - int i = 0; - - for (; i < readAheadCount; i++) { - CacheKey cacheKey = new CacheKey(topic, seqId); - - // Even if a stub exists, it means that a scan for that is - // outstanding - if (cache.containsKey(cacheKey)) { - break; - } - CacheValue cacheValue = new CacheValue(); - cache.put(cacheKey, cacheValue); - - if (logger.isDebugEnabled()) { - logger.debug("Adding stub for seq-id: " + seqId + " topic: " + topic.toStringUtf8()); - } - installedStubs.add(cacheKey); - - seqId = realPersistenceManager.getSeqIdAfterSkipping(topic, seqId, 1); - } - - // so how many did we decide to readahead - if (i == 0) { - // no readahead, hence return false - return null; - } - - long readAheadSizeLimit = cfg.getReadAheadSizeBytes(); - ReadAheadScanCallback callback = new ReadAheadScanCallback(installedStubs, topic); - RangeScanRequest rangeScanRequest = new RangeScanRequest(topic, startSeqId, i, readAheadSizeLimit, callback, - null); - - return rangeScanRequest; - - } - - /** - * This is the callback that is used for the range scans. - */ - protected class ReadAheadScanCallback implements ScanCallback { - Queue installedStubs; - ByteString topic; - - /** - * Constructor - * - * @param installedStubs - * The list of stubs that were installed for this range scan - * @param topic - */ - public ReadAheadScanCallback(Queue installedStubs, ByteString topic) { - this.installedStubs = installedStubs; - this.topic = topic; - } - - public void messageScanned(Object ctx, Message message) { - - // Any message we read is potentially useful for us, so lets first - // enqueue it - CacheKey cacheKey = new CacheKey(topic, message.getMsgId().getLocalComponent()); - enqueueWithoutFailure(new ScanResponse(cacheKey, message)); - - // Now lets see if this message is the one we were expecting - CacheKey expectedKey = installedStubs.peek(); - - if (expectedKey == null) { - // Was not expecting any more messages to come in, but they came - // in so we will keep them - return; - } - - if (expectedKey.equals(cacheKey)) { - // what we got is what we expected, dequeue it so we get the - // next expected one - installedStubs.poll(); - return; - } - - // If reached here, what we scanned was not what we were expecting. - // This means that we have wrong stubs installed in the cache. We - // should remove them, so that whoever is waiting on them can retry. - // This shouldn't be happening usually - logger.warn("Unexpected message seq-id: " + message.getMsgId().getLocalComponent() + " on topic: " - + topic.toStringUtf8() + " from readahead scan, was expecting seq-id: " + expectedKey.seqId - + " topic: " + expectedKey.topic.toStringUtf8() + " installedStubs: " + installedStubs); - enqueueDeleteOfRemainingStubs(noSuchSeqIdExceptionInstance); - - } - - public void scanFailed(Object ctx, Exception exception) { - enqueueDeleteOfRemainingStubs(exception); - } - - public void scanFinished(Object ctx, ReasonForFinish reason) { - // If the scan finished because no more messages are present, its ok - // to leave the stubs in place because they will get filled in as - // new publishes happen. However, if the scan finished due to some - // other reason, e.g., read ahead size limit was reached, we want to - // delete the stubs, so that when the time comes, we can schedule - // another readahead request. - if (reason != ReasonForFinish.NO_MORE_MESSAGES) { - enqueueDeleteOfRemainingStubs(readAheadExceptionInstance); - } - } - - private void enqueueDeleteOfRemainingStubs(Exception reason) { - CacheKey installedStub; - while ((installedStub = installedStubs.poll()) != null) { - enqueueWithoutFailure(new ExceptionOnCacheKey(installedStub, reason)); - } - } - } - - protected static class HashSetCacheKeyFactory implements Factory> { - protected static HashSetCacheKeyFactory instance = new HashSetCacheKeyFactory(); - - public Set newInstance() { - return new HashSet(); - } - } - - protected static class TreeSetLongFactory implements Factory> { - protected static TreeSetLongFactory instance = new TreeSetLongFactory(); - - public SortedSet newInstance() { - return new TreeSet(); - } - } - - /** - * For adding the message to the cache, we do some bookeeping such as the - * total size of cache, order in which entries were added etc. If the size - * of the cache has exceeded our budget, old entries are collected. - * - * @param cacheKey - * @param message - */ - protected void addMessageToCache(CacheKey cacheKey, Message message, long currTime) { - if (logger.isDebugEnabled()) { - logger.debug("Adding msg (topic: " + cacheKey.getTopic().toStringUtf8() + ", seq-id: " - + message.getMsgId().getLocalComponent() + ") to readahead cache"); - } - - CacheValue cacheValue; - - if ((cacheValue = cache.get(cacheKey)) == null) { - cacheValue = new CacheValue(); - cache.put(cacheKey, cacheValue); - } - - // update the cache size - presentCacheSize += message.getBody().size(); - - // maintain the time index of addition - MapMethods.addToMultiMap(timeIndexOfAddition, currTime, cacheKey, HashSetCacheKeyFactory.instance); - - // maintain the index of seq-id - MapMethods.addToMultiMap(orderedIndexOnSeqId, cacheKey.getTopic(), cacheKey.getSeqId(), - TreeSetLongFactory.instance); - - // finally add the message to the cache - cacheValue.setMessageAndInvokeCallbacks(message, currTime); - - // if overgrown, collect old entries - collectOldCacheEntries(); - } - - protected void removeMessageFromCache(CacheKey cacheKey, Exception exception, boolean maintainTimeIndex, - boolean maintainSeqIdIndex) { - CacheValue cacheValue = cache.remove(cacheKey); - - if (cacheValue == null) { - return; - } - - if (cacheValue.isStub()) { - cacheValue.setErrorAndInvokeCallbacks(exception); - // Stubs are not present in the indexes, so dont need to maintain - // indexes here - return; - } - - presentCacheSize -= cacheValue.getMessage().getBody().size(); - - // maintain the 2 indexes - // TODO: can we maintain these lazily? - if (maintainSeqIdIndex) { - MapMethods.removeFromMultiMap(orderedIndexOnSeqId, cacheKey.getTopic(), cacheKey.getSeqId()); - } - if (maintainTimeIndex) { - MapMethods.removeFromMultiMap(timeIndexOfAddition, cacheValue.getTimeOfAddition(), cacheKey); - } - } - - /** - * Collection of old entries is simple. Just collect in insert-time order, - * oldest to newest. - */ - protected void collectOldCacheEntries() { - long maxCacheSize = cfg.getMaximumCacheSize(); - - while (presentCacheSize > maxCacheSize && !timeIndexOfAddition.isEmpty()) { - Long earliestTime = timeIndexOfAddition.firstKey(); - Set oldCacheEntries = timeIndexOfAddition.get(earliestTime); - - // Note: only concrete cache entries, and not stubs are in the time - // index. Hence there can be no callbacks pending on these cache - // entries. Hence safe to remove them directly. - for (Iterator iter = oldCacheEntries.iterator(); iter.hasNext();) { - CacheKey cacheKey = iter.next(); - - if (logger.isDebugEnabled()) { - logger.debug("Removing topic: " + cacheKey.getTopic() + "seq-id: " + cacheKey.getSeqId() - + " from cache because its the oldest"); - } - removeMessageFromCache(cacheKey, readAheadExceptionInstance, // - // maintainTimeIndex= - false, - // maintainSeqIdIndex= - true); - } - - timeIndexOfAddition.remove(earliestTime); - - } - } - - /** - * ======================================================================== - * The rest is just simple wrapper classes. - * - */ - - protected class ExceptionOnCacheKey implements CacheRequest { - CacheKey cacheKey; - Exception exception; - - public ExceptionOnCacheKey(CacheKey cacheKey, Exception exception) { - this.cacheKey = cacheKey; - this.exception = exception; - } - - /** - * If for some reason, an outstanding read on a cache stub fails, - * exception for that key is enqueued by the - * {@link ReadAheadScanCallback}. To handle this, we simply send error - * on the callbacks registered for that stub, and delete the entry from - * the cache - */ - public void performRequest() { - removeMessageFromCache(cacheKey, exception, - // maintainTimeIndex= - true, - // maintainSeqIdIndex= - true); - } - - } - - @SuppressWarnings("serial") - protected static class NoSuchSeqIdException extends Exception { - - public NoSuchSeqIdException() { - super("No such seq-id"); - } - } - - @SuppressWarnings("serial") - protected static class ReadAheadException extends Exception { - public ReadAheadException() { - super("Readahead failed"); - } - } - - protected class ScanResponse implements CacheRequest { - CacheKey cacheKey; - Message message; - - public ScanResponse(CacheKey cacheKey, Message message) { - this.cacheKey = cacheKey; - this.message = message; - } - - public void performRequest() { - addMessageToCache(cacheKey, message, System.currentTimeMillis()); - } - - } - - protected class DeliveredUntil implements CacheRequest { - ByteString topic; - Long seqId; - - public DeliveredUntil(ByteString topic, Long seqId) { - this.topic = topic; - this.seqId = seqId; - } - - public void performRequest() { - SortedSet orderedSeqIds = orderedIndexOnSeqId.get(topic); - if (orderedSeqIds == null) { - return; - } - - // focus on the set of messages with seq-ids <= the one that - // has been delivered until - SortedSet headSet = orderedSeqIds.headSet(seqId + 1); - - for (Iterator iter = headSet.iterator(); iter.hasNext();) { - Long seqId = iter.next(); - CacheKey cacheKey = new CacheKey(topic, seqId); - - if (logger.isDebugEnabled()) { - logger.debug("Removing seq-id: " + cacheKey.getSeqId() + " topic: " - + cacheKey.getTopic().toStringUtf8() - + " from cache because every subscriber has moved past"); - } - - removeMessageFromCache(cacheKey, readAheadExceptionInstance, // - // maintainTimeIndex= - true, - // maintainSeqIdIndex= - false); - iter.remove(); - } - - if (orderedSeqIds.isEmpty()) { - orderedIndexOnSeqId.remove(topic); - } - } - } - - protected class ScanRequestWrapper implements CacheRequest { - ScanRequest request; - - public ScanRequestWrapper(ScanRequest request) { - this.request = request; - } - - /** - * To handle a scan request, we first try to do readahead (which might - * cause a range read to be issued to the underlying persistence - * manager). The readahead will put a stub in the cache, if the message - * is not already present in the cache. The scan callback that is part - * of the scan request is added to this stub, and will be called later - * when the message arrives as a result of the range scan issued to the - * underlying persistence manager. - */ - - public void performRequest() { - - RangeScanRequest readAheadRequest = doReadAhead(request); - - // Read ahead must have installed at least a stub for us, so this - // can't be null - CacheValue cacheValue = cache.get(new CacheKey(request.getTopic(), request.getStartSeqId())); - - // Add our callback to the stub. If the cache value was already a - // concrete message, the callback will be called right away - cacheValue.addCallback(request.getCallback(), request.getCtx()); - - if (readAheadRequest != null) { - realPersistenceManager.scanMessages(readAheadRequest); - } - } - } - - protected class ShutdownCacheRequest implements CacheRequest { - // This is a simple type of CacheRequest we will enqueue when - // the PubSubServer is shut down and we want to stop the ReadAheadCache - // thread. - public void performRequest() { - keepRunning = false; - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallback.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallback.java deleted file mode 100644 index 8c171d73dfa..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallback.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -public interface ScanCallback { - - enum ReasonForFinish { - NO_MORE_MESSAGES, SIZE_LIMIT_EXCEEDED, NUM_MESSAGES_LIMIT_EXCEEDED - }; - - /** - * This method is called when a message is read from the persistence layer - * as part of a scan. The message just read is handed to this listener which - * can then take the desired action on it. The return value from the method - * indicates whether the scan should continue or not. - * - * @param ctx - * The context for the callback - * @param message - * The message just scanned from the log - * @return true if the scan should continue, false otherwise - */ - public void messageScanned(Object ctx, Message message); - - /** - * This method is called when the scan finishes - * - * - * @param ctx - * @param reason - */ - - public abstract void scanFinished(Object ctx, ReasonForFinish reason); - - /** - * This method is called when the operation failed due to some reason. The - * reason for failure is passed in. - * - * @param ctx - * The context for the callback - * @param exception - * The reason for the failure of the scan - */ - public abstract void scanFailed(Object ctx, Exception exception); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallbackWithContext.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallbackWithContext.java deleted file mode 100644 index df31d818c85..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanCallbackWithContext.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -public class ScanCallbackWithContext { - ScanCallback scanCallback; - Object ctx; - - public ScanCallbackWithContext(ScanCallback callback, Object ctx) { - this.scanCallback = callback; - this.ctx = ctx; - } - - public ScanCallback getScanCallback() { - return scanCallback; - } - - public Object getCtx() { - return ctx; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanRequest.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanRequest.java deleted file mode 100644 index 792bfe1b899..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/persistence/ScanRequest.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -/** - * Encapsulates a request for reading a single message. The message on the given - * topic at the given seqId is scanned. A call-back {@link ScanCallback} - * is provided. When the message is scanned, the - * {@link ScanCallback#messageScanned(Object, Message)} method is called. Since - * there is only 1 record to be scanned the - * {@link ScanCallback#operationFinished(Object)} method may not be called since - * its redundant. - * {@link ScanCallback#scanFailed(Object, org.apache.hedwig.exceptions.PubSubException)} - * method is called in case of error. - * - */ -public class ScanRequest { - ByteString topic; - long startSeqId; - ScanCallback callback; - Object ctx; - - public ScanRequest(ByteString topic, long startSeqId, ScanCallback callback, Object ctx) { - this.topic = topic; - this.startSeqId = startSeqId; - this.callback = callback; - this.ctx = ctx; - } - - public ByteString getTopic() { - return topic; - } - - public long getStartSeqId() { - return startSeqId; - } - - public ScanCallback getCallback() { - return callback; - } - - public Object getCtx() { - return ctx; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ChannelTracker.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ChannelTracker.java deleted file mode 100644 index 8d2be4e446a..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ChannelTracker.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - -import org.jboss.netty.channel.Channel; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.TopicBusyException; -import org.apache.hedwig.server.handlers.ChannelDisconnectListener; -import org.apache.hedwig.util.Callback; - -public class ChannelTracker implements ChannelDisconnectListener { - HashMap topicSub2Channel = new HashMap(); - HashMap> channel2TopicSubs = new HashMap>(); - Subscriber subscriber; - - public ChannelTracker(Subscriber subscriber) { - this.subscriber = subscriber; - } - - static Callback noOpCallback = new Callback() { - public void operationFailed(Object ctx, PubSubException exception) { - }; - - public void operationFinished(Object ctx, Void resultOfOperation) { - }; - }; - - public synchronized void channelDisconnected(Channel channel) { - List topicSubs = channel2TopicSubs.remove(channel); - - if (topicSubs == null) { - return; - } - - for (TopicSubscriber topicSub : topicSubs) { - topicSub2Channel.remove(topicSub); - subscriber.asyncCloseSubscription(topicSub.getTopic(), topicSub.getSubscriberId(), noOpCallback, null); - } - } - - public synchronized void subscribeSucceeded(TopicSubscriber topicSubscriber, Channel channel) - throws TopicBusyException { - - if (!channel.isConnected()) { - // channel got disconnected while we were processing the - // subscribe request, nothing much we can do in this case - return; - } - - if (topicSub2Channel.containsKey(topicSubscriber)) { - TopicBusyException pse = new PubSubException.TopicBusyException( - "subscription for this topic, subscriberId is already being served on a different channel"); - throw pse; - } - - topicSub2Channel.put(topicSubscriber, channel); - - List topicSubs = channel2TopicSubs.get(channel); - - if (topicSubs == null) { - topicSubs = new LinkedList(); - channel2TopicSubs.put(channel, topicSubs); - } - topicSubs.add(topicSubscriber); - - } - - public synchronized void aboutToUnsubscribe(ByteString topic, ByteString subscriberId) { - TopicSubscriber topicSub = new TopicSubscriber(topic, subscriberId); - - Channel channel = topicSub2Channel.remove(topicSub); - - if (channel != null) { - List topicSubs = channel2TopicSubs.get(channel); - if (topicSubs != null) { - topicSubs.remove(topicSub); - } - } - } - - public synchronized void checkChannelMatches(ByteString topic, ByteString subscriberId, Channel channel) - throws PubSubException { - Channel subscribedChannel = getChannel(topic, subscriberId); - - if (subscribedChannel == null) { - throw new PubSubException.ClientNotSubscribedException( - "Can't start delivery since client is not subscribed"); - } - - if (subscribedChannel != channel) { - throw new PubSubException.TopicBusyException( - "Can't start delivery since client is subscribed on a different channel"); - } - - } - - public synchronized Channel getChannel(ByteString topic, ByteString subscriberId) { - return topicSub2Channel.get(new TopicSubscriber(topic, subscriberId)); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/HedwigProxy.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/HedwigProxy.java deleted file mode 100644 index cafe6e6d286..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/HedwigProxy.java +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import java.io.File; -import java.lang.Thread.UncaughtExceptionHandler; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; -import org.jboss.netty.bootstrap.ServerBootstrap; -import org.jboss.netty.channel.group.ChannelGroup; -import org.jboss.netty.channel.group.DefaultChannelGroup; -import org.jboss.netty.channel.socket.ServerSocketChannelFactory; -import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; -import org.jboss.netty.logging.InternalLoggerFactory; -import org.jboss.netty.logging.Log4JLoggerFactory; - -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.server.common.TerminateJVMExceptionHandler; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.PubSubServer; -import org.apache.hedwig.server.netty.PubSubServerPipelineFactory; -import org.apache.hedwig.server.netty.UmbrellaHandler; - -public class HedwigProxy { - static final Logger logger = Logger.getLogger(HedwigProxy.class); - - HedwigClient client; - ServerSocketChannelFactory serverSocketChannelFactory; - ChannelGroup allChannels; - Map handlers; - ProxyConfiguration cfg; - - public HedwigProxy(final ProxyConfiguration cfg, final UncaughtExceptionHandler exceptionHandler) - throws InterruptedException { - this.cfg = cfg; - - ThreadGroup tg = new ThreadGroup("hedwigproxy") { - @Override - public void uncaughtException(Thread t, Throwable e) { - exceptionHandler.uncaughtException(t, e); - } - }; - - final LinkedBlockingQueue queue = new LinkedBlockingQueue(); - - new Thread(tg, new Runnable() { - @Override - public void run() { - client = new HedwigClient(cfg); - - serverSocketChannelFactory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), - Executors.newCachedThreadPool()); - initializeHandlers(); - initializeNetty(); - - queue.offer(true); - } - }).start(); - - queue.take(); - } - - public HedwigProxy(ProxyConfiguration conf) throws InterruptedException { - this(conf, new TerminateJVMExceptionHandler()); - } - - protected void initializeHandlers() { - handlers = new HashMap(); - ChannelTracker tracker = new ChannelTracker(client.getSubscriber()); - - handlers.put(OperationType.PUBLISH, new ProxyPublishHander(client.getPublisher())); - handlers.put(OperationType.SUBSCRIBE, new ProxySubscribeHandler(client.getSubscriber(), tracker)); - handlers.put(OperationType.UNSUBSCRIBE, new ProxyUnsubscribeHandler(client.getSubscriber(), tracker)); - handlers.put(OperationType.CONSUME, new ProxyConsumeHandler(client.getSubscriber())); - handlers.put(OperationType.STOP_DELIVERY, new ProxyStopDeliveryHandler(client.getSubscriber(), tracker)); - handlers.put(OperationType.START_DELIVERY, new ProxyStartDeliveryHandler(client.getSubscriber(), tracker)); - - } - - protected void initializeNetty() { - InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); - allChannels = new DefaultChannelGroup("hedwigproxy"); - ServerBootstrap bootstrap = new ServerBootstrap(serverSocketChannelFactory); - UmbrellaHandler umbrellaHandler = new UmbrellaHandler(allChannels, handlers, false); - PubSubServerPipelineFactory pipeline = new PubSubServerPipelineFactory(umbrellaHandler, null, cfg - .getMaximumMessageSize()); - - bootstrap.setPipelineFactory(pipeline); - bootstrap.setOption("child.tcpNoDelay", true); - bootstrap.setOption("child.keepAlive", true); - bootstrap.setOption("reuseAddress", true); - - // Bind and start to accept incoming connections. - allChannels.add(bootstrap.bind(new InetSocketAddress(cfg.getProxyPort()))); - logger.info("Going into receive loop"); - } - - public void shutdown() { - allChannels.close().awaitUninterruptibly(); - client.stop(); - serverSocketChannelFactory.releaseExternalResources(); - } - - // the following method only exists for unit-testing purposes, should go - // away once we make start delivery totally server-side - public Handler getStartDeliveryHandler(){ - return handlers.get(OperationType.START_DELIVERY); - } - - /** - * @param args - */ - public static void main(String[] args) { - - logger.info("Attempting to start Hedwig Proxy"); - ProxyConfiguration conf = new ProxyConfiguration(); - if (args.length > 0) { - String confFile = args[0]; - try { - conf.loadConf(new File(confFile).toURI().toURL()); - } catch (MalformedURLException e) { - String msg = "Could not open configuration file: " + confFile; - PubSubServer.errorMsgAndExit(msg, e, PubSubServer.RC_INVALID_CONF_FILE); - } catch (ConfigurationException e) { - String msg = "Malformed configuration file: " + confFile; - PubSubServer.errorMsgAndExit(msg, e, PubSubServer.RC_MISCONFIGURED); - } - logger.info("Using configuration file " + confFile); - } - try { - new HedwigProxy(conf); - } catch (Throwable t) { - PubSubServer.errorMsgAndExit("Error during startup", t, PubSubServer.RC_OTHER); - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConfiguration.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConfiguration.java deleted file mode 100644 index aca54d7a12a..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConfiguration.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.apache.hedwig.client.conf.ClientConfiguration; - -public class ProxyConfiguration extends ClientConfiguration{ - - protected static String PROXY_PORT = "proxy_port"; - protected static String MAX_MESSAGE_SIZE = "max_message_size"; - - public int getProxyPort(){ - return conf.getInt(PROXY_PORT, 9099); - } - - @Override - public int getMaximumMessageSize() { - return conf.getInt(MAX_MESSAGE_SIZE, 1258291); /* 1.2M */ - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConsumeHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConsumeHandler.java deleted file mode 100644 index 8bbd68ad1b5..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyConsumeHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.protocol.PubSubProtocol.ConsumeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.UmbrellaHandler; - -public class ProxyConsumeHandler implements Handler { - - static final Logger logger = Logger.getLogger(ProxyConsumeHandler.class); - - Subscriber subscriber; - - public ProxyConsumeHandler(Subscriber subscriber) { - this.subscriber = subscriber; - } - - @Override - public void handleRequest(PubSubRequest request, Channel channel) { - if (!request.hasConsumeRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing consume request data"); - return; - } - - ConsumeRequest consumeRequest = request.getConsumeRequest(); - try { - subscriber.consume(request.getTopic(), consumeRequest.getSubscriberId(), consumeRequest.getMsgId()); - } catch (ClientNotSubscribedException e) { - // ignore - logger.warn("Unexpected consume request", e); - } - - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyPublishHander.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyPublishHander.java deleted file mode 100644 index 7ffdb92492b..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyPublishHander.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.jboss.netty.channel.Channel; - -import org.apache.hedwig.client.api.Publisher; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PublishRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.util.Callback; - -public class ProxyPublishHander implements Handler { - Publisher publisher; - - public ProxyPublishHander(Publisher publisher) { - this.publisher = publisher; - } - - @Override - public void handleRequest(final PubSubRequest request, final Channel channel) { - if (!request.hasPublishRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing publish request data"); - return; - } - - final PublishRequest publishRequest = request.getPublishRequest(); - - publisher.asyncPublish(request.getTopic(), publishRequest.getMsg(), new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - } - }, null); - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStartDeliveryHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStartDeliveryHandler.java deleted file mode 100644 index bdb82fcde71..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStartDeliveryHandler.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.util.Callback; - -public class ProxyStartDeliveryHandler implements Handler { - - static final Logger logger = Logger.getLogger(ProxyStartDeliveryHandler.class); - - Subscriber subscriber; - ChannelTracker tracker; - - public ProxyStartDeliveryHandler(Subscriber subscriber, ChannelTracker tracker) { - this.subscriber = subscriber; - this.tracker = tracker; - } - - @Override - public void handleRequest(PubSubRequest request, Channel channel) { - - if (!request.hasStartDeliveryRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing start delivery request data"); - return; - } - - final ByteString topic = request.getTopic(); - final ByteString subscriberId = request.getStartDeliveryRequest().getSubscriberId(); - - synchronized (tracker) { - // try { - // tracker.checkChannelMatches(topic, subscriberId, channel); - // } catch (PubSubException e) { - // channel.write(PubSubResponseUtils.getResponseForException(e, - // request.getTxnId())); - // return; - // } - - final Channel subscribedChannel = tracker.getChannel(topic, subscriberId); - - if (subscribedChannel == null) { - channel.write(PubSubResponseUtils.getResponseForException( - new PubSubException.ClientNotSubscribedException("no subscription to start delivery on"), - request.getTxnId())); - return; - } - - MessageHandler handler = new MessageHandler() { - @Override - public void consume(ByteString topic, ByteString subscriberId, Message msg, - final Callback callback, final Object context) { - - PubSubResponse response = PubSubResponse.newBuilder().setProtocolVersion( - ProtocolVersion.VERSION_ONE).setStatusCode(StatusCode.SUCCESS).setTxnId(0).setMessage(msg) - .setTopic(topic).setSubscriberId(subscriberId).build(); - - ChannelFuture future = subscribedChannel.write(response); - - future.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - // ignoring this failure, because this will - // only happen due to channel disconnect. - // Channel disconnect will in turn stop - // delivery, and stop these errors - return; - } - - // Tell the hedwig client, that it can send me - // more messages - callback.operationFinished(context, null); - } - }); - } - }; - - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - - try { - subscriber.startDelivery(topic, subscriberId, handler); - } catch (ClientNotSubscribedException e) { - // This should not happen, since we already checked the correct - // channel and so on - logger.fatal("Unexpected: No subscription when attempting to start delivery", e); - throw new RuntimeException(e); - } - - - - } - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStopDeliveryHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStopDeliveryHandler.java deleted file mode 100644 index 6f5eb208435..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyStopDeliveryHandler.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.UmbrellaHandler; - -public class ProxyStopDeliveryHandler implements Handler { - - static final Logger logger = Logger.getLogger(ProxyStopDeliveryHandler.class); - - Subscriber subscriber; - ChannelTracker tracker; - - public ProxyStopDeliveryHandler(Subscriber subscriber, ChannelTracker tracker) { - this.subscriber = subscriber; - this.tracker = tracker; - } - - @Override - public void handleRequest(PubSubRequest request, Channel channel) { - if (!request.hasStopDeliveryRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing stop delivery request data"); - return; - } - - final ByteString topic = request.getTopic(); - final ByteString subscriberId = request.getStartDeliveryRequest().getSubscriberId(); - - synchronized (tracker) { - try { - tracker.checkChannelMatches(topic, subscriberId, channel); - } catch (PubSubException e) { - // intentionally ignore this error, since stop delivery doesn't - // send back a response - return; - } - - try { - subscriber.stopDelivery(topic, subscriberId); - } catch (ClientNotSubscribedException e) { - // This should not happen, since we already checked the correct - // channel and so on - logger.warn("Unexpected: No subscription when attempting to stop delivery", e); - } - } - - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxySubscribeHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxySubscribeHandler.java deleted file mode 100644 index 178c7fa5973..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxySubscribeHandler.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.apache.log4j.Logger; -import org.jboss.netty.channel.Channel; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.TopicBusyException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.handlers.ChannelDisconnectListener; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.util.Callback; - -public class ProxySubscribeHandler implements Handler, ChannelDisconnectListener { - - static final Logger logger = Logger.getLogger(ProxySubscribeHandler.class); - - Subscriber subscriber; - ChannelTracker tracker; - - public ProxySubscribeHandler(Subscriber subscriber, ChannelTracker tracker) { - this.subscriber = subscriber; - this.tracker = tracker; - } - - @Override - public void channelDisconnected(Channel channel) { - tracker.channelDisconnected(channel); - } - - @Override - public void handleRequest(final PubSubRequest request, final Channel channel) { - if (!request.hasSubscribeRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing subscribe request data"); - return; - } - - SubscribeRequest subRequest = request.getSubscribeRequest(); - final TopicSubscriber topicSubscriber = new TopicSubscriber(request.getTopic(), subRequest.getSubscriberId()); - - subscriber.asyncSubscribe(topicSubscriber.getTopic(), subRequest.getSubscriberId(), subRequest - .getCreateOrAttach(), new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - try { - tracker.subscribeSucceeded(topicSubscriber, channel); - } catch (TopicBusyException e) { - channel.write(PubSubResponseUtils.getResponseForException(e, request.getTxnId())); - return; - } - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - } - }, null); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyUnsubscribeHandler.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyUnsubscribeHandler.java deleted file mode 100644 index f6119056d87..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/proxy/ProxyUnsubscribeHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.proxy; - -import org.jboss.netty.channel.Channel; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protoextensions.PubSubResponseUtils; -import org.apache.hedwig.server.handlers.Handler; -import org.apache.hedwig.server.netty.UmbrellaHandler; -import org.apache.hedwig.util.Callback; - -public class ProxyUnsubscribeHandler implements Handler { - - Subscriber subscriber; - ChannelTracker tracker; - - public ProxyUnsubscribeHandler(Subscriber subscriber, ChannelTracker tracker) { - this.subscriber = subscriber; - this.tracker = tracker; - } - - @Override - public void handleRequest(final PubSubRequest request, final Channel channel) { - if (!request.hasUnsubscribeRequest()) { - UmbrellaHandler.sendErrorResponseToMalformedRequest(channel, request.getTxnId(), - "Missing unsubscribe request data"); - return; - } - - ByteString topic = request.getTopic(); - ByteString subscriberId = request.getUnsubscribeRequest().getSubscriberId(); - - synchronized (tracker) { - - // Even if unsubscribe fails, the hedwig client closes the channel - // on which the subscription is being served. Hence better to tell - // the tracker beforehand that this subscription is no longer served - tracker.aboutToUnsubscribe(topic, subscriberId); - - subscriber.asyncUnsubscribe(topic, subscriberId, new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - channel.write(PubSubResponseUtils.getResponseForException(exception, request.getTxnId())); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - channel.write(PubSubResponseUtils.getSuccessResponse(request.getTxnId())); - } - }, null); - } - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClient.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClient.java deleted file mode 100644 index 0289c1685d0..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClient.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.regions; - -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; - -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.netty.HedwigClient; - -/** - * This is a hub specific implementation of the HedwigClient. All this does - * though is to override the HedwigSubscriber with the hub specific child class. - * Creating this class so we can call the protected method in the parent to set - * the subscriber since we don't want to expose that API to the public. - */ -public class HedwigHubClient extends HedwigClient { - - // Constructor when we already have a ChannelFactory instantiated. - public HedwigHubClient(ClientConfiguration cfg, ClientSocketChannelFactory channelFactory) { - super(cfg, channelFactory); - // Override the type of HedwigSubscriber with the hub specific one. - setSubscriber(new HedwigHubSubscriber(this)); - } - - // Constructor when we don't have a ChannelFactory. The super constructor - // will create one for us. - public HedwigHubClient(ClientConfiguration cfg) { - super(cfg); - // Override the type of HedwigSubscriber with the hub specific one. - setSubscriber(new HedwigHubSubscriber(this)); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClientFactory.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClientFactory.java deleted file mode 100644 index 9e4f61b12da..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubClientFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.regions; - -import org.jboss.netty.channel.socket.ClientSocketChannelFactory; - -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class HedwigHubClientFactory { - - private final ServerConfiguration cfg; - private final ClientSocketChannelFactory channelFactory; - - // Constructor that takes in a ServerConfiguration and a ChannelFactory - // so we can reuse it for all Clients created here. - public HedwigHubClientFactory(ServerConfiguration cfg, ClientSocketChannelFactory channelFactory) { - this.cfg = cfg; - this.channelFactory = channelFactory; - } - - /** - * Manufacture a hub client whose default server to connect to is the input - * HedwigSocketAddress hub. - * - * @param hub - * The hub in another region to connect to. - */ - HedwigHubClient create(final HedwigSocketAddress hub) { - // Create a hub specific version of the client to use - return new HedwigHubClient(new ClientConfiguration() { - @Override - protected HedwigSocketAddress getDefaultServerHedwigSocketAddress() { - return hub; - } - - @Override - public boolean isSSLEnabled() { - return cfg.isInterRegionSSLEnabled(); - } - }, channelFactory); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubSubscriber.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubSubscriber.java deleted file mode 100644 index c4047dcf0eb..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/HedwigHubSubscriber.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.regions; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.exceptions.InvalidSubscriberIdException; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigSubscriber; -import org.apache.hedwig.exceptions.PubSubException.ClientAlreadySubscribedException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.exceptions.PubSubException.CouldNotConnectException; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.util.Callback; - -/** - * This is a hub specific child class of the HedwigSubscriber. The main thing is - * does is wrap the public subscribe/unsubscribe methods by calling the - * overloaded protected ones passing in a true value for the input boolean - * parameter isHub. That will just make sure we validate the subscriberId - * passed, ensuring it is of the right format either for a local or hub - * subscriber. - */ -public class HedwigHubSubscriber extends HedwigSubscriber { - - public HedwigHubSubscriber(HedwigClient client) { - super(client); - } - - @Override - public void subscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode) - throws CouldNotConnectException, ClientAlreadySubscribedException, ServiceDownException, - InvalidSubscriberIdException { - subscribe(topic, subscriberId, mode, true); - } - - @Override - public void asyncSubscribe(ByteString topic, ByteString subscriberId, CreateOrAttach mode, Callback callback, - Object context) { - asyncSubscribe(topic, subscriberId, mode, callback, context, true); - } - - @Override - public void unsubscribe(ByteString topic, ByteString subscriberId) throws CouldNotConnectException, - ClientNotSubscribedException, ServiceDownException, InvalidSubscriberIdException { - unsubscribe(topic, subscriberId, true); - } - - @Override - public void asyncUnsubscribe(final ByteString topic, final ByteString subscriberId, final Callback callback, - final Object context) { - asyncUnsubscribe(topic, subscriberId, callback, context, true); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/RegionManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/RegionManager.java deleted file mode 100644 index 08704de9c27..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/regions/RegionManager.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.regions; - -import java.util.ArrayList; -import java.util.concurrent.ScheduledExecutorService; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.apache.zookeeper.ZooKeeper; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.netty.HedwigSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.RegionSpecificSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.protoextensions.SubscriptionStateUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.TopicOpQueuer; -import org.apache.hedwig.server.persistence.PersistRequest; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.subscriptions.SubscriptionEventListener; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.CallbackUtils; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class RegionManager implements SubscriptionEventListener { - - protected static final Logger LOGGER = Logger.getLogger(RegionManager.class); - - private final ByteString mySubId; - private final PersistenceManager pm; - private final ArrayList clients = new ArrayList(); - private final TopicOpQueuer queue; - - public RegionManager(final PersistenceManager pm, final ServerConfiguration cfg, final ZooKeeper zk, - ScheduledExecutorService scheduler, HedwigHubClientFactory hubClientFactory) { - this.pm = pm; - mySubId = ByteString.copyFromUtf8(SubscriptionStateUtils.HUB_SUBSCRIBER_PREFIX + cfg.getMyRegion()); - queue = new TopicOpQueuer(scheduler); - for (final String hub : cfg.getRegions()) { - clients.add(hubClientFactory.create(new HedwigSocketAddress(hub))); - } - } - - @Override - public void onFirstLocalSubscribe(final ByteString topic, final boolean synchronous, final Callback cb) { - // Whenever we acquire a topic due to a (local) subscribe, subscribe on - // it to all the other regions (currently using simple all-to-all - // topology). - queue.pushAndMaybeRun(topic, queue.new AsynchronousOp(topic, cb, null) { - @Override - public void run() { - Callback postCb = synchronous ? cb : CallbackUtils.logger(LOGGER, Level.DEBUG, Level.ERROR, - "all cross-region subscriptions succeeded", "at least one cross-region subscription failed"); - final Callback mcb = CallbackUtils.multiCallback(clients.size(), postCb, ctx); - for (final HedwigHubClient client : clients) { - final HedwigSubscriber sub = client.getSubscriber(); - sub.asyncSubscribe(topic, mySubId, CreateOrAttach.CREATE_OR_ATTACH, new Callback() { - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("cross-region subscription done for topic " + topic.toStringUtf8()); - try { - sub.startDelivery(topic, mySubId, new MessageHandler() { - @Override - public void consume(final ByteString topic, ByteString subscriberId, Message msg, - final Callback callback, final Object context) { - // When messages are first published - // locally, the PublishHandler sets the - // source region in the Message. - if (msg.hasSrcRegion()) { - Message.newBuilder(msg).setMsgId( - MessageSeqId.newBuilder(msg.getMsgId()).addRemoteComponents( - RegionSpecificSeqId.newBuilder().setRegion( - msg.getSrcRegion()).setSeqId( - msg.getMsgId().getLocalComponent()))); - } - pm.persistMessage(new PersistRequest(topic, msg, new Callback() { - @Override - public void operationFinished(Object ctx, Long resultOfOperation) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("cross-region recv-fwd succeeded for topic " - + topic.toStringUtf8()); - callback.operationFinished(context, null); - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - if (LOGGER.isDebugEnabled()) - LOGGER.error("cross-region recv-fwd failed for topic " - + topic.toStringUtf8(), exception); - callback.operationFailed(context, exception); - } - }, null)); - } - }); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("cross-region start-delivery succeeded for topic " - + topic.toStringUtf8()); - mcb.operationFinished(ctx, null); - } catch (PubSubException ex) { - if (LOGGER.isDebugEnabled()) - LOGGER.error( - "cross-region start-delivery failed for topic " + topic.toStringUtf8(), ex); - mcb.operationFailed(ctx, ex); - } - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - if (LOGGER.isDebugEnabled()) - LOGGER.error("cross-region subscribe failed for topic " + topic.toStringUtf8(), - exception); - mcb.operationFailed(ctx, exception); - } - }, null); - } - if (!synchronous) - cb.operationFinished(null, null); - } - }); - - } - - @Override - public void onLastLocalUnsubscribe(final ByteString topic) { - // TODO may want to ease up on the eager unsubscribe; this is dropping - // cross-region subscriptions ASAP - queue.pushAndMaybeRun(topic, queue.new AsynchronousOp(topic, new Callback() { - - @Override - public void operationFinished(Object ctx, Void result) { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("cross-region unsubscribes succeeded for topic " + topic.toStringUtf8()); - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - if (LOGGER.isDebugEnabled()) - LOGGER.error("cross-region unsubscribes failed for topic " + topic.toStringUtf8(), exception); - } - - }, null) { - @Override - public void run() { - Callback mcb = CallbackUtils.multiCallback(clients.size(), cb, ctx); - for (final HedwigHubClient client : clients) { - client.getSubscriber().asyncUnsubscribe(topic, mySubId, mcb, null); - } - } - }); - } - - // Method to shutdown and stop all of the cross-region Hedwig clients. - public void stop() { - for (HedwigHubClient client : clients) { - client.stop(); - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/ssl/SslServerContextFactory.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/ssl/SslServerContextFactory.java deleted file mode 100644 index 83d6961698d..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/ssl/SslServerContextFactory.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.ssl; - -import java.security.KeyStore; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; - -import org.apache.hedwig.client.ssl.SslContextFactory; -import org.apache.hedwig.server.common.ServerConfiguration; - -public class SslServerContextFactory extends SslContextFactory { - - public SslServerContextFactory(ServerConfiguration cfg) { - try { - // Load our Java key store. - KeyStore ks = KeyStore.getInstance("pkcs12"); - ks.load(cfg.getCertStream(), cfg.getPassword().toCharArray()); - - // Like ssh-agent. - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, cfg.getPassword().toCharArray()); - - // Create the SSL context. - ctx = SSLContext.getInstance("TLS"); - ctx.init(kmf.getKeyManagers(), getTrustManagers(), null); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - @Override - protected boolean isClient() { - return false; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AbstractSubscriptionManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AbstractSubscriptionManager.java deleted file mode 100644 index d73cf7ba47d..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AbstractSubscriptionManager.java +++ /dev/null @@ -1,450 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import java.util.ArrayList; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.log4j.Logger; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscriptionState; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.protoextensions.MessageIdUtils; -import org.apache.hedwig.protoextensions.SubscriptionStateUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.TopicOpQueuer; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.server.topics.TopicOwnershipChangeListener; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.CallbackUtils; - -public abstract class AbstractSubscriptionManager implements SubscriptionManager, TopicOwnershipChangeListener { - - ServerConfiguration cfg; - ConcurrentHashMap> top2sub2seq = new ConcurrentHashMap>(); - static Logger logger = Logger.getLogger(AbstractSubscriptionManager.class); - - TopicOpQueuer queuer; - private final ArrayList listeners = new ArrayList(); - private final ConcurrentHashMap topic2LocalCounts = new ConcurrentHashMap(); - - // Handle to the PersistenceManager for the server so we can pass along the - // message consume pointers for each topic. - private final PersistenceManager pm; - // Timer for running a recurring thread task to get the minimum message - // sequence ID for each topic that all subscribers for it have consumed - // already. With that information, we can call the PersistenceManager to - // update it on the messages that are safe to be garbage collected. - private final Timer timer = new Timer(true); - // In memory mapping of topics to the minimum consumed message sequence ID - // for all subscribers to the topic. - private final ConcurrentHashMap topic2MinConsumedMessagesMap = new ConcurrentHashMap(); - - public AbstractSubscriptionManager(ServerConfiguration cfg, TopicManager tm, PersistenceManager pm, - ScheduledExecutorService scheduler) { - this.cfg = cfg; - queuer = new TopicOpQueuer(scheduler); - tm.addTopicOwnershipChangeListener(this); - this.pm = pm; - // Schedule the recurring MessagesConsumedTask only if a - // PersistenceManager is passed. - if (pm != null) { - timer.schedule(new MessagesConsumedTask(), 0, cfg.getMessagesConsumedThreadRunInterval()); - } - } - - /** - * This is the Timer Task for finding out for each topic, what the minimum - * consumed message by the subscribers are. This information is used to pass - * along to the server's PersistenceManager so it can garbage collect older - * topic messages that are no longer needed by the subscribers. - */ - class MessagesConsumedTask extends TimerTask { - /** - * Implement the TimerTask's abstract run method. - */ - @Override - public void run() { - // We are looping through relatively small in memory data structures - // so it should be safe to run this fairly often. - for (ByteString topic : top2sub2seq.keySet()) { - final Map topicSubscriptions = top2sub2seq.get(topic); - long minConsumedMessage = Long.MAX_VALUE; - // Loop through all subscribers to the current topic to find the - // minimum consumed message id. The consume pointers are - // persisted lazily so we'll use the stale in-memory value - // instead. This keeps things consistent in case of a server - // crash. - for (InMemorySubscriptionState curSubscription : topicSubscriptions.values()) { - if (curSubscription.getSubscriptionState().getMsgId().getLocalComponent() < minConsumedMessage) - minConsumedMessage = curSubscription.getSubscriptionState().getMsgId().getLocalComponent(); - } - boolean callPersistenceManager = true; - // Don't call the PersistenceManager if nobody is subscribed to - // the topic yet, or the consume pointer has not changed since - // the last time, or if this is the initial subscription. - if (topicSubscriptions.isEmpty() - || (topic2MinConsumedMessagesMap.containsKey(topic) && topic2MinConsumedMessagesMap.get(topic) == minConsumedMessage) - || minConsumedMessage == 0) { - callPersistenceManager = false; - } - // Pass the new consume pointers to the PersistenceManager. - if (callPersistenceManager) { - topic2MinConsumedMessagesMap.put(topic, minConsumedMessage); - pm.consumedUntil(topic, minConsumedMessage); - } - } - } - } - - private class AcquireOp extends TopicOpQueuer.AsynchronousOp { - public AcquireOp(ByteString topic, Callback callback, Object ctx) { - queuer.super(topic, callback, ctx); - } - - @Override - public void run() { - if (top2sub2seq.containsKey(topic)) { - cb.operationFinished(ctx, null); - } - - readSubscriptions(topic, new Callback>() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - cb.operationFailed(ctx, exception); - } - - @Override - public void operationFinished(final Object ctx, - final Map resultOfOperation) { - // We've just inherited a bunch of subscriber for this - // topic, some of which may be local. If they are, then we - // need to (1) notify listeners of this and (2) record the - // number for bookkeeping so that future - // subscribes/unsubscribes can efficiently notify listeners. - - // Count the number of local subscribers we just inherited. - // This loop is OK since the number of subscribers per topic - // is expected to be small. - int localCount = 0; - for (ByteString subscriberId : resultOfOperation.keySet()) - if (!SubscriptionStateUtils.isHubSubscriber(subscriberId)) - localCount++; - topic2LocalCounts.put(topic, new AtomicInteger(localCount)); - - // The final "commit" (and "abort") operations. - final Callback cb2 = new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - logger.error("Subscription manager failed to acquired topic " + topic.toStringUtf8(), - exception); - cb.operationFailed(ctx, null); - } - - @Override - public void operationFinished(Object ctx, Void voidObj) { - top2sub2seq.put(topic, resultOfOperation); - logger.info("Subscription manager successfully acquired topic: " + topic.toStringUtf8()); - cb.operationFinished(ctx, null); - } - - }; - - // Notify listeners if necessary. - if (localCount > 0) { - notifySubscribe(topic, false, cb2, ctx); - } else { - cb2.operationFinished(ctx, null); - } - } - - }, ctx); - - } - - } - - private void notifySubscribe(ByteString topic, boolean synchronous, final Callback cb, final Object ctx) { - Callback mcb = CallbackUtils.multiCallback(listeners.size(), cb, ctx); - for (SubscriptionEventListener listener : listeners) { - listener.onFirstLocalSubscribe(topic, synchronous, mcb); - } - } - - /** - * Figure out who is subscribed. Do nothing if already acquired. If there's - * an error reading the subscribers' sequence IDs, then the topic is not - * acquired. - * - * @param topic - * @param callback - * @param ctx - */ - @Override - public void acquiredTopic(final ByteString topic, final Callback callback, Object ctx) { - queuer.pushAndMaybeRun(topic, new AcquireOp(topic, callback, ctx)); - } - - /** - * Remove the local mapping. - */ - @Override - public void lostTopic(ByteString topic) { - top2sub2seq.remove(topic); - // Notify listeners if necessary. - if (topic2LocalCounts.remove(topic).get() > 0) - notifyUnsubcribe(topic); - } - - private void notifyUnsubcribe(ByteString topic) { - for (SubscriptionEventListener listener : listeners) - listener.onLastLocalUnsubscribe(topic); - } - - protected abstract void readSubscriptions(final ByteString topic, - final Callback> cb, final Object ctx); - - private class SubscribeOp extends TopicOpQueuer.AsynchronousOp { - SubscribeRequest subRequest; - MessageSeqId consumeSeqId; - - public SubscribeOp(ByteString topic, SubscribeRequest subRequest, MessageSeqId consumeSeqId, - Callback callback, Object ctx) { - queuer.super(topic, callback, ctx); - this.subRequest = subRequest; - this.consumeSeqId = consumeSeqId; - } - - @Override - public void run() { - - final Map topicSubscriptions = top2sub2seq.get(topic); - if (topicSubscriptions == null) { - cb.operationFailed(ctx, new PubSubException.ServerNotResponsibleForTopicException("")); - return; - } - - final ByteString subscriberId = subRequest.getSubscriberId(); - InMemorySubscriptionState subscriptionState = topicSubscriptions.get(subscriberId); - CreateOrAttach createOrAttach = subRequest.getCreateOrAttach(); - - if (subscriptionState != null) { - - if (createOrAttach.equals(CreateOrAttach.CREATE)) { - String msg = "Topic: " + topic.toStringUtf8() + " subscriberId: " + subscriberId.toStringUtf8() - + " requested creating a subscription but it is already subscribed with state: " - + SubscriptionStateUtils.toString(subscriptionState.getSubscriptionState()); - logger.debug(msg); - cb.operationFailed(ctx, new PubSubException.ClientAlreadySubscribedException(msg)); - return; - } - - // otherwise just attach - if (logger.isDebugEnabled()) { - logger.debug("Topic: " + topic.toStringUtf8() + " subscriberId: " + subscriberId.toStringUtf8() - + " attaching to subscription with state: " - + SubscriptionStateUtils.toString(subscriptionState.getSubscriptionState())); - } - - cb.operationFinished(ctx, subscriptionState.getLastConsumeSeqId()); - return; - } - - // we don't have a mapping for this subscriber - if (createOrAttach.equals(CreateOrAttach.ATTACH)) { - String msg = "Topic: " + topic.toStringUtf8() + " subscriberId: " + subscriberId.toStringUtf8() - + " requested attaching to an existing subscription but it is not subscribed"; - logger.debug(msg); - cb.operationFailed(ctx, new PubSubException.ClientNotSubscribedException(msg)); - return; - } - - // now the hard case, this is a brand new subscription, must record - final SubscriptionState newState = SubscriptionState.newBuilder().setMsgId(consumeSeqId).build(); - createSubscriptionState(topic, subscriberId, newState, new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - cb.operationFailed(ctx, exception); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - Callback cb2 = new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - logger.error("subscription for subscriber " + subscriberId.toStringUtf8() + " to topic " - + topic.toStringUtf8() + " failed due to failed listener callback", exception); - cb.operationFailed(ctx, exception); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - topicSubscriptions.put(subscriberId, new InMemorySubscriptionState(newState)); - cb.operationFinished(ctx, consumeSeqId); - } - - }; - - if (!SubscriptionStateUtils.isHubSubscriber(subRequest.getSubscriberId()) - && topic2LocalCounts.get(topic).incrementAndGet() == 1) - notifySubscribe(topic, subRequest.getSynchronous(), cb2, ctx); - else - cb2.operationFinished(ctx, resultOfOperation); - } - }, ctx); - } - } - - @Override - public void serveSubscribeRequest(ByteString topic, SubscribeRequest subRequest, MessageSeqId consumeSeqId, - Callback callback, Object ctx) { - queuer.pushAndMaybeRun(topic, new SubscribeOp(topic, subRequest, consumeSeqId, callback, ctx)); - } - - private class ConsumeOp extends TopicOpQueuer.AsynchronousOp { - ByteString subscriberId; - MessageSeqId consumeSeqId; - - public ConsumeOp(ByteString topic, ByteString subscriberId, MessageSeqId consumeSeqId, Callback callback, - Object ctx) { - queuer.super(topic, callback, ctx); - this.subscriberId = subscriberId; - this.consumeSeqId = consumeSeqId; - } - - @Override - public void run() { - Map topicSubs = top2sub2seq.get(topic); - if (topicSubs == null) { - cb.operationFinished(ctx, null); - return; - } - - InMemorySubscriptionState subState = topicSubs.get(subscriberId); - if (subState == null) { - cb.operationFinished(ctx, null); - return; - } - - if (subState.setLastConsumeSeqId(consumeSeqId, cfg.getConsumeInterval())) { - updateSubscriptionState(topic, subscriberId, subState.getSubscriptionState(), cb, ctx); - } else { - if (logger.isDebugEnabled()) { - logger.debug("Only advanced consume pointer in memory, will persist later, topic: " - + topic.toStringUtf8() + " subscriberId: " + subscriberId.toStringUtf8() - + " persistentState: " + SubscriptionStateUtils.toString(subState.getSubscriptionState()) - + " in-memory consume-id: " - + MessageIdUtils.msgIdToReadableString(subState.getLastConsumeSeqId())); - } - cb.operationFinished(ctx, null); - } - - } - } - - @Override - public void setConsumeSeqIdForSubscriber(ByteString topic, ByteString subscriberId, MessageSeqId consumeSeqId, - Callback callback, Object ctx) { - queuer.pushAndMaybeRun(topic, new ConsumeOp(topic, subscriberId, consumeSeqId, callback, ctx)); - } - - private class UnsubscribeOp extends TopicOpQueuer.AsynchronousOp { - ByteString subscriberId; - - public UnsubscribeOp(ByteString topic, ByteString subscriberId, Callback callback, Object ctx) { - queuer.super(topic, callback, ctx); - this.subscriberId = subscriberId; - } - - @Override - public void run() { - final Map topicSubscriptions = top2sub2seq.get(topic); - if (topicSubscriptions == null) { - cb.operationFailed(ctx, new PubSubException.ServerNotResponsibleForTopicException("")); - return; - } - - if (!topicSubscriptions.containsKey(subscriberId)) { - cb.operationFailed(ctx, new PubSubException.ClientNotSubscribedException("")); - return; - } - - deleteSubscriptionState(topic, subscriberId, new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - cb.operationFailed(ctx, exception); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - topicSubscriptions.remove(subscriberId); - // Notify listeners if necessary. - if (!SubscriptionStateUtils.isHubSubscriber(subscriberId) - && topic2LocalCounts.get(topic).decrementAndGet() == 0) - notifyUnsubcribe(topic); - cb.operationFinished(ctx, null); - } - }, ctx); - - } - - } - - @Override - public void unsubscribe(ByteString topic, ByteString subscriberId, Callback callback, Object ctx) { - queuer.pushAndMaybeRun(topic, new UnsubscribeOp(topic, subscriberId, callback, ctx)); - } - - /** - * Not thread-safe. - */ - @Override - public void addListener(SubscriptionEventListener listener) { - listeners.add(listener); - } - - /** - * Method to stop this class gracefully including releasing any resources - * used and stopping all threads spawned. - */ - public void stop() { - timer.cancel(); - } - - protected abstract void createSubscriptionState(final ByteString topic, ByteString subscriberId, - SubscriptionState state, Callback callback, Object ctx); - - protected abstract void updateSubscriptionState(ByteString topic, ByteString subscriberId, SubscriptionState state, - Callback callback, Object ctx); - - protected abstract void deleteSubscriptionState(ByteString topic, ByteString subscriberId, Callback callback, - Object ctx); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AllToAllTopologyFilter.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AllToAllTopologyFilter.java deleted file mode 100644 index acdbf050f86..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/AllToAllTopologyFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -public class AllToAllTopologyFilter implements MessageFilter { - - ByteString subscriberRegion; - - public AllToAllTopologyFilter(ByteString subscriberRegion) { - this.subscriberRegion = subscriberRegion; - } - - public boolean testMessage(Message message) { - if (message.getSrcRegion().equals(subscriberRegion)) { - return false; - } else { - return true; - } - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionManager.java deleted file mode 100644 index 4a2439537bd..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionManager.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.SubscriptionState; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; - -public class InMemorySubscriptionManager extends AbstractSubscriptionManager { - - public InMemorySubscriptionManager(TopicManager tm, PersistenceManager pm, ServerConfiguration conf, ScheduledExecutorService scheduler) { - super(conf, tm, pm, scheduler); - } - - @Override - protected void createSubscriptionState(ByteString topic, ByteString subscriberId, SubscriptionState state, - Callback callback, Object ctx) { - // nothing to do, in-memory info is already recorded by base class - callback.operationFinished(ctx, null); - } - - @Override - protected void deleteSubscriptionState(ByteString topic, ByteString subscriberId, Callback callback, - Object ctx) { - // nothing to do, in-memory info is already deleted by base class - callback.operationFinished(ctx, null); - } - - @Override - protected void updateSubscriptionState(ByteString topic, ByteString subscriberId, SubscriptionState state, - Callback callback, Object ctx) { - // nothing to do, in-memory info is already updated by base class - callback.operationFinished(ctx, null); - } - - @Override - public void lostTopic(ByteString topic) { - // Intentionally do nothing, so that we dont lose in-memory information - } - - @Override - protected void readSubscriptions(ByteString topic, - Callback> cb, Object ctx) { - // Since we don't lose in-memory information on lostTopic, we can just - // return that back - Map topicSubs = top2sub2seq.get(topic); - - if (topicSubs != null) { - cb.operationFinished(ctx, topicSubs); - } else { - cb.operationFinished(ctx, new ConcurrentHashMap()); - } - - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionState.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionState.java deleted file mode 100644 index 0420d70053f..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/InMemorySubscriptionState.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscriptionState; - -public class InMemorySubscriptionState { - SubscriptionState subscriptionState; - MessageSeqId lastConsumeSeqId; - - public InMemorySubscriptionState(SubscriptionState subscriptionState, MessageSeqId lastConsumeSeqId) { - this.subscriptionState = subscriptionState; - this.lastConsumeSeqId = lastConsumeSeqId; - } - - public InMemorySubscriptionState(SubscriptionState subscriptionState) { - this(subscriptionState, subscriptionState.getMsgId()); - } - - public SubscriptionState getSubscriptionState() { - return subscriptionState; - } - - public MessageSeqId getLastConsumeSeqId() { - return lastConsumeSeqId; - } - - /** - * - * @param lastConsumeSeqId - * @param consumeInterval - * The amount of laziness we want in persisting the consume - * pointers - * @return true if the resulting structure needs to be persisted, false - * otherwise - */ - public boolean setLastConsumeSeqId(MessageSeqId lastConsumeSeqId, int consumeInterval) { - this.lastConsumeSeqId = lastConsumeSeqId; - - if (lastConsumeSeqId.getLocalComponent() - subscriptionState.getMsgId().getLocalComponent() < consumeInterval) { - return false; - } - - subscriptionState = SubscriptionState.newBuilder(subscriptionState).setMsgId(lastConsumeSeqId).build(); - return true; - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/MessageFilter.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/MessageFilter.java deleted file mode 100644 index 6aead601a73..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/MessageFilter.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -public interface MessageFilter { - - /** - * Tests whether a particular message passes the filter or not - * - * @param message - * @return - */ - public boolean testMessage(Message message); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionEventListener.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionEventListener.java deleted file mode 100644 index b8eae5b1d76..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionEventListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.util.Callback; - -/** - * For listening to events that are issued by a SubscriptionManager. - * - */ -public interface SubscriptionEventListener { - - /** - * Called by the subscription manager when it previously had zero local - * subscribers for a topic and is currently accepting its first local - * subscriber. - * - * @param topic - * The topic of interest. - * @param synchronous - * Whether this request was actually initiated by a new local - * subscriber, or whether it was an existing subscription - * inherited by the hub (e.g. when recovering the state from ZK). - * @param cb - * The subscription will not complete until success is called on - * this callback. An error on cb will result in a subscription - * error. - */ - public void onFirstLocalSubscribe(ByteString topic, boolean synchronous, Callback cb); - - /** - * Called by the SubscriptionManager when it previously had non-zero local - * subscribers for a topic and is currently dropping its last local - * subscriber. This is fully asynchronous so there is no callback. - * - * @param topic - * The topic of interest. - */ - public void onLastLocalUnsubscribe(ByteString topic); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionManager.java deleted file mode 100644 index 796343dbc1b..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/SubscriptionManager.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.util.Callback; - -/** - * All methods are thread-safe. - */ -public interface SubscriptionManager { - - /** - * - * Register a new subscription for the given subscriber for the given topic. - * This method should reliably persist the existence of the subscription in - * a way that it can't be lost. If the subscription already exists, - * depending on the create or attach flag in the subscribe request, an - * exception may be returned. - * - * This is an asynchronous method. - * - * @param topic - * @param subRequest - * @param consumeSeqId - * The seqId to start serving the subscription from, if this is a - * brand new subscription - * @param callback - * The seq id returned by the callback is where serving should - * start from - * @param ctx - */ - public void serveSubscribeRequest(ByteString topic, SubscribeRequest subRequest, MessageSeqId consumeSeqId, - Callback callback, Object ctx); - - /** - * Set the consume position of a given subscriber on a given topic. Note - * that this method need not persist the consume position immediately but - * can be lazy and persist it later asynchronously, if that is more - * efficient. - * - * @param topic - * @param subscriberId - * @param consumeSeqId - */ - public void setConsumeSeqIdForSubscriber(ByteString topic, ByteString subscriberId, MessageSeqId consumeSeqId, - Callback callback, Object ctx); - - /** - * Delete a particular subscription - * - * @param topic - * @param subscriberId - */ - public void unsubscribe(ByteString topic, ByteString subscriberId, Callback callback, Object ctx); - - // Management API methods that we will fill in later - // /** - // * Get the ids of all subscribers for a given topic - // * - // * @param topic - // * @return A list of subscriber ids that are currently subscribed to the - // * given topic - // */ - // public List getSubscriptionsForTopic(ByteString topic); - // - // /** - // * Get the topics to which a given subscriber is subscribed to - // * - // * @param subscriberId - // * @return A list of the topics to which the given subscriber is - // subscribed - // * to - // * @throws ServiceDownException - // * If there is an error in looking up the subscription - // * information - // */ - // public List getTopicsForSubscriber(ByteString subscriberId) - // throws ServiceDownException; - - /** - * Add a listener that is notified when topic-subscription pairs are added - * or removed. - */ - public void addListener(SubscriptionEventListener listener); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/TrueFilter.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/TrueFilter.java deleted file mode 100644 index 3804705d69e..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/TrueFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -public class TrueFilter implements MessageFilter { - protected static TrueFilter instance = new TrueFilter(); - - public static TrueFilter instance() { - return instance; - } - - public boolean testMessage(Message message) { - return true; - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/ZkSubscriptionManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/ZkSubscriptionManager.java deleted file mode 100644 index a08011735b6..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/subscriptions/ZkSubscriptionManager.java +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.data.Stat; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.SubscriptionState; -import org.apache.hedwig.protoextensions.SubscriptionStateUtils; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.zookeeper.SafeAsyncZKCallback; -import org.apache.hedwig.zookeeper.ZkUtils; - -public class ZkSubscriptionManager extends AbstractSubscriptionManager { - - ZooKeeper zk; - - protected final static Logger logger = Logger.getLogger(ZkSubscriptionManager.class); - - public ZkSubscriptionManager(ZooKeeper zk, TopicManager topicMgr, PersistenceManager pm, ServerConfiguration cfg, - ScheduledExecutorService scheduler) { - super(cfg, topicMgr, pm, scheduler); - this.zk = zk; - } - - private StringBuilder topicSubscribersPath(StringBuilder sb, ByteString topic) { - return cfg.getZkTopicPath(sb, topic).append("/subscribers"); - } - - private String topicSubscriberPath(ByteString topic, ByteString subscriber) { - return topicSubscribersPath(new StringBuilder(), topic).append("/").append(subscriber.toStringUtf8()) - .toString(); - } - - @Override - protected void readSubscriptions(final ByteString topic, - final Callback> cb, final Object ctx) { - - String topicSubscribersPath = topicSubscribersPath(new StringBuilder(), topic).toString(); - zk.getChildren(topicSubscribersPath, false, new SafeAsyncZKCallback.ChildrenCallback() { - @Override - public void safeProcessResult(int rc, String path, final Object ctx, final List children) { - - if (rc != Code.OK.intValue() && rc != Code.NONODE.intValue()) { - KeeperException e = ZkUtils.logErrorAndCreateZKException("Could not read subscribers for topic " - + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - return; - } - - final Map topicSubs = new ConcurrentHashMap(); - - if (rc == Code.NONODE.intValue() || children.size() == 0) { - if (logger.isDebugEnabled()) { - logger.debug("No subscriptions found while acquiring topic: " + topic.toStringUtf8()); - } - cb.operationFinished(ctx, topicSubs); - return; - } - - final AtomicBoolean failed = new AtomicBoolean(); - final AtomicInteger count = new AtomicInteger(); - - for (final String child : children) { - - final ByteString subscriberId = ByteString.copyFromUtf8(child); - final String childPath = path + "/" + child; - - zk.getData(childPath, false, new SafeAsyncZKCallback.DataCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, byte[] data, Stat stat) { - - if (rc != Code.OK.intValue()) { - KeeperException e = ZkUtils.logErrorAndCreateZKException( - "Could not read subscription data for topic: " + topic.toStringUtf8() - + ", subscriberId: " + subscriberId.toStringUtf8(), path, rc); - reportFailure(new PubSubException.ServiceDownException(e)); - return; - } - - if (failed.get()) { - return; - } - - SubscriptionState state; - - try { - state = SubscriptionState.parseFrom(data); - } catch (InvalidProtocolBufferException ex) { - String msg = "Failed to deserialize state for topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8(); - logger.error(msg, ex); - reportFailure(new PubSubException.UnexpectedConditionException(msg)); - return; - } - - if (logger.isDebugEnabled()) { - logger.debug("Found subscription while acquiring topic: " + topic.toStringUtf8() - + " subscriberId: " + child + "state: " - + SubscriptionStateUtils.toString(state)); - } - - topicSubs.put(subscriberId, new InMemorySubscriptionState(state)); - if (count.incrementAndGet() == children.size()) { - assert topicSubs.size() == count.get(); - cb.operationFinished(ctx, topicSubs); - } - } - - private void reportFailure(PubSubException e) { - if (failed.compareAndSet(false, true)) - cb.operationFailed(ctx, e); - } - }, ctx); - } - } - }, ctx); - } - - @Override - protected void createSubscriptionState(final ByteString topic, final ByteString subscriberId, - final SubscriptionState state, final Callback callback, final Object ctx) { - ZkUtils.createFullPathOptimistic(zk, topicSubscriberPath(topic, subscriberId), state.toByteArray(), - Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new SafeAsyncZKCallback.StringCallback() { - - @Override - public void safeProcessResult(int rc, String path, Object ctx, String name) { - if (rc == Code.OK.intValue()) { - if (logger.isDebugEnabled()) { - logger.debug("Successfully recorded subscription for topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8() + " state: " - + SubscriptionStateUtils.toString(state)); - } - callback.operationFinished(ctx, null); - } else { - KeeperException ke = ZkUtils.logErrorAndCreateZKException( - "Could not record new subscription for topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8(), path, rc); - callback.operationFailed(ctx, new PubSubException.ServiceDownException(ke)); - } - } - }, ctx); - } - - @Override - protected void updateSubscriptionState(final ByteString topic, final ByteString subscriberId, - final SubscriptionState state, final Callback callback, final Object ctx) { - zk.setData(topicSubscriberPath(topic, subscriberId), state.toByteArray(), -1, - new SafeAsyncZKCallback.StatCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, Stat stat) { - if (rc != Code.OK.intValue()) { - KeeperException e = ZkUtils.logErrorAndCreateZKException("Topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8() - + " could not set subscription state: " + SubscriptionStateUtils.toString(state), - path, rc); - callback.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - } else { - if (logger.isDebugEnabled()) { - logger.debug("Successfully updated subscription for topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8() + " state: " - + SubscriptionStateUtils.toString(state)); - } - - callback.operationFinished(ctx, null); - } - } - }, ctx); - } - - @Override - protected void deleteSubscriptionState(final ByteString topic, final ByteString subscriberId, - final Callback callback, final Object ctx) { - zk.delete(topicSubscriberPath(topic, subscriberId), -1, new SafeAsyncZKCallback.VoidCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx) { - if (rc == Code.OK.intValue()) { - if (logger.isDebugEnabled()) { - logger.debug("Successfully deleted subscription for topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8()); - } - - callback.operationFinished(ctx, null); - return; - } - - KeeperException e = ZkUtils.logErrorAndCreateZKException("Topic: " + topic.toStringUtf8() - + " subscriberId: " + subscriberId.toStringUtf8() + " failed to delete subscription", path, rc); - callback.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - } - }, ctx); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/AbstractTopicManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/AbstractTopicManager.java deleted file mode 100644 index c77d5f1b47d..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/AbstractTopicManager.java +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import org.apache.log4j.Logger; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.common.TopicOpQueuer; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.CallbackUtils; -import org.apache.hedwig.util.HedwigSocketAddress; - -public abstract class AbstractTopicManager implements TopicManager { - /** - * My name. - */ - protected HedwigSocketAddress addr; - - /** - * Topic change listeners. - */ - protected ArrayList listeners = new ArrayList(); - - /** - * List of topics I believe I am responsible for. - */ - protected Set topics = Collections.synchronizedSet(new HashSet()); - - protected TopicOpQueuer queuer; - protected ServerConfiguration cfg; - protected ScheduledExecutorService scheduler; - - private static final Logger logger = Logger.getLogger(AbstractTopicManager.class); - - private class GetOwnerOp extends TopicOpQueuer.AsynchronousOp { - public boolean shouldClaim; - - public GetOwnerOp(final ByteString topic, boolean shouldClaim, - final Callback cb, Object ctx) { - queuer.super(topic, cb, ctx); - this.shouldClaim = shouldClaim; - } - - @Override - public void run() { - realGetOwner(topic, shouldClaim, cb, ctx); - } - } - - private class ReleaseOp extends TopicOpQueuer.AsynchronousOp { - public ReleaseOp(ByteString topic, Callback cb, Object ctx) { - queuer.super(topic, cb, ctx); - } - - @Override - public void run() { - if (!topics.contains(topic)) { - cb.operationFinished(ctx, null); - return; - } - realReleaseTopic(topic, cb, ctx); - } - } - - public AbstractTopicManager(ServerConfiguration cfg, ScheduledExecutorService scheduler) - throws UnknownHostException { - this.cfg = cfg; - this.queuer = new TopicOpQueuer(scheduler); - this.scheduler = scheduler; - addr = cfg.getServerAddr(); - } - - @Override - public synchronized void addTopicOwnershipChangeListener(TopicOwnershipChangeListener listener) { - listeners.add(listener); - } - - protected final synchronized void notifyListenersAndAddToOwnedTopics(final ByteString topic, - final Callback originalCallback, final Object originalContext) { - - Callback postCb = new Callback() { - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - topics.add(topic); - if (cfg.getRetentionSecs() > 0) { - scheduler.schedule(new Runnable() { - @Override - public void run() { - // Enqueue a release operation. (Recall that release - // doesn't "fail" even if the topic is missing.) - releaseTopic(topic, new Callback() { - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - logger.error("failure that should never happen when periodically releasing topic " - + topic, exception); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - logger.debug("successful periodic release of topic " + topic); - } - - }, null); - } - }, cfg.getRetentionSecs(), TimeUnit.SECONDS); - } - originalCallback.operationFinished(originalContext, addr); - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - // TODO: optimization: we can release this as soon as we experience the first error. - realReleaseTopic(topic, CallbackUtils.curry(originalCallback, addr), originalContext); - originalCallback.operationFailed(ctx, exception); - } - }; - - Callback mcb = CallbackUtils.multiCallback(listeners.size(), postCb, null); - for (TopicOwnershipChangeListener listener : listeners) { - listener.acquiredTopic(topic, mcb, null); - } - } - - private void realReleaseTopic(ByteString topic, Callback callback, Object ctx) { - for (TopicOwnershipChangeListener listener : listeners) - listener.lostTopic(topic); - topics.remove(topic); - postReleaseCleanup(topic, callback, ctx); - } - - @Override - public final void getOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx) { - queuer.pushAndMaybeRun(topic, new GetOwnerOp(topic, shouldClaim, cb, ctx)); - } - - @Override - public final void releaseTopic(ByteString topic, Callback cb, Object ctx) { - queuer.pushAndMaybeRun(topic, new ReleaseOp(topic, cb, ctx)); - } - - /** - * This method should "return" the owner of the topic if one has been chosen - * already. If there is no pre-chosen owner, either this hub or some other - * should be chosen based on the shouldClaim parameter. If its ends up - * choosing this hub as the owner, the {@code - * AbstractTopicManager#notifyListenersAndAddToOwnedTopics(ByteString, - * OperationCallback, Object)} method must be called. - * - */ - protected abstract void realGetOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx); - - /** - * The method should do any cleanup necessary to indicate to other hubs that - * this topic has been released - */ - protected abstract void postReleaseCleanup(ByteString topic, Callback cb, Object ctx); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicManager.java deleted file mode 100644 index 66bc5314576..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicManager.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.HedwigSocketAddress; - -/** - * An implementor of this interface is basically responsible for ensuring that - * there is at most a single host responsible for a given topic at a given time. - * Also, it is desirable that on a host failure, some other hosts in the cluster - * claim responsibilities for the topics that were at the failed host. On - * claiming responsibility for a topic, a host should call its - * {@link TopicOwnershipChangeListener}. - * - */ - -public interface TopicManager { - /** - * Get the name of the host responsible for the given topic. - * - * @param topic - * The topic whose owner to get. - * @param cb - * Callback. - * @return The name of host responsible for the given topic - * @throws ServiceDownException - * If there is an error looking up the information - */ - public void getOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx); - - /** - * Whenever the topic manager finds out that the set of topics owned by this - * node has changed, it can notify a set of - * {@link TopicOwnershipChangeListener} objects. Any component of the system - * (e.g., the {@link PersistenceManager}) can listen for such changes by - * implementing the {@link TopicOwnershipChangeListener} interface and - * registering themselves with the {@link TopicManager} using this method. - * It is important that the {@link TopicOwnershipChangeListener} reacts - * immediately to such notifications, and with no blocking (because multiple - * listeners might need to be informed and they are all informed by the same - * thread). - * - * @param listener - */ - public void addTopicOwnershipChangeListener(TopicOwnershipChangeListener listener); - - /** - * Give up ownership of a topic. If I don't own it, do nothing. - * - * @throws ServiceDownException - * If there is an error in claiming responsibility for the topic - */ - public void releaseTopic(ByteString topic, Callback cb, Object ctx); - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicOwnershipChangeListener.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicOwnershipChangeListener.java deleted file mode 100644 index b0fe2c9f152..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TopicOwnershipChangeListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.util.Callback; - -public interface TopicOwnershipChangeListener { - - public void acquiredTopic(ByteString topic, Callback callback, Object ctx); - - public void lostTopic(ByteString topic); -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TrivialOwnAllTopicManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TrivialOwnAllTopicManager.java deleted file mode 100644 index f4d66ff90aa..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/TrivialOwnAllTopicManager.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import java.net.UnknownHostException; -import java.util.concurrent.ScheduledExecutorService; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class TrivialOwnAllTopicManager extends AbstractTopicManager { - - public TrivialOwnAllTopicManager(ServerConfiguration cfg, ScheduledExecutorService scheduler) - throws UnknownHostException { - super(cfg, scheduler); - } - - @Override - protected void realGetOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx) { - - if (topics.contains(topic)) { - cb.operationFinished(ctx, addr); - return; - } - - notifyListenersAndAddToOwnedTopics(topic, cb, ctx); - } - - @Override - protected void postReleaseCleanup(ByteString topic, Callback cb, Object ctx) { - // No cleanup to do - cb.operationFinished(ctx, null); - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/ZkTopicManager.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/ZkTopicManager.java deleted file mode 100644 index 9b2ba83878c..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/server/topics/ZkTopicManager.java +++ /dev/null @@ -1,428 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import java.net.UnknownHostException; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.SynchronousQueue; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.data.Stat; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Either; -import org.apache.hedwig.util.HedwigSocketAddress; -import org.apache.hedwig.zookeeper.SafeAsyncZKCallback; -import org.apache.hedwig.zookeeper.ZkUtils; -import org.apache.hedwig.zookeeper.SafeAsyncZKCallback.DataCallback; -import org.apache.hedwig.zookeeper.SafeAsyncZKCallback.StatCallback; - -/** - * Topics are operated on in parallel as they are independent. - * - */ -public class ZkTopicManager extends AbstractTopicManager implements TopicManager { - - static Logger logger = Logger.getLogger(ZkTopicManager.class); - Random rand = new Random(); - - /** - * Persistent storage for topic metadata. - */ - private ZooKeeper zk; - String ephemeralNodePath; - - StatCallback loadReportingStatCallback = new StatCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, Stat stat) { - if (rc != KeeperException.Code.OK.intValue()) { - logger.warn("Failed to update load information in zk"); - } - } - }; - - // Boolean flag indicating if we should suspend activity. If this is true, - // all of the Ops put into the queuer will fail automatically. - protected volatile boolean isSuspended = false; - - /** - * Create a new topic manager. Pass in an active ZooKeeper client object. - * - * @param zk - */ - public ZkTopicManager(final ZooKeeper zk, final ServerConfiguration cfg, ScheduledExecutorService scheduler) - throws UnknownHostException, PubSubException { - - super(cfg, scheduler); - this.zk = zk; - this.ephemeralNodePath = cfg.getZkHostsPrefix(new StringBuilder()).append("/").append(addr).toString(); - - zk.register(new Watcher() { - @Override - public void process(WatchedEvent event) { - if (event.getType().equals(Watcher.Event.EventType.None)) { - if (event.getState().equals(Watcher.Event.KeeperState.Disconnected)) { - logger.warn("ZK client has been disconnected to the ZK server!"); - isSuspended = true; - } else if (event.getState().equals(Watcher.Event.KeeperState.SyncConnected)) { - if (isSuspended) { - logger.info("ZK client has been reconnected to the ZK server!"); - } - isSuspended = false; - } - } - // Check for expired connection. - if (event.getState().equals(Watcher.Event.KeeperState.Expired)) { - logger.error("ZK client connection to the ZK server has expired!"); - System.exit(1); - } - } - }); - final SynchronousQueue> queue = new SynchronousQueue>(); - - registerWithZookeeper(new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - logger.error("Failed to register hub with zookeeper", exception); - ConcurrencyUtils.put(queue, Either.of((Void) null, exception)); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - logger.info("Successfully registered hub with zookeeper"); - ConcurrencyUtils.put(queue, Either.of(resultOfOperation, (PubSubException) null)); - } - }, null); - - PubSubException pse = ConcurrencyUtils.take(queue).right(); - - if (pse != null) { - throw pse; - } - } - - void registerWithZookeeper(final Callback callback, Object ctx) { - - ZkUtils.createFullPathOptimistic(zk, ephemeralNodePath, getCurrentLoadData(), Ids.OPEN_ACL_UNSAFE, - CreateMode.EPHEMERAL, new SafeAsyncZKCallback.StringCallback() { - - @Override - public void safeProcessResult(int rc, String path, Object ctx, String name) { - if (rc == Code.OK.intValue()) { - callback.operationFinished(ctx, null); - return; - } - if (rc != Code.NODEEXISTS.intValue()) { - KeeperException ke = ZkUtils.logErrorAndCreateZKException( - "Could not create ephemeral node to register hub", ephemeralNodePath, rc); - callback.operationFailed(ctx, new PubSubException.ServiceDownException(ke)); - return; - } - - logger.info("Found stale ephemeral node while registering hub with ZK, deleting it"); - - // Node exists, lets try to delete it and retry - zk.delete(ephemeralNodePath, -1, new SafeAsyncZKCallback.VoidCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx) { - if (rc == Code.OK.intValue() || rc == Code.NONODE.intValue()) { - registerWithZookeeper(callback, ctx); - return; - } - KeeperException ke = ZkUtils.logErrorAndCreateZKException( - "Could not delete stale ephemeral node to register hub", ephemeralNodePath, rc); - callback.operationFailed(ctx, new PubSubException.ServiceDownException(ke)); - return; - - } - }, ctx); - - } - }, null); - } - - String hubPath(ByteString topic) { - return cfg.getZkTopicPath(new StringBuilder(), topic).append("/hub").toString(); - } - - @Override - protected void realGetOwner(final ByteString topic, final boolean shouldClaim, - final Callback cb, final Object ctx) { - // If operations are suspended due to a ZK client disconnect, just error - // out this call and return. - if (isSuspended) { - cb.operationFailed(ctx, new PubSubException.ServiceDownException( - "ZKTopicManager service is temporarily suspended!")); - return; - } - - if (topics.contains(topic)) { - cb.operationFinished(ctx, addr); - return; - } - - new ZkGetOwnerOp(topic, shouldClaim, cb, ctx).read(); - } - - // Recursively call each other. - class ZkGetOwnerOp { - ByteString topic; - boolean shouldClaim; - Callback cb; - Object ctx; - String hubPath; - - public ZkGetOwnerOp(ByteString topic, boolean shouldClaim, Callback cb, Object ctx) { - this.topic = topic; - this.shouldClaim = shouldClaim; - this.cb = cb; - this.ctx = ctx; - hubPath = hubPath(topic); - - } - - public void choose() { - // Get the list of existing hosts - String registeredHostsPath = cfg.getZkHostsPrefix(new StringBuilder()).toString(); - zk.getChildren(registeredHostsPath, false, new SafeAsyncZKCallback.ChildrenCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, List children) { - if (rc != Code.OK.intValue()) { - KeeperException e = ZkUtils.logErrorAndCreateZKException( - "Could not get list of available hubs", path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - return; - } - chooseLeastLoadedNode(children); - } - }, null); - } - - public void chooseLeastLoadedNode(final List children) { - DataCallback dataCallback = new DataCallback() { - int numResponses = 0; - int minLoad = Integer.MAX_VALUE; - String leastLoaded = null; - - @Override - public void safeProcessResult(int rc, String path, Object ctx, byte[] data, Stat stat) { - synchronized (this) { - if (rc == KeeperException.Code.OK.intValue()) { - try { - int load = Integer.parseInt(new String(data)); - if (logger.isDebugEnabled()){ - logger.debug("Found server: " + ctx + " with load: " + load); - } - if (load < minLoad || (load == minLoad && rand.nextBoolean())) { - minLoad = load; - leastLoaded = (String) ctx; - } - } catch (NumberFormatException e) { - logger.warn("Corrupted load information from hub:" + ctx); - // some corrupted data, we'll just ignore this - // hub - } - } - numResponses++; - - if (numResponses == children.size()) { - if (leastLoaded == null) { - cb.operationFailed(ZkGetOwnerOp.this.ctx, new PubSubException.ServiceDownException( - "No hub available")); - return; - } - HedwigSocketAddress owner = new HedwigSocketAddress(leastLoaded); - if (owner.equals(addr)) { - claim(); - } else { - cb.operationFinished(ZkGetOwnerOp.this.ctx, owner); - } - } - } - - } - }; - - for (String child : children) { - zk.getData(cfg.getZkHostsPrefix(new StringBuilder()).append("/").append(child).toString(), false, - dataCallback, child); - } - } - - public void claimOrChoose() { - if (shouldClaim) - claim(); - else - choose(); - } - - public void read() { - zk.getData(hubPath, false, new SafeAsyncZKCallback.DataCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, byte[] data, Stat stat) { - - if (rc == Code.NONODE.intValue()) { - claimOrChoose(); - return; - } - - if (rc != Code.OK.intValue()) { - KeeperException e = ZkUtils.logErrorAndCreateZKException("Could not read ownership for topic: " - + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - return; - } - - // successfully did a read - HedwigSocketAddress owner = new HedwigSocketAddress(new String(data)); - if (!owner.equals(addr)) { - if (logger.isDebugEnabled()) { - logger.debug("topic: " + topic.toStringUtf8() + " belongs to someone else: " + owner); - } - cb.operationFinished(ctx, owner); - return; - } - - logger.info("Discovered stale self-node for topic: " + topic.toStringUtf8() + ", will delete it"); - - // we must have previously failed and left a - // residual ephemeral node here, so we must - // delete it (clean it up) and then - // re-create/re-acquire the topic. - zk.delete(hubPath, stat.getVersion(), new VoidCallback() { - @Override - public void processResult(int rc, String path, Object ctx) { - if (Code.OK.intValue() == rc || Code.NONODE.intValue() == rc) { - claimOrChoose(); - } else { - KeeperException e = ZkUtils.logErrorAndCreateZKException( - "Could not delete self node for topic: " + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - } - } - }, ctx); - } - }, ctx); - } - - public void claim() { - if (logger.isDebugEnabled()) { - logger.debug("claiming topic: " + topic.toStringUtf8()); - } - - ZkUtils.createFullPathOptimistic(zk, hubPath, addr.toString().getBytes(), Ids.OPEN_ACL_UNSAFE, - CreateMode.EPHEMERAL, new SafeAsyncZKCallback.StringCallback() { - - @Override - public void safeProcessResult(int rc, String path, Object ctx, String name) { - if (rc == Code.OK.intValue()) { - if (logger.isDebugEnabled()) { - logger.debug("claimed topic: " + topic.toStringUtf8()); - } - notifyListenersAndAddToOwnedTopics(topic, cb, ctx); - updateLoadInformation(); - } else if (rc == Code.NODEEXISTS.intValue()) { - read(); - } else { - KeeperException e = ZkUtils.logErrorAndCreateZKException( - "Failed to create ephemeral node to claim ownership of topic: " - + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - } - } - }, ctx); - } - - } - - byte[] getCurrentLoadData() { - // For now, using the number of topics as an indicator of load - // information - return (topics.size() + "").getBytes(); - } - - void updateLoadInformation() { - byte[] currentLoad = getCurrentLoadData(); - if (logger.isDebugEnabled()){ - logger.debug("Reporting load of " + new String(currentLoad)); - } - zk.setData(ephemeralNodePath, currentLoad, -1, loadReportingStatCallback, null); - } - - @Override - protected void postReleaseCleanup(final ByteString topic, final Callback cb, Object ctx) { - - zk.getData(hubPath(topic), false, new SafeAsyncZKCallback.DataCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, byte[] data, Stat stat) { - if (rc == Code.NONODE.intValue()) { - // Node has somehow disappeared from under us, live with it - // since its a transient node - logger.warn("While deleting self-node for topic: " + topic.toStringUtf8() + ", node not found"); - cb.operationFinished(ctx, null); - return; - } - - if (rc != Code.OK.intValue()) { - KeeperException e = ZkUtils.logErrorAndCreateZKException( - "Failed to delete self-ownership node for topic: " + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - return; - } - - HedwigSocketAddress owner = new HedwigSocketAddress(new String(data)); - if (!owner.equals(addr)) { - logger.warn("Wanted to delete self-node for topic: " + topic.toStringUtf8() + " but node for " - + owner + " found, leaving untouched"); - // Not our node, someone else's, leave it alone - cb.operationFinished(ctx, null); - return; - } - - zk.delete(path, stat.getVersion(), new SafeAsyncZKCallback.VoidCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx) { - if (rc != Code.OK.intValue() && rc != Code.NONODE.intValue()) { - KeeperException e = ZkUtils - .logErrorAndCreateZKException("Failed to delete self-ownership node for topic: " - + topic.toStringUtf8(), path, rc); - cb.operationFailed(ctx, new PubSubException.ServiceDownException(e)); - return; - } - - cb.operationFinished(ctx, null); - } - }, ctx); - } - }, ctx); - } - -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsynBKCallback.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsynBKCallback.java deleted file mode 100644 index 4e2038f540e..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsynBKCallback.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.zookeeper; - -import java.util.Enumeration; - -import org.apache.bookkeeper.client.AsyncCallback; -import org.apache.bookkeeper.client.LedgerEntry; -import org.apache.bookkeeper.client.LedgerHandle; - - -public class SafeAsynBKCallback extends SafeAsyncCallback{ - - public static abstract class OpenCallback implements AsyncCallback.OpenCallback { - @Override - public void openComplete(int rc, LedgerHandle ledgerHandle, Object ctx) { - try{ - safeOpenComplete(rc, ledgerHandle, ctx); - }catch(Throwable t){ - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeOpenComplete(int rc, LedgerHandle ledgerHandle, Object ctx); - - } - - public static abstract class CloseCallback implements AsyncCallback.CloseCallback { - @Override - public void closeComplete(int rc, LedgerHandle ledgerHandle, Object ctx){ - try{ - safeCloseComplete(rc, ledgerHandle, ctx); - }catch(Throwable t){ - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeCloseComplete(int rc, LedgerHandle ledgerHandle, Object ctx) ; - } - - public static abstract class ReadCallback implements AsyncCallback.ReadCallback { - - @Override - public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) { - try{ - safeReadComplete(rc, lh, seq, ctx); - }catch(Throwable t){ - invokeUncaughtExceptionHandler(t); - } - - } - - public abstract void safeReadComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx); - } - - public static abstract class CreateCallback implements AsyncCallback.CreateCallback { - - @Override - public void createComplete(int rc, LedgerHandle lh, Object ctx) { - try{ - safeCreateComplete(rc, lh, ctx); - }catch(Throwable t){ - invokeUncaughtExceptionHandler(t); - } - - } - - public abstract void safeCreateComplete(int rc, LedgerHandle lh, Object ctx); - - - } - - public static abstract class AddCallback implements AsyncCallback.AddCallback { - - @Override - public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) { - try{ - safeAddComplete(rc, lh, entryId, ctx); - }catch(Throwable t){ - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeAddComplete(int rc, LedgerHandle lh, long entryId, Object ctx); - - } - -} - diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncCallback.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncCallback.java deleted file mode 100644 index d1f755e6fef..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncCallback.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.zookeeper; - -import java.lang.Thread.UncaughtExceptionHandler; - -import org.apache.hedwig.server.common.TerminateJVMExceptionHandler; - -public class SafeAsyncCallback { - protected static UncaughtExceptionHandler uncaughtExceptionHandler = new TerminateJVMExceptionHandler(); - - public static void setUncaughtExceptionHandler(UncaughtExceptionHandler uncaughtExceptionHandler) { - SafeAsyncCallback.uncaughtExceptionHandler = uncaughtExceptionHandler; - } - - static void invokeUncaughtExceptionHandler(Throwable t) { - Thread thread = Thread.currentThread(); - uncaughtExceptionHandler.uncaughtException(thread, t); - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncZKCallback.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncZKCallback.java deleted file mode 100644 index 45973ffc34c..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/SafeAsyncZKCallback.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.zookeeper; - -import java.util.List; - -import org.apache.zookeeper.AsyncCallback; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Stat; - -public class SafeAsyncZKCallback extends SafeAsyncCallback{ - public static abstract class StatCallback implements AsyncCallback.StatCallback { - public void processResult(int rc, String path, Object ctx, Stat stat) { - try { - safeProcessResult(rc, path, ctx, stat); - } catch (Throwable t) { - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeProcessResult(int rc, String path, Object ctx, Stat stat); - } - - public static abstract class DataCallback implements AsyncCallback.DataCallback { - public void processResult(int rc, String path, Object ctx, byte data[], Stat stat) { - try { - safeProcessResult(rc, path, ctx, data, stat); - } catch (Throwable t) { - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeProcessResult(int rc, String path, Object ctx, byte data[], Stat stat); - } - - public static abstract class ACLCallback implements AsyncCallback.ACLCallback { - public void processResult(int rc, String path, Object ctx, List acl, Stat stat) { - try { - safeProcessResult(rc, path, ctx, acl, stat); - } catch (Throwable t) { - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeProcessResult(int rc, String path, Object ctx, List acl, Stat stat); - } - - public static abstract class ChildrenCallback implements AsyncCallback.ChildrenCallback { - public void processResult(int rc, String path, Object ctx, List children) { - try { - safeProcessResult(rc, path, ctx, children); - } catch (Throwable t) { - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeProcessResult(int rc, String path, Object ctx, List children); - } - - public static abstract class StringCallback implements AsyncCallback.StringCallback { - public void processResult(int rc, String path, Object ctx, String name) { - try { - safeProcessResult(rc, path, ctx, name); - } catch (Throwable t) { - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeProcessResult(int rc, String path, Object ctx, String name); - } - - public static abstract class VoidCallback implements AsyncCallback.VoidCallback { - public void processResult(int rc, String path, Object ctx) { - try { - safeProcessResult(rc, path, ctx); - } catch (Throwable t) { - invokeUncaughtExceptionHandler(t); - } - } - - public abstract void safeProcessResult(int rc, String path, Object ctx); - } -} diff --git a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/ZkUtils.java b/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/ZkUtils.java deleted file mode 100644 index dc9ade2bf6d..00000000000 --- a/src/contrib/hedwig/server/src/main/java/org/apache/hedwig/zookeeper/ZkUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.zookeeper; - -import java.util.List; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.AsyncCallback; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.data.ACL; - -import org.apache.hedwig.util.PathUtils; - -public class ZkUtils { - - static Logger logger = Logger.getLogger(ZkUtils.class); - - public static void createFullPathOptimistic(final ZooKeeper zk, final String originalPath, final byte[] data, - final List acl, final CreateMode createMode, final AsyncCallback.StringCallback callback, - final Object ctx) { - - zk.create(originalPath, data, acl, createMode, new SafeAsyncZKCallback.StringCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, String name) { - - if (rc != Code.NONODE.intValue()) { - callback.processResult(rc, path, ctx, name); - return; - } - - // Since I got a nonode, it means that my parents don't exist - // create mode is persistent since ephemeral nodes can't be - // parents - ZkUtils.createFullPathOptimistic(zk, PathUtils.parent(originalPath), new byte[0], acl, - CreateMode.PERSISTENT, new SafeAsyncZKCallback.StringCallback() { - - @Override - public void safeProcessResult(int rc, String path, Object ctx, String name) { - if (rc == Code.OK.intValue() || rc == Code.NODEEXISTS.intValue()) { - // succeeded in creating the parent, now - // create the original path - ZkUtils.createFullPathOptimistic(zk, originalPath, data, acl, createMode, callback, - ctx); - } else { - callback.processResult(rc, path, ctx, name); - } - } - }, ctx); - } - }, ctx); - - } - - public static KeeperException logErrorAndCreateZKException(String msg, String path, int rc) { - KeeperException ke = KeeperException.create(Code.get(rc), path); - logger.error(msg + ",zkPath: " + path, ke); - return ke; - } - -} diff --git a/src/contrib/hedwig/server/src/main/resources/p12.pass b/src/contrib/hedwig/server/src/main/resources/p12.pass deleted file mode 100644 index e7a8bf7e7f6..00000000000 --- a/src/contrib/hedwig/server/src/main/resources/p12.pass +++ /dev/null @@ -1 +0,0 @@ -eUySvp2phM2Wk diff --git a/src/contrib/hedwig/server/src/main/resources/server.p12 b/src/contrib/hedwig/server/src/main/resources/server.p12 deleted file mode 100644 index b7043b85630c8988c6b20bff335fb6f6589c7621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3925 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 diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/HelperMethods.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/HelperMethods.java deleted file mode 100644 index b855f9a0bf3..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/HelperMethods.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; - -public class HelperMethods { - static Random rand = new Random(); - - public static List getRandomPublishedMessages(int numMessages, int size) { - ByteString[] regions = { ByteString.copyFromUtf8("sp1"), ByteString.copyFromUtf8("re1"), - ByteString.copyFromUtf8("sg") }; - return getRandomPublishedMessages(numMessages, size, regions); - } - - public static List getRandomPublishedMessages(int numMessages, int size, ByteString[] regions) { - List msgs = new ArrayList(); - for (int i = 0; i < numMessages; i++) { - byte[] body = new byte[size]; - rand.nextBytes(body); - msgs.add(Message.newBuilder().setBody(ByteString.copyFrom(body)).setSrcRegion( - regions[rand.nextInt(regions.length)]).build()); - } - return msgs; - } - - public static boolean areEqual(Message m1, Message m2) { - if (m1.hasSrcRegion() != m2.hasSrcRegion()) { - return false; - } - if (m1.hasSrcRegion() && !m1.getSrcRegion().equals(m2.getSrcRegion())) { - return false; - } - return m1.getBody().equals(m2.getBody()); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControl.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControl.java deleted file mode 100644 index 9ec9e966d6b..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControl.java +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig; - -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; -import org.apache.zookeeper.server.ZooKeeperServerMain; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.hedwig.server.netty.PubSubServer; -import java.net.ConnectException; -import java.io.File; -import java.io.IOException; - -public class ServerControl { - public class TestException extends Exception { - public TestException(String str) { - super(str); - } - }; - - public interface TestServer { - public String getAddress(); - public void kill(); - } - - private class BookKeeperServer extends BookieServer implements TestServer { - private String address; - - public BookKeeperServer(int port, TestServer zkserver, String journal, String ledger) throws IOException { - super(port, zkserver.getAddress(), new File(journal), new File[] { new File(ledger) }); - - address = "localhost:"+port; - start(); - } - - public String getAddress() { - return address; - } - - public void kill() { - try { - shutdown(); - } catch (Exception e) { - } - } - } - - private class ZookeeperServer extends ZooKeeperServerMain implements TestServer { - public String address; - public Thread serverThread; - String path; - public ZookeeperServer(int port, String path) throws TestException { - super(); - - this.path = path; - final String[] args = { Integer.toString(port), path}; - address = "localhost:" + port; - serverThread = new Thread() { - public void run() { - try { - initializeAndRun(args); - } catch (Exception e) { - } - }; - }; - serverThread.start(); - } - - public String getAddress() { - return address; - } - - public void kill() { - shutdown(); - serverThread.interrupt(); - } - } - - private class HedwigServer implements TestServer { - private PubSubServer server; - private String address; - - public HedwigServer(int port, String region, TestServer zk) throws TestException { - class MyServerConfiguration extends ServerConfiguration { - MyServerConfiguration(int port, TestServer zk, String region) { - conf.setProperty(ServerConfiguration.SERVER_PORT, port); - conf.setProperty(ServerConfiguration.ZK_HOST, zk.getAddress()); - conf.setProperty(ServerConfiguration.REGION, region); - } - }; - - address = "localhost:" + port; - - try { - server = new PubSubServer(new MyServerConfiguration(port, zk, region)); - } catch (Exception e) { - throw new TestException("Couldn't create pub sub server : " + e); - } - } - - public String getAddress() { - return address; - } - - public void kill() { - server.shutdown(); - } - } - - private String createTempDirectory(String suffix) throws IOException { - String dir = System.getProperty("java.io.tmpdir") + File.separator + System.currentTimeMillis() + suffix; - final File dirf = new File(dir); - boolean good = dirf.mkdir(); - if (!good) { - throw new IOException("Unable to create directory " + dir); - } - - Runtime.getRuntime().addShutdownHook(new Thread() { - public void delete(File f) { - File[] subfiles = f.listFiles(); - if (subfiles != null) { - for (File subf : subfiles) { - delete(subf); - } - } - f.delete(); - } - - public void run() { - delete(dirf); - } - }); - return dir; - } - - public TestServer startZookeeperServer(int port) throws IOException, TestException { - String dir = createTempDirectory("-zookeeper-" + port); - ZookeeperServer server = new ZookeeperServer(port, dir); - - return server; - } - - public TestServer startBookieServer(int port, TestServer zookeeperServer) throws IOException, TestException { - int tries = 4; - while (true) { - try { - tries--; - ZooKeeper zk = new ZooKeeper(zookeeperServer.getAddress(), 1000, new Watcher() { public void process(WatchedEvent event) { /* do nothing */ } }); - if (zk.exists("/ledgers/available", false) == null) { - byte[] data = new byte[1]; - data[0] = 0; - zk.create("/ledgers", data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - zk.create("/ledgers/available", data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - } - zk.close(); - break; - } catch (KeeperException.ConnectionLossException ce) { - if (tries > 0) { - try { - Thread.sleep(3); - } catch (Exception e) { - throw new TestException("Can't even sleep. Fix your machine: " + e); - } - continue; - } else { - throw new TestException("Error connecting to zookeeper: " + ce); - } - } catch (Exception e) { - throw new TestException("Error initialising bookkeeper ledgers: " + e); - } - } - String journal = createTempDirectory("-bookie-" + port + "-journal"); - String ledger = createTempDirectory("-bookie-" + port + "-ledger"); - System.out.println(journal); - BookKeeperServer bookie = new BookKeeperServer(port, zookeeperServer, journal, ledger); - return bookie; - } - - public TestServer startPubSubServer(int port, String region, TestServer zookeeperServer) throws IOException, TestException { - return new HedwigServer(port, region, zookeeperServer); - } - - public ServerControl() { - } - - public static void main(String[] args) throws Exception { - ServerControl control = new ServerControl(); - - TestServer zk = control.startZookeeperServer(12345); - TestServer bk1 = control.startBookieServer(12346, zk); - TestServer bk2 = control.startBookieServer(12347, zk); - TestServer bk3 = control.startBookieServer(12348, zk); - - TestServer hw1 = control.startPubSubServer(12349, "foobar", zk); - TestServer hw2 = control.startPubSubServer(12350, "foobar", zk); - TestServer hw3 = control.startPubSubServer(12351, "foobar", zk); - TestServer hw4 = control.startPubSubServer(12352, "barfoo", zk); - System.out.println("Started " + zk.getAddress()); - System.out.println("Sleeping for 10 seconds"); - Thread.sleep(10000); - bk3.kill(); - bk2.kill(); - bk1.kill(); - zk.kill(); - hw1.kill(); - hw2.kill(); - hw3.kill(); - hw4.kill(); - } -} \ No newline at end of file diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControlDaemon.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControlDaemon.java deleted file mode 100644 index 7d732f29935..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/ServerControlDaemon.java +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig; - -import java.net.InetSocketAddress; -import java.util.concurrent.Executors; - -import org.jboss.netty.bootstrap.ServerBootstrap; -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.ChannelPipelineFactory; -import org.jboss.netty.channel.Channels; -import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; - -import org.apache.log4j.Logger; - -import org.jboss.netty.channel.Channel; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.channel.ChannelEvent; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.jboss.netty.channel.SimpleChannelUpstreamHandler; -import org.jboss.netty.handler.codec.string.StringEncoder; -import org.jboss.netty.handler.codec.string.StringDecoder; -import org.jboss.netty.handler.codec.frame.Delimiters; -import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder; -import org.jboss.netty.channel.ChannelPipelineCoverage; - -import java.util.HashMap; - -public class ServerControlDaemon { - private static final Logger LOG = - Logger.getLogger(ServerControlDaemon.class); - - @ChannelPipelineCoverage("all") - public static class ServerControlDaemonHandler extends SimpleChannelUpstreamHandler{ - private ServerControl control; - private HashMap> serverMap; - - public ServerControlDaemonHandler() { - serverMap = new HashMap>(); - control = new ServerControl(); - } - - private void addServerForChannel(Channel c, ServerControl.TestServer t) { - LOG.info("Created server " + t.getAddress()); - HashMap map = serverMap.get(c); - if (map == null) { - map = new HashMap(); - serverMap.put(c, map); - } - map.put(t.getAddress(), t); - } - - private void killServerForChannel(Channel c, String name) { - LOG.info("Killing server " + name); - HashMap map = serverMap.get(c); - ServerControl.TestServer t = map.get(name); - map.remove(name); - try { - t.kill(); - } catch (Exception e) { - e.printStackTrace(); - // do nothing, should be killed, we won't use it again anyhow - } - } - - private ServerControl.TestServer lookupServer(Channel c, String name) { - HashMap map = serverMap.get(c); - return map.get(name); - } - - private void clearServersForChannel(Channel c) { - HashMap map = serverMap.get(c); - serverMap.remove(map); - - if (map != null) { - for (ServerControl.TestServer t : map.values()) { - t.kill(); - } - map.clear(); - } - } - - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { - try { - String command = (String)e.getMessage(); - LOG.info("Command: " + command); - String[] args = command.split("\\s+"); - - if (args[0].equals("START")) { - ServerControl.TestServer t = null; - if (args[1].equals("ZOOKEEPER")) { - t = control.startZookeeperServer(Integer.valueOf(args[2])); - addServerForChannel(ctx.getChannel(), t); - } else if (args[1].equals("BOOKKEEPER")) { - ServerControl.TestServer zk = lookupServer(ctx.getChannel(), args[3]); - t = control.startBookieServer(Integer.valueOf(args[2]), zk); - addServerForChannel(ctx.getChannel(), t); - } else if (args[1].equals("HEDWIG")) { - ServerControl.TestServer zk = lookupServer(ctx.getChannel(), args[4]); - t = control.startPubSubServer(Integer.valueOf(args[2]), args[3], zk); - addServerForChannel(ctx.getChannel(), t); - } - - ctx.getChannel().write("OK " + t.getAddress() + "\n"); - } else if (args[0].equals("KILL")) { - killServerForChannel(ctx.getChannel(), args[1]); - - ctx.getChannel().write("OK Killed " + args[1] + "\n"); - } else if (args[0].equals("TEST")) { - LOG.info("\n******\n\n" + args[1] + "\n\n******"); - ctx.getChannel().write("OK Test Noted\n"); - } else { - ctx.getChannel().write("ERR Bad Command\n"); - } - } catch (Exception ex) { - ex.printStackTrace(); - ctx.getChannel().write("ERR " + ex.toString() + "\n"); - } - } - - public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - clearServersForChannel(ctx.getChannel()); - } - } - - public static void main(String[] args) throws Exception { - // Configure the server. - int port = 5672; - if (args.length == 1) { - port = Integer.valueOf(args[0]); - } - ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), - Executors.newCachedThreadPool())); - // Set up the pipeline factory. - bootstrap.setPipelineFactory(new ChannelPipelineFactory() { - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline p = Channels.pipeline(); - p.addLast("frameDecoder", new DelimiterBasedFrameDecoder(80, Delimiters.lineDelimiter())); - p.addLast("stringDecoder", new StringDecoder("UTF-8")); - - // Encoder - p.addLast("stringEncoder", new StringEncoder("UTF-8")); - p.addLast("handler", new ServerControlDaemonHandler()); - - return p; - } - }); - - LOG.info("Listening on localhost:"+port); - // Bind and start to accept incoming connections. - bootstrap.bind(new InetSocketAddress(port)); - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubCallback.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubCallback.java deleted file mode 100644 index 89790aab4e2..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubCallback.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig; - -import java.util.concurrent.SynchronousQueue; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Either; -import org.apache.hedwig.util.Callback; - -public class StubCallback implements Callback { - - public SynchronousQueue> queue = new SynchronousQueue>(); - - public void operationFailed(Object ctx, final PubSubException exception) { - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(queue, Either.of((T) null, exception)); - - } - }).start(); - - } - - public void operationFinished(Object ctx, final T resultOfOperation) { - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(queue, Either.of(resultOfOperation, (PubSubException) null)); - - } - }).start(); - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubScanCallback.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubScanCallback.java deleted file mode 100644 index 776a7f50a79..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/StubScanCallback.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.server.persistence.ScanCallback; - -public class StubScanCallback implements ScanCallback { - List messages = new ArrayList(); - boolean success = false, failed = false; - - public void messageScanned(Object ctx, Message message) { - messages.add(message); - success = true; - } - - public void scanFailed(Object ctx, Exception exception) { - failed = true; - success = false; - } - - public void scanFinished(Object ctx, ReasonForFinish reason) { - success = true; - failed = false; - } - - public List getMessages() { - return messages; - } - - public boolean isSuccess() { - return success; - } - - public boolean isFailed() { - return failed; - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/client/TestPubSubClient.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/client/TestPubSubClient.java deleted file mode 100644 index c5f7c622f14..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/client/TestPubSubClient.java +++ /dev/null @@ -1,230 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.client; - -import java.util.concurrent.SynchronousQueue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigPublisher; -import org.apache.hedwig.client.netty.HedwigSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.server.PubSubServerStandAloneTestBase; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.ConcurrencyUtils; - -public class TestPubSubClient extends PubSubServerStandAloneTestBase { - - // Client side variables - protected HedwigClient client; - protected HedwigPublisher publisher; - protected HedwigSubscriber subscriber; - - // SynchronousQueues to verify async calls - private final SynchronousQueue queue = new SynchronousQueue(); - private final SynchronousQueue consumeQueue = new SynchronousQueue(); - - // Test implementation of Callback for async client actions. - class TestCallback implements Callback { - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - new Thread(new Runnable() { - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Operation finished!"); - ConcurrencyUtils.put(queue, true); - } - }).start(); - } - - @Override - public void operationFailed(Object ctx, final PubSubException exception) { - new Thread(new Runnable() { - @Override - public void run() { - logger.error("Operation failed!", exception); - ConcurrencyUtils.put(queue, false); - } - }).start(); - } - } - - // Test implementation of subscriber's message handler. - class TestMessageHandler implements MessageHandler { - public void consume(ByteString topic, ByteString subscriberId, Message msg, Callback callback, - Object context) { - new Thread(new Runnable() { - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Consume operation finished successfully!"); - ConcurrencyUtils.put(consumeQueue, true); - } - }).start(); - callback.operationFinished(context, null); - } - } - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - client = new HedwigClient(new ClientConfiguration()); - publisher = client.getPublisher(); - subscriber = client.getSubscriber(); - } - - @Override - @After - public void tearDown() throws Exception { - client.stop(); - super.tearDown(); - } - - @Test - public void testSyncPublish() throws Exception { - boolean publishSuccess = true; - try { - publisher.publish(ByteString.copyFromUtf8("mySyncTopic"), Message.newBuilder().setBody( - ByteString.copyFromUtf8("Hello Sync World!")).build()); - } catch (Exception e) { - publishSuccess = false; - } - assertTrue(publishSuccess); - } - - @Test - public void testAsyncPublish() throws Exception { - publisher.asyncPublish(ByteString.copyFromUtf8("myAsyncTopic"), Message.newBuilder().setBody( - ByteString.copyFromUtf8("Hello Async World!")).build(), new TestCallback(), null); - assertTrue(queue.take()); - } - - @Test - public void testMultipleAsyncPublish() throws Exception { - ByteString topic1 = ByteString.copyFromUtf8("myFirstTopic"); - ByteString topic2 = ByteString.copyFromUtf8("myNewTopic"); - - publisher.asyncPublish(topic1, Message.newBuilder().setBody(ByteString.copyFromUtf8("Hello World!")).build(), - new TestCallback(), null); - assertTrue(queue.take()); - publisher.asyncPublish(topic2, Message.newBuilder().setBody(ByteString.copyFromUtf8("Hello on new topic!")) - .build(), new TestCallback(), null); - assertTrue(queue.take()); - publisher.asyncPublish(topic1, Message.newBuilder().setBody( - ByteString.copyFromUtf8("Hello Again on old topic!")).build(), new TestCallback(), null); - assertTrue(queue.take()); - } - - @Test - public void testSyncSubscribe() throws Exception { - boolean subscribeSuccess = true; - try { - subscriber.subscribe(ByteString.copyFromUtf8("mySyncSubscribeTopic"), ByteString.copyFromUtf8("1"), CreateOrAttach.CREATE_OR_ATTACH); - } catch (Exception e) { - subscribeSuccess = false; - } - assertTrue(subscribeSuccess); - } - - @Test - public void testAsyncSubscribe() throws Exception { - subscriber.asyncSubscribe(ByteString.copyFromUtf8("myAsyncSubscribeTopic"), ByteString.copyFromUtf8("1"), - CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(), null); - assertTrue(queue.take()); - } - - @Test - public void testSubscribeAndConsume() throws Exception { - ByteString topic = ByteString.copyFromUtf8("myConsumeTopic"); - ByteString subscriberId = ByteString.copyFromUtf8("1"); - subscriber.asyncSubscribe(topic, subscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(), null); - assertTrue(queue.take()); - - // Start delivery for the subscriber - subscriber.startDelivery(topic, subscriberId, new TestMessageHandler()); - - // Now publish some messages for the topic to be consumed by the - // subscriber. - publisher.asyncPublish(topic, Message.newBuilder().setBody(ByteString.copyFromUtf8("Message #1")).build(), - new TestCallback(), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - publisher.asyncPublish(topic, Message.newBuilder().setBody(ByteString.copyFromUtf8("Message #2")).build(), - new TestCallback(), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - publisher.asyncPublish(topic, Message.newBuilder().setBody(ByteString.copyFromUtf8("Message #3")).build(), - new TestCallback(), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - publisher.asyncPublish(topic, Message.newBuilder().setBody(ByteString.copyFromUtf8("Message #4")).build(), - new TestCallback(), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - publisher.asyncPublish(topic, Message.newBuilder().setBody(ByteString.copyFromUtf8("Message #5")).build(), - new TestCallback(), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - } - - @Test - public void testAsyncSubscribeAndUnsubscribe() throws Exception { - ByteString topic = ByteString.copyFromUtf8("myAsyncUnsubTopic"); - ByteString subscriberId = ByteString.copyFromUtf8("1"); - subscriber.asyncSubscribe(topic, subscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(), null); - assertTrue(queue.take()); - subscriber.asyncUnsubscribe(topic, subscriberId, new TestCallback(), null); - assertTrue(queue.take()); - } - - @Test - public void testSyncUnsubscribeWithoutSubscription() throws Exception { - boolean unsubscribeSuccess = false; - try { - subscriber.unsubscribe(ByteString.copyFromUtf8("mySyncUnsubTopic"), ByteString.copyFromUtf8("1")); - } catch (ClientNotSubscribedException e) { - unsubscribeSuccess = true; - } catch (Exception ex) { - unsubscribeSuccess = false; - } - assertTrue(unsubscribeSuccess); - } - - @Test - public void testAsyncSubscribeAndCloseSubscription() throws Exception { - ByteString topic = ByteString.copyFromUtf8("myAsyncSubAndCloseSubTopic"); - ByteString subscriberId = ByteString.copyFromUtf8("1"); - subscriber.asyncSubscribe(topic, subscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(), null); - assertTrue(queue.take()); - subscriber.closeSubscription(topic, subscriberId); - assertTrue(true); - } - -} \ No newline at end of file diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigHubTestBase.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigHubTestBase.java deleted file mode 100644 index 5ab97a7c0b5..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigHubTestBase.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server; - -import java.util.LinkedList; -import java.util.List; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.junit.After; -import org.junit.Before; - -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.netty.PubSubServer; -import org.apache.hedwig.server.persistence.BookKeeperTestBase; - -/** - * This is a base class for any tests that need a Hedwig Hub(s) setup with an - * associated BookKeeper and ZooKeeper instance. - * - */ -public abstract class HedwigHubTestBase extends TestCase { - - protected static Logger logger = Logger.getLogger(HedwigHubTestBase.class); - - // BookKeeper variables - // Default number of bookie servers to setup. Extending classes can - // override this. - protected int numBookies = 3; - protected BookKeeperTestBase bktb; - - // PubSubServer variables - // Default number of PubSubServer hubs to setup. Extending classes can - // override this. - protected int numServers = 1; - protected int initialServerPort = 4080; - protected int initialSSLServerPort = 9876; - protected List serversList; - - // Default child class of the ServerConfiguration to be used here. - // Extending classes can define their own (possibly extending from this) and - // override the getServerConfiguration method below to return their own - // configuration. - protected class HubServerConfiguration extends ServerConfiguration { - private final int serverPort, sslServerPort; - - public HubServerConfiguration(int serverPort, int sslServerPort) { - this.serverPort = serverPort; - this.sslServerPort = sslServerPort; - } - - @Override - public int getServerPort() { - return serverPort; - } - - @Override - public int getSSLServerPort() { - return sslServerPort; - } - - @Override - public String getZkHost() { - return bktb.getZkHostPort(); - } - - @Override - public boolean isSSLEnabled() { - return true; - } - - @Override - public String getCertName() { - return "/server.p12"; - } - - @Override - public String getPassword() { - return "eUySvp2phM2Wk"; - } - } - - // Method to get a ServerConfiguration for the PubSubServers created using - // the specified ports. Extending child classes can override this. This - // default implementation will return the HubServerConfiguration object - // defined above. - protected ServerConfiguration getServerConfiguration(int serverPort, int sslServerPort) { - return new HubServerConfiguration(serverPort, sslServerPort); - } - - @Override - @Before - public void setUp() throws Exception { - logger.info("STARTING " + getName()); - bktb = new BookKeeperTestBase(numBookies); - bktb.setUp(); - // Now create the PubSubServer Hubs - serversList = new LinkedList(); - for (int i = 0; i < numServers; i++) { - serversList.add(new PubSubServer(getServerConfiguration(initialServerPort + i, initialSSLServerPort + i))); - } - logger.info("HedwigHub test setup finished"); - } - - @Override - @After - public void tearDown() throws Exception { - logger.info("tearDown starting"); - // Shutdown all of the PubSubServers - for (PubSubServer server : serversList) { - server.shutdown(); - } - bktb.tearDown(); - logger.info("FINISHED " + getName()); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigRegionTestBase.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigRegionTestBase.java deleted file mode 100644 index 7c621c2c57f..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/HedwigRegionTestBase.java +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server; - -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.junit.After; -import org.junit.Before; - -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.netty.PubSubServer; -import org.apache.hedwig.server.persistence.BookKeeperTestBase; -import org.apache.hedwig.util.HedwigSocketAddress; - -/** - * This is a base class for any tests that need a Hedwig Region(s) setup with a - * number of Hedwig hubs per region, an associated HedwigClient per region and - * the required BookKeeper and ZooKeeper instances. - * - */ -public abstract class HedwigRegionTestBase extends TestCase { - - protected static Logger logger = Logger.getLogger(HedwigRegionTestBase.class); - - // BookKeeper variables - // Default number of bookie servers to setup. Extending classes - // can override this. We should be able to reuse the same BookKeeper - // ensemble among all of the regions, at least for unit testing purposes. - protected int numBookies = 3; - protected BookKeeperTestBase bktb; - - // Hedwig Region variables - // Default number of Hedwig Regions to setup. Extending classes can - // override this. - protected int numRegions = 2; - protected int numServersPerRegion = 1; - protected int initialServerPort = 4080; - protected int initialSSLServerPort = 9876; - // Map with keys being Region names and values being the list of Hedwig - // Hubs (PubSubServers) for that particular region. - protected Map> regionServersMap; - // Map with keys being Region names and values being the Hedwig Client - // instance. - protected Map regionClientsMap; - - // String constant used as the prefix for the region names. - protected static final String REGION_PREFIX = "region"; - - // Default child class of the ServerConfiguration to be used here. - // Extending classes can define their own (possibly extending from this) and - // override the getServerConfiguration method below to return their own - // configuration. - protected class RegionServerConfiguration extends ServerConfiguration { - private final int serverPort, sslServerPort; - private final String regionName; - - public RegionServerConfiguration(int serverPort, int sslServerPort, String regionName) { - this.serverPort = serverPort; - this.sslServerPort = sslServerPort; - this.regionName = regionName; - setRegionList(); - } - - protected void setRegionList() { - List myRegionList = new LinkedList(); - for (int i = 0; i < numRegions; i++) { - int curDefaultServerPort = initialServerPort + (i * numServersPerRegion); - int curDefaultSSLServerPort = initialSSLServerPort + (i * numServersPerRegion); - // Add this region default server port if it is for a region - // other than its own. - if (curDefaultServerPort > serverPort - || Math.abs(serverPort - curDefaultServerPort) >= numServersPerRegion) - myRegionList.add("localhost:" + curDefaultServerPort + ":" + curDefaultSSLServerPort); - } - regionList = myRegionList; - } - - @Override - public int getServerPort() { - return serverPort; - } - - @Override - public int getSSLServerPort() { - return sslServerPort; - } - - @Override - public String getZkHost() { - return bktb.getZkHostPort(); - } - - @Override - public String getMyRegion() { - return regionName; - } - - @Override - public boolean isSSLEnabled() { - return true; - } - - @Override - public boolean isInterRegionSSLEnabled() { - return true; - } - - @Override - public String getCertName() { - return "/server.p12"; - } - - @Override - public String getPassword() { - return "eUySvp2phM2Wk"; - } - } - - // Method to get a ServerConfiguration for the PubSubServers created using - // the specified ports and region name. Extending child classes can override - // this. This default implementation will return the - // RegionServerConfiguration object defined above. - protected ServerConfiguration getServerConfiguration(int serverPort, int sslServerPort, String regionName) { - return new RegionServerConfiguration(serverPort, sslServerPort, regionName); - } - - // Default ClientConfiguration to use. This just points to the first - // Hedwig hub server in each region as the "default server host" to connect - // to. - protected class RegionClientConfiguration extends ClientConfiguration { - public RegionClientConfiguration(int serverPort, int sslServerPort) { - myDefaultServerAddress = new HedwigSocketAddress("localhost:" + serverPort + ":" + sslServerPort); - } - // Below you can override any of the default ClientConfiguration - // parameters if needed. - } - - // Method to get a ClientConfiguration for the HedwigClients created. - // Inputs are the default Hedwig hub server's ports to point to. - protected ClientConfiguration getClientConfiguration(int serverPort, int sslServerPort) { - return new RegionClientConfiguration(serverPort, sslServerPort); - } - - @Override - @Before - public void setUp() throws Exception { - logger.info("STARTING " + getName()); - bktb = new BookKeeperTestBase(numBookies); - bktb.setUp(); - - // Create the Hedwig PubSubServer Hubs for all of the regions - regionServersMap = new HashMap>(numRegions, 1.0f); - regionClientsMap = new HashMap(numRegions, 1.0f); - for (int i = 0; i < numRegions; i++) { - List serversList = new LinkedList(); - // For the current region, create the necessary amount of hub - // servers. We will basically increment through the port numbers - // starting from the initial ones defined. - for (int j = 0; j < numServersPerRegion; j++) { - serversList.add(new PubSubServer(getServerConfiguration(initialServerPort - + (j + i * numServersPerRegion), initialSSLServerPort + (j + i * numServersPerRegion), - REGION_PREFIX + i))); - } - // Store this list of servers created for the current region - regionServersMap.put(REGION_PREFIX + i, serversList); - - // Create a Hedwig Client that points to the first Hub server - // created in the loop above for the current region. - HedwigClient regionClient = new HedwigClient(getClientConfiguration(initialServerPort - + (i * numServersPerRegion), initialSSLServerPort + (i * numServersPerRegion))); - regionClientsMap.put(REGION_PREFIX + i, regionClient); - } - logger.info("HedwigRegion test setup finished"); - } - - @Override - @After - public void tearDown() throws Exception { - logger.info("tearDown starting"); - // Stop all of the HedwigClients for all regions - for (HedwigClient client : regionClientsMap.values()) { - client.stop(); - } - regionClientsMap.clear(); - // Shutdown all of the PubSubServers in all regions - for (List serversList : regionServersMap.values()) { - for (PubSubServer server : serversList) { - server.shutdown(); - } - } - logger.info("Finished shutting down all of the hub servers!"); - regionServersMap.clear(); - // Shutdown the BookKeeper and ZooKeeper stuff - bktb.tearDown(); - logger.info("FINISHED " + getName()); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/PubSubServerStandAloneTestBase.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/PubSubServerStandAloneTestBase.java deleted file mode 100644 index 1e3d5dd29ab..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/PubSubServerStandAloneTestBase.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.junit.After; -import org.junit.Before; - -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.netty.PubSubServer; - -/** - * This is a base class for any tests that need a StandAlone PubSubServer setup. - */ -public abstract class PubSubServerStandAloneTestBase extends TestCase { - - protected static Logger logger = Logger.getLogger(PubSubServerStandAloneTestBase.class); - - protected class StandAloneServerConfiguration extends ServerConfiguration { - @Override - public boolean isStandalone() { - return true; - } - } - - protected PubSubServer server; - - @Override - @Before - public void setUp() throws Exception { - logger.info("STARTING " + getName()); - server = new PubSubServer(new StandAloneServerConfiguration()); - logger.info("Standalone PubSubServer test setup finished"); - } - - @Override - @After - public void tearDown() throws Exception { - logger.info("tearDown starting"); - server.shutdown(); - logger.info("FINISHED " + getName()); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/delivery/StubDeliveryManager.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/delivery/StubDeliveryManager.java deleted file mode 100644 index 25b2ace6d5d..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/delivery/StubDeliveryManager.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.delivery; - -import java.util.LinkedList; -import java.util.Queue; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.server.subscriptions.MessageFilter; - -public class StubDeliveryManager implements DeliveryManager { - - public static class StartServingRequest { - public ByteString topic; - public ByteString subscriberId; - public MessageSeqId seqIdToStartFrom; - public DeliveryEndPoint endPoint; - public MessageFilter filter; - public boolean isHubSubscriber; - - public StartServingRequest(ByteString topic, ByteString subscriberId, MessageSeqId seqIdToStartFrom, - DeliveryEndPoint endPoint, MessageFilter filter, boolean isHubSubscriber) { - this.topic = topic; - this.subscriberId = subscriberId; - this.seqIdToStartFrom = seqIdToStartFrom; - this.endPoint = endPoint; - this.filter = filter; - this.isHubSubscriber = isHubSubscriber; - } - - } - - public Queue lastRequest = new LinkedList(); - - @Override - public void startServingSubscription(ByteString topic, ByteString subscriberId, MessageSeqId seqIdToStartFrom, - DeliveryEndPoint endPoint, MessageFilter filter, boolean isHubSubscriber) { - - lastRequest.add(new StartServingRequest(topic, subscriberId, seqIdToStartFrom, endPoint, filter, - isHubSubscriber)); - - } - - @Override - public void stopServingSubscriber(ByteString topic, ByteString subscriberId) { - lastRequest.add(new TopicSubscriber(topic, subscriberId)); - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestBaseHandler.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestBaseHandler.java deleted file mode 100644 index 14a24beed43..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestBaseHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import java.util.List; -import junit.framework.TestCase; - -import org.jboss.netty.channel.Channel; -import org.junit.Before; -import org.junit.Test; - -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.netty.WriteRecordingChannel; -import org.apache.hedwig.server.topics.StubTopicManager; -import org.apache.hedwig.server.topics.TopicManager; - -public class TestBaseHandler extends TestCase { - - MyBaseHandler handler; - StubTopicManager tm; - PubSubRequest request = PubSubRequest.getDefaultInstance(); - WriteRecordingChannel channel = new WriteRecordingChannel(); - - protected class MyBaseHandler extends BaseHandler { - - public MyBaseHandler(TopicManager tm, ServerConfiguration conf) { - super(tm, conf); - } - - PubSubRequest request; - - public PubSubRequest getRequest() { - return request; - } - - @Override - public void handleRequestAtOwner(PubSubRequest request, Channel channel) { - this.request = request; - } - - } - - @Override - @Before - public void setUp() throws Exception { - ServerConfiguration conf = new ServerConfiguration(); - tm = new StubTopicManager(conf); - handler = new MyBaseHandler(tm, conf); - request = PubSubRequest.getDefaultInstance(); - channel = new WriteRecordingChannel(); - } - - public PubSubResponse getPubSubResponse(WriteRecordingChannel channel) { - List messages = channel.getMessagesWritten(); - assertEquals(messages.size(), 1); - - Object message = messages.get(0); - assertEquals(message.getClass(), PubSubResponse.class); - - return (PubSubResponse) message; - } - - @Test - public void testHandleRequestOnRedirect() throws Exception { - tm.setShouldOwnEveryNewTopic(false); - handler.handleRequest(request, channel); - - PubSubResponse response = getPubSubResponse(channel); - assertEquals(response.getStatusCode(), StatusCode.NOT_RESPONSIBLE_FOR_TOPIC); - assertEquals(request.getTxnId(), response.getTxnId()); - assertNull(handler.getRequest()); - - } - - @Test - public void testHandleRequestOnOwner() throws Exception { - - tm.setShouldOwnEveryNewTopic(true); - handler.handleRequest(request, channel); - assertEquals(0, channel.getMessagesWritten().size()); - assertEquals(handler.getRequest(), request); - - } - - @Test - public void testHandleRequestOnError() throws Exception { - - tm.setShouldError(true); - handler.handleRequest(request, channel); - - PubSubResponse response = getPubSubResponse(channel); - assertEquals(response.getStatusCode(), StatusCode.SERVICE_DOWN); - assertEquals(request.getTxnId(), response.getTxnId()); - assertNull(handler.getRequest()); - - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestSubUnsubHandler.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestSubUnsubHandler.java deleted file mode 100644 index dc4b4d1d4e3..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/handlers/TestSubUnsubHandler.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.handlers; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.StubCallback; -import org.apache.hedwig.client.data.TopicSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.UnsubscribeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.delivery.ChannelEndPoint; -import org.apache.hedwig.server.delivery.StubDeliveryManager; -import org.apache.hedwig.server.delivery.StubDeliveryManager.StartServingRequest; -import org.apache.hedwig.server.netty.WriteRecordingChannel; -import org.apache.hedwig.server.persistence.LocalDBPersistenceManager; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.subscriptions.StubSubscriptionManager; -import org.apache.hedwig.server.subscriptions.TrueFilter; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.server.topics.TrivialOwnAllTopicManager; -import org.apache.hedwig.util.ConcurrencyUtils; - -import junit.framework.TestCase; - -public class TestSubUnsubHandler extends TestCase { - - SubscribeHandler sh; - StubDeliveryManager dm; - StubSubscriptionManager sm; - ByteString topic = ByteString.copyFromUtf8("topic"); - WriteRecordingChannel channel; - - SubscribeRequest subRequestPrototype; - PubSubRequest pubSubRequestPrototype; - ByteString subscriberId; - UnsubscribeHandler ush; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - ServerConfiguration conf = new ServerConfiguration(); - ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); - - TopicManager tm = new TrivialOwnAllTopicManager(conf, executor); - dm = new StubDeliveryManager(); - PersistenceManager pm = LocalDBPersistenceManager.instance(); - sm = new StubSubscriptionManager(tm, pm, conf, executor); - sh = new SubscribeHandler(tm, dm, pm, sm, conf); - channel = new WriteRecordingChannel(); - - subscriberId = ByteString.copyFromUtf8("subId"); - - subRequestPrototype = SubscribeRequest.newBuilder().setSubscriberId(subscriberId).build(); - pubSubRequestPrototype = PubSubRequest.newBuilder().setProtocolVersion(ProtocolVersion.VERSION_ONE).setType( - OperationType.SUBSCRIBE).setTxnId(0).setTopic(topic).setSubscribeRequest(subRequestPrototype).build(); - - ush = new UnsubscribeHandler(tm, conf, sm, dm); - } - - @Test - public void testNoSubscribeRequest() { - sh.handleRequestAtOwner(PubSubRequest.newBuilder(pubSubRequestPrototype).clearSubscribeRequest().build(), - channel); - assertEquals(StatusCode.MALFORMED_REQUEST, ((PubSubResponse) channel.getMessagesWritten().get(0)) - .getStatusCode()); - } - - @Test - public void testSuccessCase() { - StubCallback callback = new StubCallback(); - sm.acquiredTopic(topic, callback, null); - assertNull(ConcurrencyUtils.take(callback.queue).right()); - - sh.handleRequestAtOwner(pubSubRequestPrototype, channel); - assertEquals(StatusCode.SUCCESS, ((PubSubResponse) channel.getMessagesWritten().get(0)).getStatusCode()); - - // make sure the channel was put in the maps - assertEquals(new TopicSubscriber(topic, subscriberId), sh.channel2sub.get(channel)); - assertEquals(channel, sh.sub2Channel.get(new TopicSubscriber(topic, subscriberId))); - - // make sure delivery was started - StartServingRequest startRequest = (StartServingRequest) dm.lastRequest.poll(); - assertEquals(channel, ((ChannelEndPoint) startRequest.endPoint).getChannel()); - assertEquals(false, startRequest.isHubSubscriber); - assertEquals(TrueFilter.class, startRequest.filter.getClass()); - assertEquals(1, startRequest.seqIdToStartFrom.getLocalComponent()); - assertEquals(subscriberId, startRequest.subscriberId); - assertEquals(topic, startRequest.topic); - - // make sure subscription was registered - StubCallback callback1 = new StubCallback(); - sm.serveSubscribeRequest(topic, SubscribeRequest.newBuilder(subRequestPrototype).setCreateOrAttach( - CreateOrAttach.CREATE).build(), MessageSeqId.newBuilder().setLocalComponent(10).build(), callback1, - null); - - assertEquals(PubSubException.ClientAlreadySubscribedException.class, ConcurrencyUtils.take(callback1.queue) - .right().getClass()); - - // trying to subscribe again should throw an error - WriteRecordingChannel dupChannel = new WriteRecordingChannel(); - sh.handleRequestAtOwner(pubSubRequestPrototype, dupChannel); - assertEquals(StatusCode.TOPIC_BUSY, ((PubSubResponse) dupChannel.getMessagesWritten().get(0)).getStatusCode()); - - // after disconnecting the channel, subscribe should work again - sh.channelDisconnected(channel); - - dupChannel = new WriteRecordingChannel(); - sh.handleRequestAtOwner(pubSubRequestPrototype, dupChannel); - assertEquals(StatusCode.SUCCESS, ((PubSubResponse) dupChannel.getMessagesWritten().get(0)).getStatusCode()); - - // test unsubscribe - channel = new WriteRecordingChannel(); - ush.handleRequestAtOwner(pubSubRequestPrototype, channel); - assertEquals(StatusCode.MALFORMED_REQUEST, ((PubSubResponse) channel.getMessagesWritten().get(0)) - .getStatusCode()); - - PubSubRequest unsubRequest = PubSubRequest.newBuilder(pubSubRequestPrototype).setUnsubscribeRequest( - UnsubscribeRequest.newBuilder().setSubscriberId(subscriberId)).build(); - channel = new WriteRecordingChannel(); - dm.lastRequest.clear(); - - ush.handleRequestAtOwner(unsubRequest, channel); - assertEquals(StatusCode.SUCCESS, ((PubSubResponse) channel.getMessagesWritten().get(0)).getStatusCode()); - - // make sure delivery has been stopped - assertEquals(new TopicSubscriber(topic, subscriberId), dm.lastRequest.poll()); - - // make sure the info is gone from the sm - StubCallback callback2 = new StubCallback(); - sm.serveSubscribeRequest(topic, SubscribeRequest.newBuilder(subRequestPrototype).setCreateOrAttach( - CreateOrAttach.ATTACH).build(), MessageSeqId.newBuilder().setLocalComponent(10).build(), callback2, - null); - assertEquals(PubSubException.ClientNotSubscribedException.class, ConcurrencyUtils.take(callback2.queue).right() - .getClass()); - - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigHub.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigHub.java deleted file mode 100644 index c24b226a01c..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigHub.java +++ /dev/null @@ -1,692 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.integration; - -import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.concurrent.SynchronousQueue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.api.MessageHandler; -import org.apache.hedwig.client.api.Subscriber; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.exceptions.InvalidSubscriberIdException; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigPublisher; -import org.apache.hedwig.client.netty.HedwigSubscriber; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.ClientNotSubscribedException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.OperationType; -import org.apache.hedwig.protocol.PubSubProtocol.ProtocolVersion; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubRequest; -import org.apache.hedwig.protocol.PubSubProtocol.PubSubResponse; -import org.apache.hedwig.protocol.PubSubProtocol.StartDeliveryRequest; -import org.apache.hedwig.protocol.PubSubProtocol.StatusCode; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.protoextensions.SubscriptionStateUtils; -import org.apache.hedwig.server.HedwigHubTestBase; -import org.apache.hedwig.server.netty.WriteRecordingChannel; -import org.apache.hedwig.server.proxy.HedwigProxy; -import org.apache.hedwig.server.proxy.ProxyConfiguration; -import org.apache.hedwig.server.regions.HedwigHubClient; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.ConcurrencyUtils; - -@RunWith(Parameterized.class) -public class TestHedwigHub extends HedwigHubTestBase { - - // Client side variables - protected HedwigClient client; - protected HedwigPublisher publisher; - protected HedwigSubscriber subscriber; - - // Common ByteStrings used in tests. - private final ByteString localSubscriberId = ByteString.copyFromUtf8("LocalSubscriber"); - private final ByteString hubSubscriberId = ByteString.copyFromUtf8(SubscriptionStateUtils.HUB_SUBSCRIBER_PREFIX - + "HubSubcriber"); - - enum Mode { - REGULAR, PROXY, SSL - }; - - @Parameters - public static Collection configs() { - return Arrays.asList(new Object[][] { { Mode.PROXY }, { Mode.REGULAR }, { Mode.SSL }}); - } - - protected Mode mode; - - public TestHedwigHub(Mode mode) { - this.mode = mode; - } - - protected HedwigProxy proxy; - protected ProxyConfiguration proxyConf = new ProxyConfiguration(); - - // SynchronousQueues to verify async calls - private final SynchronousQueue queue = new SynchronousQueue(); - private final SynchronousQueue consumeQueue = new SynchronousQueue(); - - // Test implementation of Callback for async client actions. - static class TestCallback implements Callback { - private final SynchronousQueue queue; - - public TestCallback(SynchronousQueue queue) { - this.queue = queue; - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - new Thread(new Runnable() { - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Operation finished!"); - ConcurrencyUtils.put(queue, true); - } - }).start(); - } - - @Override - public void operationFailed(Object ctx, final PubSubException exception) { - new Thread(new Runnable() { - @Override - public void run() { - logger.error("Operation failed!", exception); - ConcurrencyUtils.put(queue, false); - } - }).start(); - } - } - - // Test implementation of subscriber's message handler. - static class TestMessageHandler implements MessageHandler { - // For subscribe reconnect testing, the server could send us back - // messages we've already processed and consumed. We need to keep - // track of the ones we've encountered so we only signal back to the - // consumeQueue once. - private HashSet consumedMessages = new HashSet(); - private long largestMsgSeqIdConsumed = -1; - private final SynchronousQueue consumeQueue; - - public TestMessageHandler(SynchronousQueue consumeQueue) { - this.consumeQueue = consumeQueue; - } - - public void consume(ByteString topic, ByteString subscriberId, final Message msg, Callback callback, - Object context) { - if (!consumedMessages.contains(msg.getMsgId())) { - // New message to consume. Add it to the Set of consumed - // messages. - consumedMessages.add(msg.getMsgId()); - // Check that the msg seq ID is incrementing by 1 compared to - // the last consumed message. Don't do this check if this is the - // initial message being consumed. - if (largestMsgSeqIdConsumed >= 0 && msg.getMsgId().getLocalComponent() != largestMsgSeqIdConsumed + 1) { - new Thread(new Runnable() { - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Consuming message that is out of order for msgId: " - + msg.getMsgId().getLocalComponent()); - ConcurrencyUtils.put(consumeQueue, false); - } - }).start(); - } else { - new Thread(new Runnable() { - @Override - public void run() { - if (logger.isDebugEnabled()) - logger.debug("Consume operation finished successfully!"); - ConcurrencyUtils.put(consumeQueue, true); - } - }).start(); - } - // Store the consumed message as the new last msg id consumed. - largestMsgSeqIdConsumed = msg.getMsgId().getLocalComponent(); - } else { - if (logger.isDebugEnabled()) - logger.debug("Consumed a message that we've processed already: " + msg); - } - callback.operationFinished(context, null); - } - } - - class TestClientConfiguration extends ClientConfiguration { - @Override - public InetSocketAddress getDefaultServerHost() { - if (mode == Mode.PROXY) { - return new InetSocketAddress(proxyConf.getProxyPort()); - } else { - return super.getDefaultServerHost(); - } - } - - @Override - public boolean isSSLEnabled() { - if (mode == Mode.SSL) - return true; - else - return false; - } - } - - // ClientConfiguration to use for this test. - protected ClientConfiguration getClientConfiguration() { - return new TestClientConfiguration(); - } - - @Override - @Before - public void setUp() throws Exception { - numServers = 3; - super.setUp(); - if (mode == Mode.PROXY) { - proxy = new HedwigProxy(proxyConf); - } - client = new HedwigClient(getClientConfiguration()); - publisher = client.getPublisher(); - subscriber = client.getSubscriber(); - } - - @Override - @After - public void tearDown() throws Exception { - client.stop(); - if (mode == Mode.PROXY) { - proxy.shutdown(); - } - super.tearDown(); - - } - - // Helper function to generate Messages - protected Message getMsg(int msgNum) { - return Message.newBuilder().setBody(ByteString.copyFromUtf8("Message" + msgNum)).build(); - } - - // Helper function to generate Topics - protected ByteString getTopic(int topicNum) { - return ByteString.copyFromUtf8("Topic" + topicNum); - } - - protected void startDelivery(ByteString topic, ByteString subscriberId, MessageHandler handler) throws Exception { - startDelivery(subscriber, topic, subscriberId, handler); - } - - protected void startDelivery(Subscriber subscriber, ByteString topic, ByteString subscriberId, - MessageHandler handler) throws Exception { - subscriber.startDelivery(topic, subscriberId, handler); - if (mode == Mode.PROXY) { - WriteRecordingChannel channel = new WriteRecordingChannel(); - PubSubRequest request = PubSubRequest.newBuilder().setProtocolVersion(ProtocolVersion.VERSION_ONE) - .setTopic(topic).setTxnId(0).setType(OperationType.START_DELIVERY).setStartDeliveryRequest( - StartDeliveryRequest.newBuilder().setSubscriberId(subscriberId)).build(); - proxy.getStartDeliveryHandler().handleRequest(request, channel); - assertEquals(StatusCode.SUCCESS, ((PubSubResponse) channel.getMessagesWritten().get(0)).getStatusCode()); - } - } - - protected void publishFirstBatch(int batchSize, boolean messagesToBeConsumed) throws Exception { - if (logger.isDebugEnabled()) - logger.debug("Publishing first batch of messages."); - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(getTopic(i), getMsg(i), new TestCallback(queue), null); - assertTrue(queue.take()); - if (messagesToBeConsumed) - assertTrue(consumeQueue.take()); - } - } - - protected void publishSecondBatch(int batchSize, boolean messagesToBeConsumed) throws Exception { - if (logger.isDebugEnabled()) - logger.debug("Publishing second batch of messages."); - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(getTopic(i), getMsg(i + batchSize), new TestCallback(queue), null); - assertTrue(queue.take()); - if (messagesToBeConsumed) - assertTrue(consumeQueue.take()); - } - } - - protected void subscribeToTopics(int batchSize) throws Exception { - if (logger.isDebugEnabled()) - logger.debug("Subscribing to topics and starting delivery."); - for (int i = 0; i < batchSize; i++) { - subscriber.asyncSubscribe(getTopic(i), localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, - new TestCallback(queue), null); - assertTrue(queue.take()); - } - - // Start delivery for the subscriber - for (int i = 0; i < batchSize; i++) { - startDelivery(getTopic(i), localSubscriberId, new TestMessageHandler(consumeQueue)); - } - } - - protected void shutDownLastServer() { - if (logger.isDebugEnabled()) - logger.debug("Shutting down the last server in the Hedwig hub cluster."); - serversList.get(serversList.size() - 1).shutdown(); - // Due to a possible race condition, after we've shutdown the server, - // the client could still be caching the channel connection to that - // server. It is possible for a publish request to go to the shutdown - // server using the closed/shutdown channel before the channel - // disconnect logic kicks in. What could happen is that the publish - // is done successfully on the channel but the server on the other end - // can't/won't read it. This publish request will time out and the - // Junit test will fail. Since that particular scenario is not what is - // tested here, use a workaround of sleeping in this thread (so the - // channel disconnect logic can complete) before we publish again. - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - logger.error("Thread was interrupted while sleeping after shutting down last server!", e); - } - } - - // This tests out the manual sending of consume messages to the server - // instead of relying on the automatic sending by the client lib for it. - @Test - public void testManualConsumeClient() throws Exception { - HedwigClient myClient = new HedwigClient(new TestClientConfiguration() { - @Override - public boolean isAutoSendConsumeMessageEnabled() { - return false; - } - - }); - HedwigSubscriber mySubscriber = myClient.getSubscriber(); - HedwigPublisher myPublisher = myClient.getPublisher(); - ByteString myTopic = getTopic(0); - // Subscribe to a topic and start delivery on it - mySubscriber.asyncSubscribe(myTopic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, - new TestCallback(queue), null); - assertTrue(queue.take()); - startDelivery(mySubscriber, myTopic, localSubscriberId, new TestMessageHandler(consumeQueue)); - // Publish some messages - int batchSize = 10; - for (int i = 0; i < batchSize; i++) { - myPublisher.asyncPublish(myTopic, getMsg(i), new TestCallback(queue), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - } - // Now manually send a consume message for each message received - for (int i = 0; i < batchSize; i++) { - boolean success = true; - try { - mySubscriber.consume(myTopic, localSubscriberId, MessageSeqId.newBuilder().setLocalComponent(i + 1) - .build()); - } catch (ClientNotSubscribedException e) { - success = false; - } - assertTrue(success); - } - // Since the consume call eventually does an async write to the Netty - // channel, the writing of the consume requests may not have completed - // yet before we stop the client. Sleep a little before we stop the - // client just so error messages are not logged. - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - logger.error("Thread was interrupted while waiting to stop client for manual consume test!!", e); - } - myClient.stop(); - } - - @Test - public void testAttachToSubscriptionSuccess() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(queue), - null); - assertTrue(queue.take()); - // Close the subscription asynchronously - subscriber.asyncCloseSubscription(topic, localSubscriberId, new TestCallback(queue), null); - assertTrue(queue.take()); - // Now try to attach to the subscription - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.ATTACH, new TestCallback(queue), null); - assertTrue(queue.take()); - // Start delivery and publish some messages. Make sure they are consumed - // correctly. - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - int batchSize = 5; - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(topic, getMsg(i), new TestCallback(queue), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - } - } - - @Test - public void testServerRedirect() throws Exception { - int batchSize = 10; - publishFirstBatch(batchSize, false); - } - - @Test - public void testSubscribeAndConsume() throws Exception { - int batchSize = 10; - subscribeToTopics(batchSize); - publishFirstBatch(batchSize, true); - } - - @Test - public void testServerFailoverPublishOnly() throws Exception { - int batchSize = 10; - publishFirstBatch(batchSize, false); - shutDownLastServer(); - publishSecondBatch(batchSize, false); - } - - @Test - public void testServerFailover() throws Exception { - int batchSize = 10; - subscribeToTopics(batchSize); - publishFirstBatch(batchSize, true); - shutDownLastServer(); - publishSecondBatch(batchSize, true); - } - - @Test - public void testUnsubscribe() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(queue), - null); - assertTrue(queue.take()); - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - publisher.asyncPublish(topic, getMsg(0), new TestCallback(queue), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - // Send an Unsubscribe request - subscriber.asyncUnsubscribe(topic, localSubscriberId, new TestCallback(queue), null); - assertTrue(queue.take()); - // Now publish a message and make sure it is not consumed by the client - publisher.asyncPublish(topic, getMsg(1), new TestCallback(queue), null); - assertTrue(queue.take()); - // Wait a little bit just in case the message handler is still active, - // consuming the message, and then putting a true value in the - // consumeQueue. - Thread.sleep(1000); - // Put a False value on the consumeQueue so we can verify that it - // is not blocked by a message consume action which already put a True - // value into the queue. - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(consumeQueue, false); - } - }).start(); - assertFalse(consumeQueue.take()); - } - - @Test - public void testSyncUnsubscribeWithoutSubscription() throws Exception { - boolean unsubscribeSuccess = false; - try { - subscriber.unsubscribe(getTopic(0), localSubscriberId); - } catch (ClientNotSubscribedException e) { - unsubscribeSuccess = true; - } catch (Exception ex) { - unsubscribeSuccess = false; - } - assertTrue(unsubscribeSuccess); - } - - @Test - public void testAsyncUnsubscribeWithoutSubscription() throws Exception { - subscriber.asyncUnsubscribe(getTopic(0), localSubscriberId, new TestCallback(queue), null); - assertFalse(queue.take()); - } - - @Test - public void testCloseSubscription() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(queue), - null); - assertTrue(queue.take()); - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - publisher.asyncPublish(topic, getMsg(0), new TestCallback(queue), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - // Close the subscription asynchronously - subscriber.asyncCloseSubscription(topic, localSubscriberId, new TestCallback(queue), null); - assertTrue(queue.take()); - // Now publish a message and make sure it is not consumed by the client - publisher.asyncPublish(topic, getMsg(1), new TestCallback(queue), null); - assertTrue(queue.take()); - // Wait a little bit just in case the message handler is still active, - // consuming the message, and then putting a true value in the - // consumeQueue. - Thread.sleep(1000); - // Put a False value on the consumeQueue so we can verify that it - // is not blocked by a message consume action which already put a True - // value into the queue. - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(consumeQueue, false); - } - }).start(); - assertFalse(consumeQueue.take()); - } - - @Test - public void testStopDelivery() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(queue), - null); - assertTrue(queue.take()); - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - publisher.asyncPublish(topic, getMsg(0), new TestCallback(queue), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - // Stop the delivery for this subscription - subscriber.stopDelivery(topic, localSubscriberId); - // Publish some more messages so they are queued up to be delivered to - // the client - int batchSize = 10; - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(topic, getMsg(i + 1), new TestCallback(queue), null); - assertTrue(queue.take()); - } - // Wait a little bit just in case the message handler is still active, - // consuming the message, and then putting a true value in the - // consumeQueue. - Thread.sleep(1000); - // Put a False value on the consumeQueue so we can verify that it - // is not blocked by a message consume action which already put a True - // value into the queue. - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(consumeQueue, false); - } - }).start(); - assertFalse(consumeQueue.take()); - // Now start delivery again and verify that the queued up messages are - // consumed - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - for (int i = 0; i < batchSize; i++) { - assertTrue(consumeQueue.take()); - } - } - - @Test - public void testConsumedMessagesInOrder() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(queue), - null); - assertTrue(queue.take()); - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - // Now publish some messages and verify that they are delivered in order - // to the subscriber - int batchSize = 100; - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(topic, getMsg(i), new TestCallback(queue), null); - } - // We've sent out all of the publish messages asynchronously, - // now verify that they are consumed in the correct order. - for (int i = 0; i < batchSize; i++) { - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - } - } - - @Test - public void testCreateSubscriptionFailure() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback(queue), - null); - assertTrue(queue.take()); - // Close the subscription asynchronously - subscriber.asyncCloseSubscription(topic, localSubscriberId, new TestCallback(queue), null); - assertTrue(queue.take()); - // Now try to create the subscription when it already exists - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE, new TestCallback(queue), null); - assertFalse(queue.take()); - } - - @Test - public void testCreateSubscriptionSuccess() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.CREATE, new TestCallback(queue), null); - assertTrue(queue.take()); - startDelivery(topic, localSubscriberId, new TestMessageHandler(consumeQueue)); - int batchSize = 5; - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(topic, getMsg(i), new TestCallback(queue), null); - assertTrue(queue.take()); - assertTrue(consumeQueue.take()); - } - } - - @Test - public void testAttachToSubscriptionFailure() throws Exception { - ByteString topic = getTopic(0); - subscriber.asyncSubscribe(topic, localSubscriberId, CreateOrAttach.ATTACH, new TestCallback(queue), null); - assertFalse(queue.take()); - } - - // The following 4 tests are to make sure that the subscriberId validation - // works when it is a local subscriber and we're expecting the subscriberId - // to be in the "local" specific format. - @Test - public void testSyncSubscribeWithInvalidSubscriberId() throws Exception { - boolean subscribeSuccess = false; - try { - subscriber.subscribe(getTopic(0), hubSubscriberId, CreateOrAttach.CREATE_OR_ATTACH); - } catch (InvalidSubscriberIdException e) { - subscribeSuccess = true; - } catch (Exception ex) { - subscribeSuccess = false; - } - assertTrue(subscribeSuccess); - } - - @Test - public void testAsyncSubscribeWithInvalidSubscriberId() throws Exception { - subscriber.asyncSubscribe(getTopic(0), hubSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, - new TestCallback(queue), null); - assertFalse(queue.take()); - } - - @Test - public void testSyncUnsubscribeWithInvalidSubscriberId() throws Exception { - boolean unsubscribeSuccess = false; - try { - subscriber.unsubscribe(getTopic(0), hubSubscriberId); - } catch (InvalidSubscriberIdException e) { - unsubscribeSuccess = true; - } catch (Exception ex) { - unsubscribeSuccess = false; - } - assertTrue(unsubscribeSuccess); - } - - @Test - public void testAsyncUnsubscribeWithInvalidSubscriberId() throws Exception { - subscriber.asyncUnsubscribe(getTopic(0), hubSubscriberId, new TestCallback(queue), null); - assertFalse(queue.take()); - } - - // The following 4 tests are to make sure that the subscriberId validation - // also works when it is a hub subscriber and we're expecting the - // subscriberId to be in the "hub" specific format. - @Test - public void testSyncHubSubscribeWithInvalidSubscriberId() throws Exception { - HedwigClient hubClient = new HedwigHubClient(new ClientConfiguration()); - HedwigSubscriber hubSubscriber = hubClient.getSubscriber(); - boolean subscribeSuccess = false; - try { - hubSubscriber.subscribe(getTopic(0), localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH); - } catch (InvalidSubscriberIdException e) { - subscribeSuccess = true; - } catch (Exception ex) { - subscribeSuccess = false; - } - assertTrue(subscribeSuccess); - hubClient.stop(); - } - - @Test - public void testAsyncHubSubscribeWithInvalidSubscriberId() throws Exception { - HedwigClient hubClient = new HedwigHubClient(new ClientConfiguration()); - HedwigSubscriber hubSubscriber = hubClient.getSubscriber(); - hubSubscriber.asyncSubscribe(getTopic(0), localSubscriberId, CreateOrAttach.CREATE_OR_ATTACH, new TestCallback( - queue), null); - assertFalse(queue.take()); - hubClient.stop(); - } - - @Test - public void testSyncHubUnsubscribeWithInvalidSubscriberId() throws Exception { - HedwigClient hubClient = new HedwigHubClient(new ClientConfiguration()); - HedwigSubscriber hubSubscriber = hubClient.getSubscriber(); - boolean unsubscribeSuccess = false; - try { - hubSubscriber.unsubscribe(getTopic(0), localSubscriberId); - } catch (InvalidSubscriberIdException e) { - unsubscribeSuccess = true; - } catch (Exception ex) { - unsubscribeSuccess = false; - } - assertTrue(unsubscribeSuccess); - hubClient.stop(); - } - - @Test - public void testAsyncHubUnsubscribeWithInvalidSubscriberId() throws Exception { - HedwigClient hubClient = new HedwigHubClient(new ClientConfiguration()); - HedwigSubscriber hubSubscriber = hubClient.getSubscriber(); - hubSubscriber.asyncUnsubscribe(getTopic(0), localSubscriberId, new TestCallback(queue), null); - assertFalse(queue.take()); - hubClient.stop(); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigRegion.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigRegion.java deleted file mode 100644 index c8edb616715..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/integration/TestHedwigRegion.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.integration; - -import java.util.concurrent.SynchronousQueue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigPublisher; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.server.HedwigRegionTestBase; -import org.apache.hedwig.server.integration.TestHedwigHub.TestCallback; -import org.apache.hedwig.server.integration.TestHedwigHub.TestMessageHandler; - -public class TestHedwigRegion extends HedwigRegionTestBase { - - // SynchronousQueues to verify async calls - private final SynchronousQueue queue = new SynchronousQueue(); - private final SynchronousQueue consumeQueue = new SynchronousQueue(); - - @Override - @Before - public void setUp() throws Exception { - numRegions = 3; - numServersPerRegion = 4; - super.setUp(); - } - - @Override - @After - public void tearDown() throws Exception { - super.tearDown(); - } - - @Test - public void testMultiRegionSubscribeAndConsume() throws Exception { - int batchSize = 10; - // Subscribe to topics for clients in all regions - for (HedwigClient client : regionClientsMap.values()) { - for (int i = 0; i < batchSize; i++) { - client.getSubscriber().asyncSubscribe(ByteString.copyFromUtf8("Topic" + i), - ByteString.copyFromUtf8("LocalSubscriber"), CreateOrAttach.CREATE_OR_ATTACH, - new TestCallback(queue), null); - assertTrue(queue.take()); - } - } - - // Start delivery for the local subscribers in all regions - for (HedwigClient client : regionClientsMap.values()) { - for (int i = 0; i < batchSize; i++) { - client.getSubscriber().startDelivery(ByteString.copyFromUtf8("Topic" + i), - ByteString.copyFromUtf8("LocalSubscriber"), new TestMessageHandler(consumeQueue)); - } - } - - // Now start publishing messages for the subscribed topics in one of the - // regions and verify that it gets delivered and consumed in all of the - // other ones. - HedwigPublisher publisher = regionClientsMap.values().iterator().next().getPublisher(); - for (int i = 0; i < batchSize; i++) { - publisher.asyncPublish(ByteString.copyFromUtf8("Topic" + i), Message.newBuilder().setBody( - ByteString.copyFromUtf8("Message" + i)).build(), new TestCallback(queue), null); - assertTrue(queue.take()); - } - // Make sure each region consumes the same set of published messages. - for (int i = 0; i < regionClientsMap.size(); i++) { - for (int j = 0; j < batchSize; j++) { - assertTrue(consumeQueue.take()); - } - } - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/TestPubSubServer.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/TestPubSubServer.java deleted file mode 100644 index 66edbaaeaf5..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/TestPubSubServer.java +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.netty; - -import java.io.IOException; -import java.lang.Thread.UncaughtExceptionHandler; -import java.net.InetSocketAddress; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.SynchronousQueue; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.client.conf.ClientConfiguration; -import org.apache.hedwig.client.netty.HedwigClient; -import org.apache.hedwig.client.netty.HedwigPublisher; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.server.PubSubServerStandAloneTestBase; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.topics.AbstractTopicManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.HedwigSocketAddress; -import org.apache.hedwig.zookeeper.SafeAsyncZKCallback; - -public class TestPubSubServer extends PubSubServerStandAloneTestBase { - - @Test - public void testSecondServer() throws Exception { - PubSubServer server1 = new PubSubServer(new StandAloneServerConfiguration() { - @Override - public int getServerPort() { - return super.getServerPort() + 1; - } - }); - server1.shutdown(); - } - - class RecordingUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { - SynchronousQueue queue; - - public RecordingUncaughtExceptionHandler(SynchronousQueue queue) { - this.queue = queue; - } - - @Override - public void uncaughtException(Thread t, Throwable e) { - queue.add(e); - } - - } - - private interface TopicManagerInstantiator { - public TopicManager instantiateTopicManager() throws IOException; - } - - PubSubServer startServer(final UncaughtExceptionHandler uncaughtExceptionHandler, final int port, - final TopicManagerInstantiator instantiator) throws Exception { - PubSubServer server = new PubSubServer(new StandAloneServerConfiguration() { - @Override - public int getServerPort() { - return port; - } - - }, uncaughtExceptionHandler) { - - @Override - protected TopicManager instantiateTopicManager() throws IOException { - return instantiator.instantiateTopicManager(); - } - }; - - return server; - - } - - public void runPublishRequest(final int port) throws Exception { - HedwigPublisher publisher = new HedwigClient(new ClientConfiguration() { - @Override - public InetSocketAddress getDefaultServerHost() { - return new InetSocketAddress("localhost", port); - } - }).getPublisher(); - - publisher.asyncPublish(ByteString.copyFromUtf8("blah"), Message.newBuilder().setBody( - ByteString.copyFromUtf8("blah")).build(), new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - assertTrue(false); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - assertTrue(false); - } - - }, null); - } - - @Test - public void testUncaughtExceptionInNettyThread() throws Exception { - - SynchronousQueue queue = new SynchronousQueue(); - RecordingUncaughtExceptionHandler uncaughtExceptionHandler = new RecordingUncaughtExceptionHandler(queue); - final int port = 9876; - - PubSubServer server = startServer(uncaughtExceptionHandler, port, new TopicManagerInstantiator() { - - @Override - public TopicManager instantiateTopicManager() throws IOException { - return new AbstractTopicManager(new ServerConfiguration(), Executors.newSingleThreadScheduledExecutor()) { - @Override - protected void realGetOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx) { - throw new RuntimeException("this exception should be uncaught"); - } - - @Override - protected void postReleaseCleanup(ByteString topic, Callback cb, Object ctx) { - } - }; - } - }); - - runPublishRequest(port); - assertEquals(RuntimeException.class, queue.take().getClass()); - server.shutdown(); - } - - @Test - public void testUncaughtExceptionInZKThread() throws Exception { - - SynchronousQueue queue = new SynchronousQueue(); - RecordingUncaughtExceptionHandler uncaughtExceptionHandler = new RecordingUncaughtExceptionHandler(queue); - final int port = 9876; - final String hostPort = "127.0.0.1:33221"; - - PubSubServer server = startServer(uncaughtExceptionHandler, port, new TopicManagerInstantiator() { - - @Override - public TopicManager instantiateTopicManager() throws IOException { - return new AbstractTopicManager(new ServerConfiguration(), Executors.newSingleThreadScheduledExecutor()) { - - @Override - protected void realGetOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx) { - ZooKeeper zookeeper; - try { - zookeeper = new ZooKeeper(hostPort, 60000, new Watcher() { - @Override - public void process(WatchedEvent event) { - // TODO Auto-generated method stub - - } - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - - zookeeper.getData("/fake", false, new SafeAsyncZKCallback.DataCallback() { - @Override - public void safeProcessResult(int rc, String path, Object ctx, byte[] data, - org.apache.zookeeper.data.Stat stat) { - throw new RuntimeException("This should go to the uncaught exception handler"); - } - - }, null); - } - - @Override - protected void postReleaseCleanup(ByteString topic, Callback cb, Object ctx) { - } - }; - } - }); - - runPublishRequest(port); - assertEquals(RuntimeException.class, queue.take().getClass()); - server.shutdown(); - } - - @Test - public void testInvalidServerConfiguration() throws Exception { - boolean success = false; - ServerConfiguration conf = new ServerConfiguration() { - @Override - public boolean isInterRegionSSLEnabled() { - return conf.getBoolean(INTER_REGION_SSL_ENABLED, true); - } - - @Override - public List getRegions() { - List regionsList = new LinkedList(); - regionsList.add("regionHost1:4080:9876"); - regionsList.add("regionHost2:4080"); - regionsList.add("regionHost3:4080:9876"); - return regionsList; - } - }; - try { - conf.validate(); - } - catch (ConfigurationException e) { - logger.error("Invalid configuration: ", e); - success = true; - } - assertTrue(success); - } - - @Test - public void testValidServerConfiguration() throws Exception { - boolean success = true; - ServerConfiguration conf = new ServerConfiguration() { - @Override - public boolean isInterRegionSSLEnabled() { - return conf.getBoolean(INTER_REGION_SSL_ENABLED, true); - } - - @Override - public List getRegions() { - List regionsList = new LinkedList(); - regionsList.add("regionHost1:4080:9876"); - regionsList.add("regionHost2:4080:2938"); - regionsList.add("regionHost3:4080:9876"); - return regionsList; - } - }; - try { - conf.validate(); - } - catch (ConfigurationException e) { - logger.error("Invalid configuration: ", e); - success = false; - } - assertTrue(success); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/WriteRecordingChannel.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/WriteRecordingChannel.java deleted file mode 100644 index 7ea9aff369f..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/netty/WriteRecordingChannel.java +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.netty; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.LinkedList; -import java.util.List; - -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelConfig; -import org.jboss.netty.channel.ChannelFactory; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.DefaultChannelFuture; -import org.jboss.netty.channel.SucceededChannelFuture; - -public class WriteRecordingChannel implements Channel { - - public boolean closed = false; - ChannelFuture closingFuture = new DefaultChannelFuture(this, false); - List messagesWritten = new LinkedList(); - - public List getMessagesWritten() { - return messagesWritten; - } - - public void clearMessages() { - messagesWritten.clear(); - } - - @Override - public ChannelFuture bind(SocketAddress localAddress) { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFuture close() { - closed = true; - closingFuture.setSuccess(); - return new SucceededChannelFuture(this); - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress) { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFuture disconnect() { - return close(); - } - - @Override - public ChannelFuture getCloseFuture() { - return closingFuture; - } - - @Override - public ChannelConfig getConfig() { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFactory getFactory() { - throw new RuntimeException("Not intended"); - } - - @Override - public Integer getId() { - throw new RuntimeException("Not intended"); - } - - @Override - public int getInterestOps() { - throw new RuntimeException("Not intended"); - } - - @Override - public SocketAddress getLocalAddress() { - return new InetSocketAddress("localhost", 1234); - } - - @Override - public Channel getParent() { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelPipeline getPipeline() { - throw new RuntimeException("Not intended"); - } - - @Override - public SocketAddress getRemoteAddress() { - return new InetSocketAddress("www.yahoo.com", 80); - } - - @Override - public boolean isBound() { - throw new RuntimeException("Not intended"); - } - - @Override - public boolean isConnected() { - return closed == false; - } - - @Override - public boolean isOpen() { - throw new RuntimeException("Not intended"); - } - - @Override - public boolean isReadable() { - throw new RuntimeException("Not intended"); - } - - @Override - public boolean isWritable() { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFuture setInterestOps(int interestOps) { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFuture setReadable(boolean readable) { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFuture unbind() { - throw new RuntimeException("Not intended"); - } - - @Override - public ChannelFuture write(Object message) { - messagesWritten.add(message); - return new SucceededChannelFuture(this); - } - - @Override - public ChannelFuture write(Object message, SocketAddress remoteAddress) { - throw new RuntimeException("Not intended"); - } - - @Override - public int compareTo(Channel o) { - throw new RuntimeException("Not intended"); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/BookKeeperTestBase.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/BookKeeperTestBase.java deleted file mode 100644 index a1e2b0de680..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/BookKeeperTestBase.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.io.File; -import java.util.LinkedList; -import java.util.List; - -import org.apache.bookkeeper.client.BookKeeper; -import org.apache.bookkeeper.proto.BookieServer; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import org.apache.hedwig.util.FileUtils; -import org.apache.hedwig.zookeeper.ZooKeeperTestBase; - -/** - * This is a base class for any tests that require a BookKeeper client/server - * setup. - * - */ -public class BookKeeperTestBase extends ZooKeeperTestBase { - - // BookKeeper Server variables - private List bookiesList; - private int initialPort = 5000; - - // String constants used for creating the bookie server files. - private static final String PREFIX = "bookie"; - private static final String SUFFIX = "test"; - - // Variable to decide how many bookie servers to set up. - private final int numBookies; - // BookKeeper client instance - protected BookKeeper bk; - - // Constructor - public BookKeeperTestBase(int numBookies) { - this.numBookies = numBookies; - } - - public BookKeeperTestBase() { - // By default, use 3 bookies. - this(3); - } - - // Getter for the ZooKeeper client instance that the parent class sets up. - protected ZooKeeper getZooKeeperClient() { - return zk; - } - - // Give junit a fake test so that its happy - @Test - public void testNothing() throws Exception { - - } - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - // Initialize the zk client with values - try { - zk.create("/ledgers", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - zk.create("/ledgers/available", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - } catch (KeeperException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // Create Bookie Servers - bookiesList = new LinkedList(); - - for (int i = 0; i < numBookies; i++) { - File tmpDir = FileUtils.createTempDirectory(PREFIX + i, SUFFIX); - BookieServer bs = new BookieServer(initialPort + i, hostPort, tmpDir, new File[] { tmpDir }); - bs.start(); - bookiesList.add(bs); - } - - // Create the BookKeeper client - bk = new BookKeeper(hostPort); - } - - public String getZkHostPort() { - return hostPort; - } - - @Override - @After - public void tearDown() throws Exception { - // Shutdown all of the bookie servers - try { - for (BookieServer bs : bookiesList) { - bs.shutdown(); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - // Close the BookKeeper client - bk.halt(); - super.tearDown(); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubPersistenceManager.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubPersistenceManager.java deleted file mode 100644 index 5fa0a1acf8e..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubPersistenceManager.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException.ServiceDownException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protoextensions.MessageIdUtils; -import org.apache.hedwig.server.persistence.ScanCallback.ReasonForFinish; - -public class StubPersistenceManager implements PersistenceManagerWithRangeScan { - Map> messages = new HashMap>(); - boolean failure = false; - ServiceDownException exception = new ServiceDownException("Asked to fail"); - - public void deliveredUntil(ByteString topic, Long seqId) { - // noop - } - - public void consumedUntil(ByteString topic, Long seqId) { - // noop - } - - protected static class ArrayListMessageFactory implements Factory> { - static ArrayListMessageFactory instance = new ArrayListMessageFactory(); - - public List newInstance() { - return new ArrayList(); - } - } - - public MessageSeqId getCurrentSeqIdForTopic(ByteString topic) { - long seqId = MapMethods.getAfterInsertingIfAbsent(messages, topic, ArrayListMessageFactory.instance).size(); - return MessageSeqId.newBuilder().setLocalComponent(seqId).build(); - } - - public long getSeqIdAfterSkipping(ByteString topic, long seqId, int skipAmount) { - return seqId + skipAmount; - } - - public void persistMessage(PersistRequest request) { - if (failure) { - request.callback.operationFailed(request.getCtx(), exception); - return; - } - - MapMethods.addToMultiMap(messages, request.getTopic(), request.getMessage(), ArrayListMessageFactory.instance); - request.callback.operationFinished(request.getCtx(), (long) messages.get(request.getTopic()).size()); - } - - public void scanSingleMessage(ScanRequest request) { - if (failure) { - request.getCallback().scanFailed(request.getCtx(), exception); - return; - } - - request.getCallback().messageScanned(request.getCtx(), - messages.get(request.getTopic()).get((int) request.getStartSeqId())); - - } - - public void scanMessages(RangeScanRequest request) { - if (failure) { - request.getCallback().scanFailed(request.getCtx(), exception); - return; - } - - long totalSize = 0; - long startSeqId = request.getStartSeqId(); - for (int i = 0; i < request.getMessageLimit(); i++) { - List messageList = MapMethods.getAfterInsertingIfAbsent(messages, request.getTopic(), - ArrayListMessageFactory.instance); - if (startSeqId + i > messageList.size()) { - request.getCallback().scanFinished(request.getCtx(), ReasonForFinish.NO_MORE_MESSAGES); - return; - } - Message msg = messageList.get((int) startSeqId + i - 1); - Message toDeliver = MessageIdUtils.mergeLocalSeqId(msg, startSeqId + i); - request.getCallback().messageScanned(request.getCtx(), toDeliver); - - totalSize += toDeliver.getBody().size(); - - if (totalSize > request.getSizeLimit()) { - request.getCallback().scanFinished(request.getCtx(), ReasonForFinish.SIZE_LIMIT_EXCEEDED); - return; - } - } - request.getCallback().scanFinished(request.getCtx(), ReasonForFinish.NUM_MESSAGES_LIMIT_EXCEEDED); - - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubScanCallback.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubScanCallback.java deleted file mode 100644 index 4ef31984485..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/StubScanCallback.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.concurrent.LinkedBlockingQueue; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Either; - -public class StubScanCallback implements ScanCallback{ - - public static Message END_MESSAGE = Message.newBuilder().setBody(ByteString.EMPTY).build(); - - LinkedBlockingQueue> queue = new LinkedBlockingQueue>(); - - @Override - public void messageScanned(Object ctx, Message message) { - ConcurrencyUtils.put(queue, Either.of(message, (Exception) null)); - } - - @Override - public void scanFailed(Object ctx, Exception exception) { - ConcurrencyUtils.put(queue, Either.of((Message) null, exception)); - } - - @Override - public void scanFinished(Object ctx, ReasonForFinish reason) { - ConcurrencyUtils.put(queue, Either.of(END_MESSAGE, (Exception) null)); - - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookKeeperPersistenceManagerBlackBox.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookKeeperPersistenceManagerBlackBox.java deleted file mode 100644 index daab792f4cd..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookKeeperPersistenceManagerBlackBox.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.junit.After; -import org.junit.Before; - -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.topics.TrivialOwnAllTopicManager; - -public class TestBookKeeperPersistenceManagerBlackBox extends TestPersistenceManagerBlackBox { - BookKeeperTestBase bktb; - private final int numBookies = 3; - - @Override - @Before - protected void setUp() throws Exception { - // We need to setUp this class first since the super.setUp() method will - // need the BookKeeperTestBase to be instantiated. - bktb = new BookKeeperTestBase(numBookies); - bktb.setUp(); - super.setUp(); - } - - @Override - @After - protected void tearDown() throws Exception { - bktb.tearDown(); - super.tearDown(); - } - - @Override - long getLowestSeqId() { - return 1; - } - - @Override - PersistenceManager instantiatePersistenceManager() throws Exception { - ServerConfiguration conf = new ServerConfiguration(); - ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - - return new BookkeeperPersistenceManager(bktb.bk, bktb.getZooKeeperClient(), new TrivialOwnAllTopicManager(conf, - scheduler), conf, scheduler); - } - - @Override - public long getExpectedSeqId(int numPublished) { - return numPublished; - } - - public static Test suite() { - return new TestSuite(TestBookKeeperPersistenceManagerBlackBox.class); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookkeeperPersistenceManagerWhiteBox.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookkeeperPersistenceManagerWhiteBox.java deleted file mode 100644 index fcefaa332a4..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestBookkeeperPersistenceManagerWhiteBox.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.List; -import java.util.Random; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; - -import junit.framework.TestCase; - -import org.apache.bookkeeper.client.BookKeeper; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.HelperMethods; -import org.apache.hedwig.StubCallback; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.server.topics.TrivialOwnAllTopicManager; -import org.apache.hedwig.util.ConcurrencyUtils; - -public class TestBookkeeperPersistenceManagerWhiteBox extends TestCase { - - BookKeeperTestBase bktb; - private final int numBookies = 3; - BookkeeperPersistenceManager bkpm; - ServerConfiguration conf; - ScheduledExecutorService scheduler; - TopicManager tm; - ByteString topic = ByteString.copyFromUtf8("topic0"); - - @Override - @Before - protected void setUp() throws Exception { - super.setUp(); - bktb = new BookKeeperTestBase(numBookies); - bktb.setUp(); - - conf = new ServerConfiguration(); - scheduler = Executors.newScheduledThreadPool(1); - tm = new TrivialOwnAllTopicManager(conf, scheduler); - - bkpm = new BookkeeperPersistenceManager(bktb.bk, bktb.getZooKeeperClient(), tm, conf, scheduler); - } - - @Override - @After - protected void tearDown() throws Exception { - bktb.tearDown(); - super.tearDown(); - } - - @Test - public void testEmptyDirtyLedger() throws Exception { - - StubCallback stubCallback = new StubCallback(); - bkpm.acquiredTopic(topic, stubCallback, null); - assertNull(ConcurrencyUtils.take(stubCallback.queue).right()); - // now abandon, and try another time, the prev ledger should be dirty - - bkpm = new BookkeeperPersistenceManager(new BookKeeper(bktb.getZkHostPort()), bktb.getZooKeeperClient(), tm, - conf, scheduler); - bkpm.acquiredTopic(topic, stubCallback, null); - assertNull(ConcurrencyUtils.take(stubCallback.queue).right()); - assertEquals(0, bkpm.topicInfos.get(topic).ledgerRanges.size()); - } - - public void testNonEmptyDirtyLedger() throws Exception { - - Random r = new Random(); - int NUM_MESSAGES_TO_TEST = 100; - int SIZE_OF_MESSAGES_TO_TEST = 100; - int index = 0; - int numPrevLedgers = 0; - List messages = HelperMethods.getRandomPublishedMessages(NUM_MESSAGES_TO_TEST, - SIZE_OF_MESSAGES_TO_TEST); - - while (index < messages.size()) { - - StubCallback stubCallback = new StubCallback(); - bkpm.acquiredTopic(topic, stubCallback, null); - assertNull(ConcurrencyUtils.take(stubCallback.queue).right()); - assertEquals(numPrevLedgers, bkpm.topicInfos.get(topic).ledgerRanges.size()); - - StubCallback persistCallback = new StubCallback(); - bkpm.persistMessage(new PersistRequest(topic, messages.get(index), persistCallback, null)); - assertEquals(new Long(index + 1), ConcurrencyUtils.take(persistCallback.queue).left()); - - // once in every 10 times, give up ledger - if (r.nextInt(10) == 9) { - // Make the bkpm lose its memory - bkpm.topicInfos.clear(); - numPrevLedgers++; - } - index++; - } - - // Lets scan now - StubScanCallback scanCallback = new StubScanCallback(); - bkpm.scanMessages(new RangeScanRequest(topic, 1, NUM_MESSAGES_TO_TEST, Long.MAX_VALUE, scanCallback, null)); - for (int i = 0; i < messages.size(); i++) { - Message scannedMessage = ConcurrencyUtils.take(scanCallback.queue).left(); - assertTrue(messages.get(i).getBody().equals(scannedMessage.getBody())); - assertEquals(i + 1, scannedMessage.getMsgId().getLocalComponent()); - } - assertTrue(StubScanCallback.END_MESSAGE == ConcurrencyUtils.take(scanCallback.queue).left()); - - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestLocalDBPersistenceManagerBlackBox.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestLocalDBPersistenceManagerBlackBox.java deleted file mode 100644 index d80f17aa6d8..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestLocalDBPersistenceManagerBlackBox.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.hedwig.server.persistence.LocalDBPersistenceManager; -import org.apache.hedwig.server.persistence.PersistenceManager; - -public class TestLocalDBPersistenceManagerBlackBox extends TestPersistenceManagerBlackBox { - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - ((LocalDBPersistenceManager) persistenceManager).reset(); - } - - @Override - long getLowestSeqId() { - return 1; - } - - @Override - PersistenceManager instantiatePersistenceManager() { - return LocalDBPersistenceManager.instance(); - } - - @Override - public long getExpectedSeqId(int numPublished) { - return numPublished; - } - - public static Test suite() { - return new TestSuite(TestLocalDBPersistenceManagerBlackBox.class); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestPersistenceManagerBlackBox.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestPersistenceManagerBlackBox.java deleted file mode 100644 index f0157990744..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestPersistenceManagerBlackBox.java +++ /dev/null @@ -1,305 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.HelperMethods; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.server.topics.TopicOwnershipChangeListener; -import org.apache.hedwig.util.Callback; - -public abstract class TestPersistenceManagerBlackBox extends TestCase { - protected PersistenceManager persistenceManager; - protected int NUM_MESSAGES_TO_TEST = 5; - protected int NUM_TOPICS_TO_TEST = 5; - static Logger logger = Logger.getLogger(TestPersistenceManagerBlackBox.class); - TestCallback testCallback = new TestCallback(); - - RuntimeException failureException; - - class TestCallback implements Callback { - - public void operationFailed(Object ctx, PubSubException exception) { - throw (failureException = new RuntimeException(exception)); - } - - @SuppressWarnings("unchecked") - public void operationFinished(Object ctx, Long resultOfOperation) { - LinkedBlockingQueue statusQueue = (LinkedBlockingQueue) ctx; - try { - statusQueue.put(true); - } catch (InterruptedException e) { - throw (failureException = new RuntimeException(e)); - } - } - } - - class RangeScanVerifierListener implements ScanCallback { - List pubMsgs; - - public RangeScanVerifierListener(List pubMsgs) { - this.pubMsgs = pubMsgs; - } - - public void messageScanned(Object ctx, Message recvMessage) { - if (pubMsgs.isEmpty()) { - throw (failureException = new RuntimeException("Message received when none expected")); - } - - Message pubMsg = pubMsgs.get(0); - if (!HelperMethods.areEqual(recvMessage, pubMsg)) { - throw (failureException = new RuntimeException("Scanned message not equal to expected")); - } - pubMsgs.remove(0); - } - - public void scanFailed(Object ctx, Exception exception) { - throw (failureException = new RuntimeException(exception)); - } - - @SuppressWarnings("unchecked") - public void scanFinished(Object ctx, ReasonForFinish reason) { - if (reason != ReasonForFinish.NO_MORE_MESSAGES) { - throw (failureException = new RuntimeException("Scan finished prematurely " + reason)); - } - LinkedBlockingQueue statusQueue = (LinkedBlockingQueue) ctx; - try { - statusQueue.put(true); - } catch (InterruptedException e) { - throw (failureException = new RuntimeException(e)); - } - } - - } - - class PointScanVerifierListener implements ScanCallback { - List pubMsgs; - ByteString topic; - - public PointScanVerifierListener(List pubMsgs, ByteString topic) { - this.topic = topic; - this.pubMsgs = pubMsgs; - } - - @SuppressWarnings("unchecked") - public void messageScanned(Object ctx, Message recvMessage) { - - Message pubMsg = pubMsgs.get(0); - if (!HelperMethods.areEqual(recvMessage, pubMsg)) { - throw (failureException = new RuntimeException("Scanned message not equal to expected")); - } - pubMsgs.remove(0); - - if (pubMsgs.isEmpty()) { - LinkedBlockingQueue statusQueue = (LinkedBlockingQueue) ctx; - try { - statusQueue.put(true); - } catch (InterruptedException e) { - throw (failureException = new RuntimeException(e)); - } - } else { - long seqId = recvMessage.getMsgId().getLocalComponent(); - seqId = persistenceManager.getSeqIdAfterSkipping(topic, seqId, 1); - ScanRequest request = new ScanRequest(topic, seqId, new PointScanVerifierListener(pubMsgs, topic), ctx); - persistenceManager.scanSingleMessage(request); - } - - } - - public void scanFailed(Object ctx, Exception exception) { - throw (failureException = new RuntimeException(exception)); - } - - public void scanFinished(Object ctx, ReasonForFinish reason) { - - } - - } - - class ScanVerifier implements Runnable { - List pubMsgs; - ByteString topic; - LinkedBlockingQueue statusQueue = new LinkedBlockingQueue(); - - public ScanVerifier(ByteString topic, List pubMsgs) { - this.topic = topic; - this.pubMsgs = pubMsgs; - } - - public void run() { - // start the scan - try { - if (persistenceManager instanceof PersistenceManagerWithRangeScan) { - - ScanCallback listener = new RangeScanVerifierListener(pubMsgs); - - PersistenceManagerWithRangeScan rangePersistenceManager = (PersistenceManagerWithRangeScan) persistenceManager; - - rangePersistenceManager.scanMessages(new RangeScanRequest(topic, getLowestSeqId(), - NUM_MESSAGES_TO_TEST + 1, Long.MAX_VALUE, listener, statusQueue)); - - } else { - - ScanCallback listener = new PointScanVerifierListener(pubMsgs, topic); - persistenceManager - .scanSingleMessage(new ScanRequest(topic, getLowestSeqId(), listener, statusQueue)); - - } - // now listen for it to finish - // wait a maximum of a minute - Boolean b = statusQueue.poll(60, TimeUnit.SECONDS); - if (b == null) { - throw (failureException = new RuntimeException("Scanning timed out")); - } - } catch (InterruptedException e) { - throw (failureException = new RuntimeException(e)); - } - } - } - - class Publisher implements Runnable { - List pubMsgs; - ByteString topic; - - public Publisher(ByteString topic, List pubMsgs) { - this.pubMsgs = pubMsgs; - this.topic = topic; - } - - public void run() { - LinkedBlockingQueue statusQueue = new LinkedBlockingQueue(); - - for (Message msg : pubMsgs) { - - try { - persistenceManager.persistMessage(new PersistRequest(topic, msg, testCallback, statusQueue)); - // wait a maximum of a minute - Boolean b = statusQueue.poll(60, TimeUnit.SECONDS); - if (b == null) { - throw (failureException = new RuntimeException("Scanning timed out")); - } - } catch (InterruptedException e) { - throw (failureException = new RuntimeException(e)); - } - } - } - - } - - @Override - protected void setUp() throws Exception { - logger.info("STARTING " + getName()); - persistenceManager = instantiatePersistenceManager(); - failureException = null; - logger.info("Persistence Manager test setup finished"); - } - - abstract long getLowestSeqId(); - - abstract PersistenceManager instantiatePersistenceManager() throws Exception; - - @Override - protected void tearDown() throws Exception { - logger.info("tearDown starting"); - super.tearDown(); - logger.info("FINISHED " + getName()); - } - - protected ByteString getTopicName(int number) { - return ByteString.copyFromUtf8("topic" + number); - } - - @Test - public void testPersistenceManager() throws Exception { - List publisherThreads = new LinkedList(); - List scannerThreads = new LinkedList(); - Thread thread; - Semaphore latch = new Semaphore(1); - - for (int i = 0; i < NUM_TOPICS_TO_TEST; i++) { - ByteString topic = getTopicName(i); - - if (persistenceManager instanceof TopicOwnershipChangeListener) { - - TopicOwnershipChangeListener tocl = (TopicOwnershipChangeListener) persistenceManager; - - latch.acquire(); - - tocl.acquiredTopic(topic, new Callback() { - @Override - public void operationFailed(Object ctx, PubSubException exception) { - failureException = new RuntimeException(exception); - ((Semaphore) ctx).release(); - } - - @Override - public void operationFinished(Object ctx, Void res) { - ((Semaphore) ctx).release(); - } - }, latch); - - latch.acquire(); - latch.release(); - if (failureException != null) { - throw (Exception) failureException.getCause(); - } - } - List msgs = HelperMethods.getRandomPublishedMessages(NUM_MESSAGES_TO_TEST, 1024); - - thread = new Thread(new Publisher(topic, msgs)); - publisherThreads.add(thread); - thread.start(); - - thread = new Thread(new ScanVerifier(topic, msgs)); - scannerThreads.add(thread); - } - for (Thread t : publisherThreads) { - t.join(); - } - - for (Thread t : scannerThreads) { - t.start(); - } - - for (Thread t : scannerThreads) { - t.join(); - } - - assertEquals(null, failureException); - for (int i = 0; i < NUM_TOPICS_TO_TEST; i++) { - assertEquals(persistenceManager.getCurrentSeqIdForTopic(getTopicName(i)).getLocalComponent(), - getExpectedSeqId(NUM_MESSAGES_TO_TEST)); - } - - } - - abstract long getExpectedSeqId(int numPublished); - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheBlackBox.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheBlackBox.java deleted file mode 100644 index ffb297f5bbd..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheBlackBox.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.persistence.LocalDBPersistenceManager; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.persistence.ReadAheadCache; - -public class TestReadAheadCacheBlackBox extends TestPersistenceManagerBlackBox { - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - LocalDBPersistenceManager.instance().reset(); - } - - @Override - long getExpectedSeqId(int numPublished) { - return numPublished; - } - - @Override - long getLowestSeqId() { - return 1; - } - - @Override - PersistenceManager instantiatePersistenceManager() { - return new ReadAheadCache(LocalDBPersistenceManager.instance(), new ServerConfiguration()).start(); - } - - public static Test suite() { - return new TestSuite(TestReadAheadCacheBlackBox.class); - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheWhiteBox.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheWhiteBox.java deleted file mode 100644 index 317481b7746..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/persistence/TestReadAheadCacheWhiteBox.java +++ /dev/null @@ -1,268 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.persistence; - -import static org.junit.Assert.*; - -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.HelperMethods; -import org.apache.hedwig.StubCallback; -import org.apache.hedwig.StubScanCallback; -import org.apache.hedwig.protocol.PubSubProtocol.Message; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.util.ConcurrencyUtils; - -public class TestReadAheadCacheWhiteBox { - ByteString topic = ByteString.copyFromUtf8("testTopic"); - final static int NUM_MESSAGES = 10; - final static int MSG_SIZE = 50; - List messages = HelperMethods.getRandomPublishedMessages(NUM_MESSAGES, MSG_SIZE); - StubPersistenceManager stubPersistenceManager; - ReadAheadCache cacheBasedPersistenceManager; - MyServerConfiguration myConf = new MyServerConfiguration(); - - class MyReadAheadCache extends ReadAheadCache { - public MyReadAheadCache(PersistenceManagerWithRangeScan persistenceManger, ServerConfiguration cfg) { - super(persistenceManger, cfg); - } - - @Override - protected void enqueueWithoutFailure(CacheRequest obj) { - // make it perform in the same thread - obj.performRequest(); - } - } - - class MyServerConfiguration extends ServerConfiguration { - - // Note these are set up, so that the size limit will be reached before - // the count limit - int readAheadCount = NUM_MESSAGES / 2; - long readAheadSize = (long) (MSG_SIZE * 2.5); - long maxCacheSize = Long.MAX_VALUE; - - @Override - public int getReadAheadCount() { - return readAheadCount; - } - - @Override - public long getReadAheadSizeBytes() { - return readAheadSize; - } - - @Override - public long getMaximumCacheSize() { - return maxCacheSize; - } - } - - @Before - public void setUp() throws Exception { - stubPersistenceManager = new StubPersistenceManager(); - cacheBasedPersistenceManager = new MyReadAheadCache(stubPersistenceManager, myConf).start(); - } - - @After - public void tearDown() throws Exception { - - } - - @Test - public void testPersistMessage() throws Exception{ - StubCallback callback = new StubCallback(); - PersistRequest request = new PersistRequest(topic, messages.get(0), callback, null); - - stubPersistenceManager.failure = true; - cacheBasedPersistenceManager.persistMessage(request); - assertNotNull(ConcurrencyUtils.take(callback.queue).right()); - - CacheKey key = new CacheKey(topic, cacheBasedPersistenceManager.getCurrentSeqIdForTopic(topic) - .getLocalComponent()); - assertFalse(cacheBasedPersistenceManager.cache.containsKey(key)); - - stubPersistenceManager.failure = false; - persistMessage(messages.get(0)); - } - - private void persistMessage(Message msg) throws Exception{ - StubCallback callback = new StubCallback(); - PersistRequest request = new PersistRequest(topic, msg, callback, null); - cacheBasedPersistenceManager.persistMessage(request); - assertNotNull(ConcurrencyUtils.take(callback.queue).left()); - CacheKey key = new CacheKey(topic, cacheBasedPersistenceManager.getCurrentSeqIdForTopic(topic) - .getLocalComponent()); - CacheValue cacheValue = cacheBasedPersistenceManager.cache.get(key); - assertNotNull(cacheValue); - assertFalse(cacheValue.isStub()); - assertTrue(HelperMethods.areEqual(cacheValue.getMessage(), msg)); - - } - - @Test - public void testScanSingleMessage() throws Exception { - StubScanCallback callback = new StubScanCallback(); - ScanRequest request = new ScanRequest(topic, 1, callback, null); - stubPersistenceManager.failure = true; - - cacheBasedPersistenceManager.scanSingleMessage(request); - assertTrue(callback.isFailed()); - assertTrue(0 == cacheBasedPersistenceManager.cache.size()); - - stubPersistenceManager.failure = false; - cacheBasedPersistenceManager.scanSingleMessage(request); - assertTrue(myConf.readAheadCount == cacheBasedPersistenceManager.cache.size()); - - persistMessage(messages.get(0)); - assertTrue(callback.isSuccess()); - - } - - @Test - public void testDeliveredUntil() throws Exception{ - for (Message m : messages) { - persistMessage(m); - } - assertEquals((long) NUM_MESSAGES * MSG_SIZE, cacheBasedPersistenceManager.presentCacheSize); - long middle = messages.size() / 2; - cacheBasedPersistenceManager.deliveredUntil(topic, middle); - - assertEquals(messages.size() - middle, cacheBasedPersistenceManager.cache.size()); - - long middle2 = middle - 1; - cacheBasedPersistenceManager.deliveredUntil(topic, middle2); - // should have no effect - assertEquals(messages.size() - middle, cacheBasedPersistenceManager.cache.size()); - - // delivered all messages - cacheBasedPersistenceManager.deliveredUntil(topic, (long) messages.size()); - // should have no effect - assertTrue(cacheBasedPersistenceManager.cache.isEmpty()); - assertTrue(cacheBasedPersistenceManager.timeIndexOfAddition.isEmpty()); - assertTrue(cacheBasedPersistenceManager.orderedIndexOnSeqId.isEmpty()); - assertTrue(0 == cacheBasedPersistenceManager.presentCacheSize); - - } - - @Test - public void testDoReadAhead() { - StubScanCallback callback = new StubScanCallback(); - ScanRequest request = new ScanRequest(topic, 1, callback, null); - cacheBasedPersistenceManager.doReadAhead(request); - - assertEquals(myConf.readAheadCount, cacheBasedPersistenceManager.cache.size()); - - request = new ScanRequest(topic, myConf.readAheadCount / 2 - 1, callback, null); - cacheBasedPersistenceManager.doReadAhead(request); - assertEquals(myConf.readAheadCount, cacheBasedPersistenceManager.cache.size()); - - request = new ScanRequest(topic, myConf.readAheadCount / 2 + 2, callback, null); - cacheBasedPersistenceManager.doReadAhead(request); - assertEquals((int) (1.5 * myConf.readAheadCount), cacheBasedPersistenceManager.cache.size()); - - } - - @Test - public void testReadAheadSizeLimit() throws Exception{ - for (Message m : messages) { - persistMessage(m); - } - cacheBasedPersistenceManager.cache.clear(); - StubScanCallback callback = new StubScanCallback(); - ScanRequest request = new ScanRequest(topic, 1, callback, null); - cacheBasedPersistenceManager.scanSingleMessage(request); - - assertTrue(callback.isSuccess()); - assertEquals((int) Math.ceil(myConf.readAheadSize / (MSG_SIZE + 0.0)), cacheBasedPersistenceManager.cache - .size()); - - } - - @Test - public void testDoReadAheadStartingFrom() throws Exception{ - persistMessage(messages.get(0)); - int readAheadCount = 5; - int start = 1; - RangeScanRequest readAheadRequest = cacheBasedPersistenceManager.doReadAheadStartingFrom(topic, start, - readAheadCount); - assertNull(readAheadRequest); - - StubScanCallback callback = new StubScanCallback(); - int end = 100; - ScanRequest request = new ScanRequest(topic, end, callback, null); - cacheBasedPersistenceManager.doReadAhead(request); - - int pos = 98; - readAheadRequest = cacheBasedPersistenceManager.doReadAheadStartingFrom(topic, pos, readAheadCount); - assertEquals(readAheadRequest.messageLimit, end - pos); - - end = 200; - request = new ScanRequest(topic, end, callback, null); - cacheBasedPersistenceManager.doReadAhead(request); - - // too far back - pos = 150; - readAheadRequest = cacheBasedPersistenceManager.doReadAheadStartingFrom(topic, pos, readAheadCount); - assertEquals(readAheadRequest.messageLimit, readAheadCount); - } - - @Test - public void testAddMessageToCache() { - CacheKey key = new CacheKey(topic, 1); - cacheBasedPersistenceManager.addMessageToCache(key, messages.get(0), System.currentTimeMillis()); - assertEquals(1, cacheBasedPersistenceManager.cache.size()); - assertEquals(MSG_SIZE, cacheBasedPersistenceManager.presentCacheSize); - assertEquals(1, cacheBasedPersistenceManager.orderedIndexOnSeqId.get(topic).size()); - assertTrue(cacheBasedPersistenceManager.orderedIndexOnSeqId.get(topic).contains(1L)); - - CacheValue value = cacheBasedPersistenceManager.cache.get(key); - assertTrue(cacheBasedPersistenceManager.timeIndexOfAddition.get(value.timeOfAddition).contains(key)); - } - - @Test - public void testRemoveMessageFromCache() { - CacheKey key = new CacheKey(topic, 1); - cacheBasedPersistenceManager.addMessageToCache(key, messages.get(0), System.currentTimeMillis()); - cacheBasedPersistenceManager.removeMessageFromCache(key, new Exception(), true, true); - assertTrue(cacheBasedPersistenceManager.cache.isEmpty()); - assertTrue(cacheBasedPersistenceManager.orderedIndexOnSeqId.isEmpty()); - assertTrue(cacheBasedPersistenceManager.timeIndexOfAddition.isEmpty()); - } - - @Test - public void testCollectOldCacheEntries() { - int i = 1; - for (Message m : messages) { - CacheKey key = new CacheKey(topic, i); - cacheBasedPersistenceManager.addMessageToCache(key, m, i); - i++; - } - - int n = 2; - myConf.maxCacheSize = n * MSG_SIZE; - cacheBasedPersistenceManager.collectOldCacheEntries(); - assertEquals(n, cacheBasedPersistenceManager.cache.size()); - assertEquals(n, cacheBasedPersistenceManager.timeIndexOfAddition.size()); - } -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/StubSubscriptionManager.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/StubSubscriptionManager.java deleted file mode 100644 index 850d64cc42e..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/StubSubscriptionManager.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import java.util.concurrent.ScheduledExecutorService; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.persistence.PersistenceManager; -import org.apache.hedwig.server.topics.TopicManager; -import org.apache.hedwig.util.Callback; - -public class StubSubscriptionManager extends InMemorySubscriptionManager { - boolean fail = false; - - public void setFail(boolean fail) { - this.fail = fail; - } - - public StubSubscriptionManager(TopicManager tm, PersistenceManager pm, ServerConfiguration conf, ScheduledExecutorService scheduler) { - super(tm, pm, conf, scheduler); - } - - @Override - public void serveSubscribeRequest(ByteString topic, SubscribeRequest subRequest, MessageSeqId consumeSeqId, - Callback callback, Object ctx) { - if (fail) { - callback.operationFailed(ctx, new PubSubException.ServiceDownException("Asked to fail")); - return; - } - super.serveSubscribeRequest(topic, subRequest, consumeSeqId, callback, ctx); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/TestZkSubscriptionManager.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/TestZkSubscriptionManager.java deleted file mode 100644 index 4b48497702f..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/subscriptions/TestZkSubscriptionManager.java +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.subscriptions; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.SynchronousQueue; - -import org.junit.Assert; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.protocol.PubSubProtocol.MessageSeqId; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest; -import org.apache.hedwig.protocol.PubSubProtocol.SubscribeRequest.CreateOrAttach; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.server.topics.TrivialOwnAllTopicManager; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Either; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.zookeeper.ZooKeeperTestBase; - -public class TestZkSubscriptionManager extends ZooKeeperTestBase { - ZkSubscriptionManager sm; - ServerConfiguration cfg = new ServerConfiguration(); - SynchronousQueue> msgIdCallbackQueue = new SynchronousQueue>(); - SynchronousQueue> BooleanCallbackQueue = new SynchronousQueue>(); - - Callback voidCallback; - Callback msgIdCallback; - - @Override - public void setUp() throws Exception { - super.setUp(); - cfg = new ServerConfiguration(); - final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - sm = new ZkSubscriptionManager(zk, new TrivialOwnAllTopicManager(cfg, scheduler), null, cfg, scheduler); - msgIdCallback = new Callback() { - @Override - public void operationFailed(Object ctx, final PubSubException exception) { - scheduler.execute(new Runnable() { - public void run() { - ConcurrencyUtils.put(msgIdCallbackQueue, Either.of((MessageSeqId) null, exception)); - } - }); - } - - @Override - public void operationFinished(Object ctx, final MessageSeqId resultOfOperation) { - scheduler.execute(new Runnable() { - public void run() { - ConcurrencyUtils.put(msgIdCallbackQueue, Either.of(resultOfOperation, (PubSubException) null)); - } - }); - } - }; - - voidCallback = new Callback() { - @Override - public void operationFailed(Object ctx, final PubSubException exception) { - scheduler.execute(new Runnable() { - public void run() { - ConcurrencyUtils.put(BooleanCallbackQueue, Either.of((Boolean) null, exception)); - } - }); - } - - @Override - public void operationFinished(Object ctx, Void resultOfOperation) { - scheduler.execute(new Runnable() { - public void run() { - ConcurrencyUtils.put(BooleanCallbackQueue, Either.of(true, (PubSubException) null)); - } - }); - } - }; - - } - - @Test - public void testBasics() throws Exception { - - ByteString topic1 = ByteString.copyFromUtf8("topic1"); - ByteString sub1 = ByteString.copyFromUtf8("sub1"); - - // - // No topics acquired. - // - SubscribeRequest subRequest = SubscribeRequest.newBuilder().setSubscriberId(sub1).build(); - MessageSeqId msgId = MessageSeqId.newBuilder().setLocalComponent(100).build(); - - sm.serveSubscribeRequest(topic1, subRequest, msgId, msgIdCallback, null); - - Assert.assertEquals(ConcurrencyUtils.take(msgIdCallbackQueue).right().getClass(), - PubSubException.ServerNotResponsibleForTopicException.class); - - sm.unsubscribe(topic1, sub1, voidCallback, null); - - Assert.assertEquals(ConcurrencyUtils.take(BooleanCallbackQueue).right().getClass(), - PubSubException.ServerNotResponsibleForTopicException.class); - - // - // Acquire topic. - // - - sm.acquiredTopic(topic1, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - - Assert.assertTrue(sm.top2sub2seq.containsKey(topic1)); - Assert.assertEquals(0, sm.top2sub2seq.get(topic1).size()); - - sm.unsubscribe(topic1, sub1, voidCallback, null); - Assert.assertEquals(ConcurrencyUtils.take(BooleanCallbackQueue).right().getClass(), - PubSubException.ClientNotSubscribedException.class); - - // - // Try to attach to a subscription. - subRequest = SubscribeRequest.newBuilder().setCreateOrAttach(CreateOrAttach.ATTACH).setSubscriberId(sub1) - .build(); - - sm.serveSubscribeRequest(topic1, subRequest, msgId, msgIdCallback, null); - Assert.assertEquals(ConcurrencyUtils.take(msgIdCallbackQueue).right().getClass(), - PubSubException.ClientNotSubscribedException.class); - - // now create - subRequest = SubscribeRequest.newBuilder().setCreateOrAttach(CreateOrAttach.CREATE).setSubscriberId(sub1) - .build(); - sm.serveSubscribeRequest(topic1, subRequest, msgId, msgIdCallback, null); - Assert.assertEquals(msgId.getLocalComponent(), ConcurrencyUtils.take(msgIdCallbackQueue).left().getLocalComponent()); - Assert.assertEquals(msgId.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getLastConsumeSeqId() - .getLocalComponent()); - - // try to create again - sm.serveSubscribeRequest(topic1, subRequest, msgId, msgIdCallback, null); - Assert.assertEquals(ConcurrencyUtils.take(msgIdCallbackQueue).right().getClass(), - PubSubException.ClientAlreadySubscribedException.class); - Assert.assertEquals(msgId.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getLastConsumeSeqId() - .getLocalComponent()); - - sm.lostTopic(topic1); - sm.acquiredTopic(topic1, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - - // try to attach - subRequest = SubscribeRequest.newBuilder().setCreateOrAttach(CreateOrAttach.ATTACH).setSubscriberId(sub1) - .build(); - MessageSeqId msgId1 = MessageSeqId.newBuilder().setLocalComponent(msgId.getLocalComponent() + 10).build(); - sm.serveSubscribeRequest(topic1, subRequest, msgId1, msgIdCallback, null); - Assert.assertEquals(msgId.getLocalComponent(), msgIdCallbackQueue.take().left().getLocalComponent()); - Assert.assertEquals(msgId.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getLastConsumeSeqId() - .getLocalComponent()); - - // now manipulate the consume ptrs - // dont give it enough to have it persist to ZK - MessageSeqId msgId2 = MessageSeqId.newBuilder().setLocalComponent( - msgId.getLocalComponent() + cfg.getConsumeInterval() - 1).build(); - sm.setConsumeSeqIdForSubscriber(topic1, sub1, msgId2, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - Assert.assertEquals(msgId2.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getLastConsumeSeqId() - .getLocalComponent()); - Assert.assertEquals(msgId.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getSubscriptionState().getMsgId() - .getLocalComponent()); - - // give it more so that it will write to ZK - MessageSeqId msgId3 = MessageSeqId.newBuilder().setLocalComponent( - msgId.getLocalComponent() + cfg.getConsumeInterval() + 1).build(); - sm.setConsumeSeqIdForSubscriber(topic1, sub1, msgId3, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - - sm.lostTopic(topic1); - sm.acquiredTopic(topic1, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - - Assert.assertEquals(msgId3.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getLastConsumeSeqId() - .getLocalComponent()); - Assert.assertEquals(msgId3.getLocalComponent(), sm.top2sub2seq.get(topic1).get(sub1).getSubscriptionState().getMsgId() - .getLocalComponent()); - - // finally unsubscribe - sm.unsubscribe(topic1, sub1, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - - sm.lostTopic(topic1); - sm.acquiredTopic(topic1, voidCallback, null); - Assert.assertTrue(BooleanCallbackQueue.take().left()); - Assert.assertFalse(sm.top2sub2seq.get(topic1).containsKey(sub1)); - - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/StubTopicManager.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/StubTopicManager.java deleted file mode 100644 index 26b403f61f4..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/StubTopicManager.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import java.net.UnknownHostException; -import java.util.concurrent.Executors; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.HedwigSocketAddress; - -public class StubTopicManager extends TrivialOwnAllTopicManager { - - boolean shouldOwnEveryNewTopic = false; - boolean shouldError = false; - - public void setShouldOwnEveryNewTopic(boolean shouldOwnEveryNewTopic) { - this.shouldOwnEveryNewTopic = shouldOwnEveryNewTopic; - } - - public void setShouldError(boolean shouldError) { - this.shouldError = shouldError; - } - - public StubTopicManager(ServerConfiguration conf) throws UnknownHostException { - super(conf, Executors.newSingleThreadScheduledExecutor()); - } - - @Override - protected void realGetOwner(ByteString topic, boolean shouldClaim, - Callback cb, Object ctx) { - - if (shouldError) { - cb.operationFailed(ctx, new PubSubException.ServiceDownException("Asked to fail")); - return; - } - if (topics.contains(topic) // already own it - || shouldOwnEveryNewTopic) { - super.realGetOwner(topic, shouldClaim, cb, ctx); - return; - } else { - // return some other address - cb.operationFinished(ctx, new HedwigSocketAddress("124.31.0.1:80")); - } - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/TestZkTopicManager.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/TestZkTopicManager.java deleted file mode 100644 index caaa08db3ca..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/server/topics/TestZkTopicManager.java +++ /dev/null @@ -1,311 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.server.topics; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.SynchronousQueue; - -import org.apache.zookeeper.KeeperException; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.google.protobuf.ByteString; -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.exceptions.PubSubException.CompositeException; -import org.apache.hedwig.server.common.ServerConfiguration; -import org.apache.hedwig.util.Callback; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Either; -import org.apache.hedwig.util.HedwigSocketAddress; -import org.apache.hedwig.util.Pair; -import org.apache.hedwig.zookeeper.ZooKeeperTestBase; - -public class TestZkTopicManager extends ZooKeeperTestBase { - - protected ZkTopicManager tm; - - protected class CallbackQueue implements Callback { - SynchronousQueue> q = new SynchronousQueue>(); - - public SynchronousQueue> getQueue() { - return q; - } - - public Either take() throws InterruptedException { - return q.take(); - } - - @Override - public void operationFailed(Object ctx, final PubSubException exception) { - LOG.error("got exception: " + exception); - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(q, Either.of((T) null, (Exception) exception)); - } - }).start(); - } - - @Override - public void operationFinished(Object ctx, final T resultOfOperation) { - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(q, Either.of(resultOfOperation, (Exception) null)); - } - }).start(); - } - } - - protected CallbackQueue addrCbq = new CallbackQueue(); - protected CallbackQueue bsCbq = new CallbackQueue(); - protected CallbackQueue voidCbq = new CallbackQueue(); - - protected ByteString topic = ByteString.copyFromUtf8("topic"); - protected ServerConfiguration cfg; - protected HedwigSocketAddress me; - protected ScheduledExecutorService scheduler; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - cfg = new ServerConfiguration(); - me = cfg.getServerAddr(); - scheduler = Executors.newSingleThreadScheduledExecutor(); - tm = new ZkTopicManager(zk, cfg, scheduler); - } - - @Test - public void testGetOwnerSingle() throws Exception { - tm.getOwner(topic, false, addrCbq, null); - Assert.assertEquals(me, check(addrCbq.take())); - } - - protected ByteString mkTopic(int i) { - return ByteString.copyFromUtf8(topic.toStringUtf8() + i); - } - - protected T check(Either ex) throws Exception { - if (ex.left() == null) - throw ex.right(); - else - return ex.left(); - } - - public static class CustomServerConfiguration extends ServerConfiguration { - int port; - - public CustomServerConfiguration(int port) { - this.port = port; - } - - @Override - public int getServerPort() { - return port; - } - } - - @Test - public void testGetOwnerMulti() throws Exception { - ServerConfiguration cfg1 = new CustomServerConfiguration(cfg.getServerPort() + 1), cfg2 = new CustomServerConfiguration( - cfg.getServerPort() + 2); - // TODO change cfg1 cfg2 params - ZkTopicManager tm1 = new ZkTopicManager(zk, cfg1, scheduler), tm2 = new ZkTopicManager(zk, cfg2, scheduler); - - tm.getOwner(topic, false, addrCbq, null); - HedwigSocketAddress owner = check(addrCbq.take()); - - // If we were told to have another person claim the topic, make them - // claim the topic. - if (owner.getPort() == cfg1.getServerPort()) - tm1.getOwner(topic, true, addrCbq, null); - else if (owner.getPort() == cfg2.getServerPort()) - tm2.getOwner(topic, true, addrCbq, null); - if (owner.getPort() != cfg.getServerPort()) - Assert.assertEquals(owner, check(addrCbq.take())); - - for (int i = 0; i < 100; ++i) { - tm.getOwner(topic, false, addrCbq, null); - Assert.assertEquals(owner, check(addrCbq.take())); - - tm1.getOwner(topic, false, addrCbq, null); - Assert.assertEquals(owner, check(addrCbq.take())); - - tm2.getOwner(topic, false, addrCbq, null); - Assert.assertEquals(owner, check(addrCbq.take())); - } - - // Give us 100 chances to choose another owner if not shouldClaim. - for (int i = 0; i < 100; ++i) { - if (!owner.equals(me)) - break; - tm.getOwner(mkTopic(i), false, addrCbq, null); - owner = check(addrCbq.take()); - if (i == 99) - Assert.fail("Never chose another owner"); - } - - // Make sure we always choose ourselves if shouldClaim. - for (int i = 0; i < 100; ++i) { - tm.getOwner(mkTopic(100), true, addrCbq, null); - Assert.assertEquals(me, check(addrCbq.take())); - } - } - - @Test - public void testLoadBalancing() throws Exception { - tm.getOwner(topic, false, addrCbq, null); - - Assert.assertEquals(me, check(addrCbq.take())); - - ServerConfiguration cfg1 = new CustomServerConfiguration(cfg.getServerPort() + 1); - new ZkTopicManager(zk, cfg1, scheduler); - - ByteString topic1 = mkTopic(1); - tm.getOwner(topic1, false, addrCbq, null); - Assert.assertEquals(cfg1.getServerAddr(), check(addrCbq.take())); - - } - - class StubOwnershipChangeListener implements TopicOwnershipChangeListener { - boolean failure; - SynchronousQueue> bsQueue; - - public StubOwnershipChangeListener(SynchronousQueue> bsQueue) { - this.bsQueue = bsQueue; - } - - public void setFailure(boolean failure) { - this.failure = failure; - } - - @Override - public void lostTopic(final ByteString topic) { - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(bsQueue, Pair.of(topic, false)); - } - }).start(); - } - - public void acquiredTopic(final ByteString topic, final Callback callback, final Object ctx) { - new Thread(new Runnable() { - @Override - public void run() { - ConcurrencyUtils.put(bsQueue, Pair.of(topic, true)); - if (failure) { - callback.operationFailed(ctx, new PubSubException.ServiceDownException("Asked to fail")); - } else { - callback.operationFinished(ctx, null); - } - } - }).start(); - } - } - - @Test - public void testOwnershipChange() throws Exception { - SynchronousQueue> bsQueue = new SynchronousQueue>(); - - StubOwnershipChangeListener listener = new StubOwnershipChangeListener(bsQueue); - - tm.addTopicOwnershipChangeListener(listener); - - // regular acquire - tm.getOwner(topic, true, addrCbq, null); - Pair pair = bsQueue.take(); - Assert.assertEquals(topic, pair.first()); - Assert.assertTrue(pair.second()); - Assert.assertEquals(me, check(addrCbq.take())); - assertOwnershipNodeExists(); - - // topic that I already own - tm.getOwner(topic, true, addrCbq, null); - Assert.assertEquals(me, check(addrCbq.take())); - Assert.assertTrue(bsQueue.isEmpty()); - assertOwnershipNodeExists(); - - // regular release - tm.releaseTopic(topic, cb, null); - pair = bsQueue.take(); - Assert.assertEquals(topic, pair.first()); - Assert.assertFalse(pair.second()); - Assert.assertTrue(queue.take()); - assertOwnershipNodeDoesntExist(); - - // releasing topic that I don't own - tm.releaseTopic(mkTopic(0), cb, null); - Assert.assertTrue(queue.take()); - Assert.assertTrue(bsQueue.isEmpty()); - - // set listener to return error - listener.setFailure(true); - - tm.getOwner(topic, true, addrCbq, null); - pair = bsQueue.take(); - Assert.assertEquals(topic, pair.first()); - Assert.assertTrue(pair.second()); - Assert.assertEquals(PubSubException.ServiceDownException.class, ((CompositeException) addrCbq.take().right()) - .getExceptions().iterator().next().getClass()); - Assert.assertFalse(tm.topics.contains(topic)); - Thread.sleep(100); - assertOwnershipNodeDoesntExist(); - - } - - public void assertOwnershipNodeExists() throws Exception { - byte[] data = zk.getData(tm.hubPath(topic), false, null); - Assert.assertEquals(new HedwigSocketAddress(new String(data)), tm.addr); - } - - public void assertOwnershipNodeDoesntExist() throws Exception { - try { - zk.getData(tm.hubPath(topic), false, null); - Assert.assertTrue(false); - } catch (KeeperException e) { - Assert.assertEquals(e.code(), KeeperException.Code.NONODE); - } - } - - @Test - public void testZKClientDisconnected() throws Exception { - // First assert ownership of the topic - tm.getOwner(topic, true, addrCbq, null); - Assert.assertEquals(me, check(addrCbq.take())); - - // Suspend the ZKTopicManager and make sure calls to getOwner error out - tm.isSuspended = true; - tm.getOwner(topic, true, addrCbq, null); - Assert.assertEquals(PubSubException.ServiceDownException.class, addrCbq.take().right().getClass()); - // Release the topic. This should not error out even if suspended. - tm.releaseTopic(topic, cb, null); - Assert.assertTrue(queue.take()); - assertOwnershipNodeDoesntExist(); - - // Restart the ZKTopicManager and make sure calls to getOwner are okay - tm.isSuspended = false; - tm.getOwner(topic, true, addrCbq, null); - Assert.assertEquals(me, check(addrCbq.take())); - assertOwnershipNodeExists(); - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/TestZkUtils.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/TestZkUtils.java deleted file mode 100644 index 94a2d61a015..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/TestZkUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.zookeeper; - -import java.util.Arrays; - -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.ZooDefs.Ids; -import org.junit.Assert; -import org.junit.Test; - -public class TestZkUtils extends ZooKeeperTestBase { - - @Test - public void testCreateFullPathOptimistic() throws Exception { - testPath("/a/b/c", CreateMode.EPHEMERAL); - - testPath("/b/c/d", CreateMode.PERSISTENT); - - testPath("/b/c/d/e", CreateMode.PERSISTENT); - - } - - void testPath(String path, CreateMode mode) throws Exception { - byte[] data = new byte[] { 77 }; - ZkUtils.createFullPathOptimistic(zk, path, data, Ids.OPEN_ACL_UNSAFE, mode, strCb, null); - Assert.assertTrue(queue.take()); - Assert.assertTrue(Arrays.equals(data, zk.getData(path, false, null))); - - } - -} diff --git a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/ZooKeeperTestBase.java b/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/ZooKeeperTestBase.java deleted file mode 100644 index a2c9d76d935..00000000000 --- a/src/contrib/hedwig/server/src/test/java/org/apache/hedwig/zookeeper/ZooKeeperTestBase.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.hedwig.zookeeper; - -import java.util.concurrent.SynchronousQueue; - -import org.apache.zookeeper.AsyncCallback; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.KeeperException.Code; -import org.apache.zookeeper.test.ClientBase; -import org.junit.After; -import org.junit.Before; - -import org.apache.hedwig.exceptions.PubSubException; -import org.apache.hedwig.util.ConcurrencyUtils; -import org.apache.hedwig.util.Callback; - -/** - * This is a base class for any tests that need a ZooKeeper client/server setup. - * - */ -public abstract class ZooKeeperTestBase extends ClientBase { - - protected ZooKeeper zk; - - protected SynchronousQueue queue = new SynchronousQueue(); - - protected Callback cb = new Callback() { - - @Override - public void operationFinished(Object ctx, Void result) { - new Thread(new Runnable() { - public void run() { - ConcurrencyUtils.put(queue, true); - } - }).start(); - } - - @Override - public void operationFailed(Object ctx, PubSubException exception) { - new Thread(new Runnable() { - public void run() { - ConcurrencyUtils.put(queue, false); - } - }).start(); - } - }; - - protected AsyncCallback.StringCallback strCb = new AsyncCallback.StringCallback() { - @Override - public void processResult(int rc, String path, Object ctx, String name) { - ConcurrencyUtils.put(queue, rc == Code.OK.intValue()); - } - }; - - protected AsyncCallback.VoidCallback voidCb = new AsyncCallback.VoidCallback() { - @Override - public void processResult(int rc, String path, Object ctx) { - ConcurrencyUtils.put(queue, rc == Code.OK.intValue()); - } - }; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - zk = createClient(); - } - - @Override - @After - public void tearDown() throws Exception { - super.tearDown(); - zk.close(); - } - -} diff --git a/src/contrib/huebrowser/README b/src/contrib/huebrowser/README deleted file mode 100644 index ef921f5aed0..00000000000 --- a/src/contrib/huebrowser/README +++ /dev/null @@ -1,62 +0,0 @@ - -ZooKeeper Browser - Hue Application -=================================== - -The ZooKeeper Browser application allows you to see how the cluster nodes are working and also allows you to do CRUD operations on the znode hierarchy. - -Requirements ------------- - -Hue-1.0: - * http://github.com/downloads/cloudera/hue/hue-1.0.tgz - * http://github.com/downloads/cloudera/hue/release-notes-1.0.html - -ZooKeeper REST gateway: - * available as contrib: contrib/rest - -How to install? ---------------- - -First of all you need to install Hue 1.0 release: - - * http://archive.cloudera.com/cdh/3/hue/sdk/sdk.html - * http://github.com/cloudera/hue/tree/release-1.0 - -After you finish the previous step you should copy the zkui/ folder to apps/ and register the new application: - - * $ ./build/env/bin/python tools/app_reg/app_reg.py --install apps/zkui - * $ ./build/env/bin/python tools/app_reg/app_reg.py --list 2>&1 | grep zkui - zkui 0.1 /Users/philip/src/hue/apps/zkui - - -And restart the Hue application server. - -Configuration -------------- - -Edit zkui/src/zkui/settings.py: - -CLUSTERS = [{ - 'nice_name': 'Default', - 'hostport': 'localhost:2181,localhost:2182,localhost:2183', - 'rest_gateway': 'http://localhost:9998' - }, { - # ... and more clusters - } -] - -What is Hue? ------------- - -Wiki: http://wiki.github.com/cloudera/hue/ -Main Repo: http://github.com/cloudera/hue - -Hue is both a web UI for Hadoop and a framework to create interactive web applications. It features a FileBrowser for accessing HDFS, JobSub and JobBrowser applications for submitting and viewing MapReduce jobs, a Beeswax application for interacting with Hive. On top of that, the web frontend is mostly built from declarative widgets that require no JavaScript and are easy to learn. - -What is ZooKeeper? ------------------- - -http://hadoop.apache.org/zookeeper/ - -ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed. - diff --git a/src/contrib/huebrowser/zkui/Makefile b/src/contrib/huebrowser/zkui/Makefile deleted file mode 100644 index 9c22d1c22bc..00000000000 --- a/src/contrib/huebrowser/zkui/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -ifeq ($(ROOT),) - $(error "Error: Expect the environment variable $$ROOT to point to the Desktop installation") -endif - -include $(ROOT)/Makefile.sdk diff --git a/src/contrib/huebrowser/zkui/setup.py b/src/contrib/huebrowser/zkui/setup.py deleted file mode 100644 index d2ae009123d..00000000000 --- a/src/contrib/huebrowser/zkui/setup.py +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from setuptools import setup, find_packages -import os - -def expand_package_data(src_dirs, strip=""): - ret = [] - for src_dir in src_dirs: - for path, dnames, fnames in os.walk(src_dir): - for fname in fnames: - ret.append(os.path.join(path, fname).replace(strip, "")) - return ret - -os.chdir(os.path.dirname(os.path.abspath(__file__))) -setup( - name = "zkui", - version = "0.1", - url = 'http://hadoop.apache.org/zookeeper/', - description = 'ZooKeeper Browser', - packages = find_packages('src'), - package_dir = {'': 'src'}, - install_requires = ['setuptools', 'desktop'], - entry_points = { 'desktop.sdk.application': 'zkui=zkui' }, - zip_safe = False, - package_data = { - # Include static resources. Package_data doesn't - # deal well with directory globs, so we enumerate - # the files manually. - 'zkui': expand_package_data( - ["src/zkui/templates", "src/zkui/static"], - "src/zkui/") - } -) diff --git a/src/contrib/huebrowser/zkui/src/zkui/__init__.py b/src/contrib/huebrowser/zkui/src/zkui/__init__.py deleted file mode 100644 index eccc8816c1d..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - diff --git a/src/contrib/huebrowser/zkui/src/zkui/forms.py b/src/contrib/huebrowser/zkui/src/zkui/forms.py deleted file mode 100644 index 6b1f178fb9d..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/forms.py +++ /dev/null @@ -1,29 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from django import forms -from django.forms.widgets import Textarea, HiddenInput - -class CreateZNodeForm(forms.Form): - name = forms.CharField(max_length=64) - data = forms.CharField(required=False, widget=Textarea) - sequence = forms.BooleanField(required=False) - -class EditZNodeForm(forms.Form): - data = forms.CharField(required=False, widget=Textarea) - version = forms.IntegerField(required=False, widget=HiddenInput) - - diff --git a/src/contrib/huebrowser/zkui/src/zkui/models.py b/src/contrib/huebrowser/zkui/src/zkui/models.py deleted file mode 100644 index a46696b6dc2..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/models.py +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - diff --git a/src/contrib/huebrowser/zkui/src/zkui/rest.py b/src/contrib/huebrowser/zkui/src/zkui/rest.py deleted file mode 100644 index e4874a1ec81..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/rest.py +++ /dev/null @@ -1,230 +0,0 @@ - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import urllib2 -import urllib -import simplejson - -from contextlib import contextmanager - -class RequestWithMethod(urllib2.Request): - """ Request class that know how to set the method name """ - def __init__(self, *args, **kwargs): - urllib2.Request.__init__(self, *args, **kwargs) - self._method = None - - def get_method(self): - return self._method or \ - urllib2.Request.get_method(self) - - def set_method(self, method): - self._method = method - -class ZooKeeper(object): - - class Error(Exception): pass - - class NotFound(Error): pass - - class ZNodeExists(Error): pass - - class InvalidSession(Error): pass - - class WrongVersion(Error): pass - - def __init__(self, uri = 'http://localhost:9998'): - self._base = uri - self._session = None - - def start_session(self, expire=5, id=None): - """ Create a session and return the ID """ - if id is None: - url = "%s/sessions/v1/?op=create&expire=%d" % (self._base, expire) - self._session = self._do_post(url)['id'] - else: - self._session = id - return self._session - - def close_session(self): - """ Close the session on the server """ - if self._session is not None: - url = '%s/sessions/v1/%s' % (self._base, self._session) - self._do_delete(url) - self._session = None - - def heartbeat(self): - """ Send a heartbeat request. This is needed in order to keep a session alive """ - if self._session is not None: - url = '%s/sessions/v1/%s' % (self._base, self._session) - self._do_put(url, '') - - @contextmanager - def session(self, *args, **kwargs): - """ Session handling using a context manager """ - yield self.start_session(*args, **kwargs) - self.close_session() - - def get(self, path): - """ Get a node """ - url = "%s/znodes/v1%s" % (self._base, path) - return self._do_get(url) - - def get_children(self, path): - """ Get all the children for a given path. This function creates a generator """ - for child_path in self.get_children_paths(path, uris=True): - try: - yield self._do_get(child_path) - except ZooKeeper.NotFound: - continue - - def get_children_paths(self, path, uris=False): - """ Get the paths for children nodes """ - url = "%s/znodes/v1%s?view=children" % (self._base, path) - resp = self._do_get(url) - for child in resp.get('children', []): - yield child if not uris else resp['child_uri_template']\ - .replace('{child}', urllib2.quote(child)) - - def create(self, path, data=None, sequence=False, ephemeral=False): - """ Create a new node. By default this call creates a persistent znode. - - You can also create an ephemeral or a sequential znode. - """ - ri = path.rindex('/') - head, name = path[:ri+1], path[ri+1:] - if head != '/': head = head[:-1] - - flags = { - 'null': 'true' if data is None else 'false', - 'ephemeral': 'true' if ephemeral else 'false', - 'sequence': 'true' if sequence else 'false' - } - if ephemeral: - if self._session: - flags['session'] = self._session - else: - raise ZooKeeper.Error, 'You need a session '\ - 'to create an ephemeral node' - flags = urllib.urlencode(flags) - - url = "%s/znodes/v1%s?op=create&name=%s&%s" % \ - (self._base, head, name, flags) - - return self._do_post(url, data) - - def set(self, path, data=None, version=-1, null=False): - """ Set the value of node """ - url = "%s/znodes/v1%s?%s" % (self._base, path, \ - urllib.urlencode({ - 'version': version, - 'null': 'true' if null else 'false' - })) - return self._do_put(url, data) - - def delete(self, path, version=-1): - """ Delete a znode """ - if type(path) is list: - map(lambda el: self.delete(el, version), path) - return - - url = '%s/znodes/v1%s?%s' % (self._base, path, \ - urllib.urlencode({ - 'version':version - })) - try: - return self._do_delete(url) - except urllib2.HTTPError, e: - if e.code == 412: - raise ZooKeeper.WrongVersion(path) - elif e.code == 404: - raise ZooKeeper.NotFound(path) - raise - - def recursive_delete(self, path): - """ Delete all the nodes from the tree """ - for child in self.get_children_paths(path): - fp = ("%s/%s" % (path, child)).replace('//', '/') - self.recursive_delete(fp) - self.delete(path) - - def exists(self, path): - """ Do a znode exists """ - try: - self.get(path) - return True - except ZooKeeper.NotFound: - return False - - def _do_get(self, uri): - """ Send a GET request and convert errors to exceptions """ - try: - req = urllib2.urlopen(uri) - resp = simplejson.load(req) - - if 'Error' in resp: - raise ZooKeeper.Error(resp['Error']) - - return resp - except urllib2.HTTPError, e: - if e.code == 404: - raise ZooKeeper.NotFound(uri) - raise - - def _do_post(self, uri, data=None): - """ Send a POST request and convert errors to exceptions """ - try: - req = urllib2.Request(uri, {}) - req.add_header('Content-Type', 'application/octet-stream') - if data is not None: - req.add_data(data) - - resp = simplejson.load(urllib2.urlopen(req)) - if 'Error' in resp: - raise ZooKeeper.Error(resp['Error']) - return resp - - except urllib2.HTTPError, e: - if e.code == 201: - return True - elif e.code == 409: - raise ZooKeeper.ZNodeExists(uri) - elif e.code == 401: - raise ZooKeeper.InvalidSession(uri) - raise - - def _do_delete(self, uri): - """ Send a DELETE request """ - req = RequestWithMethod(uri) - req.set_method('DELETE') - req.add_header('Content-Type', 'application/octet-stream') - return urllib2.urlopen(req).read() - - def _do_put(self, uri, data): - """ Send a PUT request """ - try: - req = RequestWithMethod(uri) - req.set_method('PUT') - req.add_header('Content-Type', 'application/octet-stream') - if data is not None: - req.add_data(data) - - return urllib2.urlopen(req).read() - except urllib2.HTTPError, e: - if e.code == 412: # precondition failed - raise ZooKeeper.WrongVersion(uri) - raise - diff --git a/src/contrib/huebrowser/zkui/src/zkui/settings.py b/src/contrib/huebrowser/zkui/src/zkui/settings.py deleted file mode 100644 index 844c6952da8..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/settings.py +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -DJANGO_APPS = [ "zkui" ] -NICE_NAME = "ZooKeeper Browser" -REQUIRES_HADOOP = False - -CLUSTERS = [{ - 'nice_name': 'Default', - 'hostport': 'localhost:2181,localhost:2182,localhost:2183', - 'rest_gateway': 'http://localhost:9998' - } -] - -DEPENDER_PACKAGE_YMLS = [ - "src/zkui/static/js/package.yml", -] diff --git a/src/contrib/huebrowser/zkui/src/zkui/static/art/line_icons.png b/src/contrib/huebrowser/zkui/src/zkui/static/art/line_icons.png deleted file mode 100644 index 1da4a294b34c5bcfe27ce676c85a2f363bab95c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7499 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 UeFd$x$^ZZW07*qoM6N<$f+d!kO#lD@ diff --git a/src/contrib/huebrowser/zkui/src/zkui/static/bootstrap.js b/src/contrib/huebrowser/zkui/src/zkui/static/bootstrap.js deleted file mode 100644 index 8e3fbfb8e6b..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/static/bootstrap.js +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -CCS.Desktop.register({ - Zkui : { - name : 'ZooKeeper Browser', - css : '/zkui/static/css/zkui.css', - require: [ 'Zkui' ], - launch: function(path, options){ - return new Zkui(path || '/zkui/', options); - }, - menu: { - id: 'ccs-zkui-menu', - img: { - src: '/zkui/static/art/zkui.png' - } - }, - help: '/help/zkui/' - } -}); diff --git a/src/contrib/huebrowser/zkui/src/zkui/static/css/zkui.css b/src/contrib/huebrowser/zkui/src/zkui/static/css/zkui.css deleted file mode 100644 index c49f392c087..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/static/css/zkui.css +++ /dev/null @@ -1,56 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -.zkui img.zkui_icon { - width: 55px; - height: 55px; - position: absolute; - top: 27px; - left: 3px; -} - -div.zkui .left_col li { - margin: 5px 0px; - font-size: 16px; - background-color: white; - color: black; - padding: 2px 1px 1px 5px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - border: solid black 1px; -} - -div.zkui .left_col li:hover { - background-color: lightBlue; - color: white; -} - -div.zkui .left_col li a { - color: black; - display: block; -} - -div.zkui .left_col li a:hover { - text-decoration: none; -} - -div.zkui .createZnodeForm td, -div.zkui .editZnodeForm td { - padding: 5px; -} - diff --git a/src/contrib/huebrowser/zkui/src/zkui/static/help/index.html b/src/contrib/huebrowser/zkui/src/zkui/static/help/index.html deleted file mode 100644 index 8fe70bd961d..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/static/help/index.html +++ /dev/null @@ -1,10 +0,0 @@ - -

ZooKeeper Browser

- - -

ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services

- -

About

- -

The ZooKeeper Browser application allows you to see how the cluster nodes are working and also allows you to do CRUD operations on the znode hierarchy.

- diff --git a/src/contrib/huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js b/src/contrib/huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js deleted file mode 100644 index c8bf3833cee..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* ---- - -script: Zkui.js - -description: Defines Zkui; a Hue application that extends CCS.JBrowser. - -authors: -- Unknown - -requires: -- ccs-shared/CCS.JBrowser - -provides: [Zkui] - -... -*/ -ART.Sheet.define('window.art.browser.zkui', { - 'min-width': 620 -}); - -var Zkui = new Class({ - - Extends: CCS.JBrowser, - - options: { - className: 'art browser logo_header zkui' - }, - - initialize: function(path, options){ - this.parent(path || '/zkui/', options); - } - -}); diff --git a/src/contrib/huebrowser/zkui/src/zkui/static/js/package.yml b/src/contrib/huebrowser/zkui/src/zkui/static/js/package.yml deleted file mode 100644 index de707f8b15b..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/static/js/package.yml +++ /dev/null @@ -1,5 +0,0 @@ -copyright: Apache License v2.0 -version: 0.1 -description: ZooKeeper Browser -name: ZooKeeper Browser -sources: [Source/Zkui/Zkui.js] diff --git a/src/contrib/huebrowser/zkui/src/zkui/stats.py b/src/contrib/huebrowser/zkui/src/zkui/stats.py deleted file mode 100644 index 48f35dd3787..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/stats.py +++ /dev/null @@ -1,170 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import socket -import re - -from StringIO import StringIO - -class Session(object): - - class BrokenLine(Exception): pass - - def __init__(self, session): - m = re.search('/(\d+\.\d+\.\d+\.\d+):(\d+)\[(\d+)\]\((.*)\)', session) - if m: - self.host = m.group(1) - self.port = m.group(2) - self.interest_ops = m.group(3) - for d in m.group(4).split(","): - k,v = d.split("=") - self.__dict__[k] = v - else: - raise Session.BrokenLine() - -class ZooKeeperStats(object): - - def __init__(self, host='localhost', port='2181', timeout=1): - self._address = (host, int(port)) - self._timeout = timeout - - def get_stats(self): - """ Get ZooKeeper server stats as a map """ - data = self._send_cmd('mntr') - if data: - return self._parse(data) - else: - data = self._send_cmd('stat') - return self._parse_stat(data) - - def get_clients(self): - """ Get ZooKeeper server clients """ - clients = [] - - stat = self._send_cmd('stat') - if not stat: - return clients - - sio = StringIO(stat) - - #skip two lines - sio.readline() - sio.readline() - - for line in sio: - if not line.strip(): - break - try: - clients.append(Session(line.strip())) - except Session.BrokenLine: - continue - - return clients - - def _create_socket(self): - return socket.socket() - - def _send_cmd(self, cmd): - """ Send a 4letter word command to the server """ - s = self._create_socket() - s.settimeout(self._timeout) - - s.connect(self._address) - s.send(cmd) - - data = s.recv(2048) - s.close() - - return data - - def _parse(self, data): - """ Parse the output from the 'mntr' 4letter word command """ - h = StringIO(data) - - result = {} - for line in h.readlines(): - try: - key, value = self._parse_line(line) - result[key] = value - except ValueError: - pass # ignore broken lines - - return result - - def _parse_stat(self, data): - """ Parse the output from the 'stat' 4letter word command """ - h = StringIO(data) - - result = {} - - version = h.readline() - if version: - result['zk_version'] = version[version.index(':')+1:].strip() - - # skip all lines until we find the empty one - while h.readline().strip(): pass - - for line in h.readlines(): - m = re.match('Latency min/avg/max: (\d+)/(\d+)/(\d+)', line) - if m is not None: - result['zk_min_latency'] = int(m.group(1)) - result['zk_avg_latency'] = int(m.group(2)) - result['zk_max_latency'] = int(m.group(3)) - continue - - m = re.match('Received: (\d+)', line) - if m is not None: - result['zk_packets_received'] = int(m.group(1)) - continue - - m = re.match('Sent: (\d+)', line) - if m is not None: - result['zk_packets_sent'] = int(m.group(1)) - continue - - m = re.match('Outstanding: (\d+)', line) - if m is not None: - result['zk_outstanding_requests'] = int(m.group(1)) - continue - - m = re.match('Mode: (.*)', line) - if m is not None: - result['zk_server_state'] = m.group(1) - continue - - m = re.match('Node count: (\d+)', line) - if m is not None: - result['zk_znode_count'] = int(m.group(1)) - continue - - return result - - def _parse_line(self, line): - try: - key, value = map(str.strip, line.split('\t')) - except ValueError: - raise ValueError('Found invalid line: %s' % line) - - if not key: - raise ValueError('The key is mandatory and should not be empty') - - try: - value = int(value) - except (TypeError, ValueError): - pass - - return key, value - diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/clients.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/clients.mako deleted file mode 100644 index 2bee9a7c52d..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/clients.mako +++ /dev/null @@ -1,51 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> - -<%namespace name="shared" file="shared_components.mako" /> - -${shared.header("ZooKeeper Browser > Clients > %s:%s" % (host, port))} - -

${host}:${port} :: client connections

-
- -% if clients: - - - - - - - - - - - % for client in clients: - - - - - - - - - % endfor -
HostPortInterest OpsQueuedReceivedSent
${client.host}${client.port}${client.interest_ops}${client.queued}${client.recved}${client.sent}
-% endif - -${shared.footer()} - diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/create.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/create.mako deleted file mode 100644 index 2a8b8ccca40..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/create.mako +++ /dev/null @@ -1,34 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> -<%namespace name="shared" file="shared_components.mako" /> - -${shared.header("ZooKeeper Browser > Create Znode")} - -

Create New Znode :: ${path}

-

- -
- - ${form.as_table()|n} - -
- -
-
- -${shared.footer()} diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/edit.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/edit.mako deleted file mode 100644 index 997bd07afc1..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/edit.mako +++ /dev/null @@ -1,34 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> -<%namespace name="shared" file="shared_components.mako" /> - -${shared.header("ZooKeeper Browser > Edit Znode > %s" % path)} - -

Edit Znode Data :: ${path}

-

- -
- - ${form.as_table()|n} - -
- -
-
- -${shared.footer()} diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/index.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/index.mako deleted file mode 100644 index 567919dd0f1..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/index.mako +++ /dev/null @@ -1,54 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> -<%namespace name="shared" file="shared_components.mako" /> - -${shared.header("ZooKeeper Browser")} - -

Overview

- -
- -% for i, c in enumerate(overview): -

${i+1}. ${c['nice_name']} Cluster Overview


- - - - - - - - - - - - % for host, stats in c['stats'].items(): - - - - - - - - % endfor -
NodeRoleAvg LatencyWatch CountVersion
${host}${stats.get('zk_server_state', '')}${stats.get('zk_avg_latency', '')}${stats.get('zk_watch_count', '')}${stats.get('zk_version', '')}
- -

-% endfor - -${shared.footer()} - diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/shared_components.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/shared_components.mako deleted file mode 100644 index f9a45893411..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/shared_components.mako +++ /dev/null @@ -1,66 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> - -<%! -import datetime -from django.template.defaultfilters import urlencode, escape -from zkui import settings -%> - -<%def name="header(title='ZooKeeper Browser', toolbar=True)"> - - - - ${title} - - - % if toolbar: -
- -
- % endif - -
-
- -
- -

Clusters

- -
- -
- - -<%def name="info_button(url, text)"> - ${text} - - -<%def name="footer()"> -
-
- - - diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako deleted file mode 100644 index aa721695aca..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/tree.mako +++ /dev/null @@ -1,75 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> -<%namespace name="shared" file="shared_components.mako" /> - -${shared.header("ZooKeeper Browser > Tree > %s > %s" % (cluster['nice_name'], path))} - -

${cluster['nice_name'].lower()} :: ${path}

-
- - - - - - % for child in children: - - % endfor -
Children
- - ${child} - - Delete -
-
- - ${shared.info_button(url('zkui.views.create', id=cluster['id'], path=path), 'Create New')} - - -
- -

data :: base64 :: length :: ${znode.get('dataLength', 0)}

-
- - -
- - ${shared.info_button(url('zkui.views.edit_as_base64', id=cluster['id'], path=path), 'Edit as Base64')} - ${shared.info_button(url('zkui.views.edit_as_text', id=cluster['id'], path=path), 'Edit as Text')} - -
-
- -

stat information

-
- - - - - % for key in ('pzxid', 'ctime', 'aversion', 'mzxid', \ - 'ephemeralOwner', 'version', 'mtime', 'cversion', 'czxid'): - - % endfor -
KeyValue
${key}${znode[key]}
- -
-Details on stat information. - -${shared.footer()} - diff --git a/src/contrib/huebrowser/zkui/src/zkui/templates/view.mako b/src/contrib/huebrowser/zkui/src/zkui/templates/view.mako deleted file mode 100644 index e046afc4fe5..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/templates/view.mako +++ /dev/null @@ -1,128 +0,0 @@ -<%! -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -%> -<%namespace name="shared" file="shared_components.mako" /> - -${shared.header("ZooKeeper Browser > %s" % cluster['nice_name'])} - -<%def name="show_stats(stats)"> - - Key - Value - - - Version - ${stats.get('zk_version')} - - - Latency - Min: ${stats.get('zk_min_latency', '')} - Avg: ${stats.get('zk_avg_latency', '')} - Max: ${stats.get('zk_max_latency', '')} - - - Packets - Sent: ${stats.get('zk_packets_sent', '')} - Received: ${stats.get('zk_packets_received', '')} - - - - Outstanding Requests - ${stats.get('zk_outstanding_requests', '')} - - - Watch Count - ${stats.get('zk_watch_count', '')} - - - Open FD Count - ${stats.get('zk_open_file_descriptor_count', '')} - - - Max FD Count - ${stats.get('zk_max_file_descriptor_count', '')} - - - - -

${cluster['nice_name']} Cluster Overview

- -${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Znode Hierarchy')} - -

- -% if leader: -

General

- - - - - - - - - - - - - - - -
KeyValue
ZNode Count${leader.get('zk_znode_count', '')}
Ephemerals Count${leader.get('zk_ephemerals_count', '')}
Approximate Data Size${leader.get('zk_approximate_data_size', '')} bytes
-

-% endif - -% if leader: -

node :: ${leader['host']} :: leader

- - ${shared.info_button(url('zkui.views.clients', host=leader['host']), 'View Client Connections')} - -

- - ${show_stats(leader)} - - - - - - - - - - - - - -
Followers${leader.get('zk_followers', '')}
Synced Followers${leader.get('zk_synced_followers', '')}
Pending Syncs${leader.get('zk_pending_syncs', '')}
-

-% endif - -% for stats in followers: -

node :: ${stats['host']} :: follower

-
- - ${shared.info_button(url('zkui.views.clients', host=stats['host']), 'View Client Connections')} - -

- - ${show_stats(stats)} -
-

-% endfor - -${shared.footer()} - diff --git a/src/contrib/huebrowser/zkui/src/zkui/urls.py b/src/contrib/huebrowser/zkui/src/zkui/urls.py deleted file mode 100644 index f795f7e71e5..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/urls.py +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from django.conf.urls.defaults import patterns, url - -urlpatterns = patterns('zkui', - url(r'^$', 'views.index'), - url(r'view/(?P\d+)$', 'views.view'), - url(r'clients/(?P.+)$', 'views.clients'), - url(r'tree/(?P\d+)(?P.+)$', 'views.tree'), - url(r'create/(?P\d+)(?P.*)$', 'views.create'), - url(r'delete/(?P\d+)(?P.*)$', 'views.delete'), - url(r'edit/base64/(?P\d+)(?P.*)$', 'views.edit_as_base64'), - url(r'edit/text/(?P\d+)(?P.*)$', 'views.edit_as_text') -) diff --git a/src/contrib/huebrowser/zkui/src/zkui/utils.py b/src/contrib/huebrowser/zkui/src/zkui/utils.py deleted file mode 100644 index fb013170f6c..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/utils.py +++ /dev/null @@ -1,33 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from zkui import settings - -from django.http import Http404 - -def get_cluster_or_404(id): - try: - id = int(id) - if not (0 <= id < len(settings.CLUSTERS)): - raise ValueError, 'Undefined cluster id.' - except (TypeError, ValueError): - raise Http404() - - cluster = settings.CLUSTERS[id] - cluster['id'] = id - - return cluster - diff --git a/src/contrib/huebrowser/zkui/src/zkui/views.py b/src/contrib/huebrowser/zkui/src/zkui/views.py deleted file mode 100644 index 64d926b6063..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/views.py +++ /dev/null @@ -1,165 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from desktop.lib.django_util import render -from django.http import Http404 - -from zkui import settings -from zkui.stats import ZooKeeperStats -from zkui.rest import ZooKeeper -from zkui.utils import get_cluster_or_404 -from zkui.forms import CreateZNodeForm, EditZNodeForm - -def _get_global_overview(): - overview = [] - for c in settings.CLUSTERS: - overview.append(_get_overview(c)) - return overview - -def _get_overview(cluster): - stats = {} - for s in cluster['hostport'].split(','): - host, port = map(str.strip, s.split(':')) - - zks = ZooKeeperStats(host, port) - stats[s] = zks.get_stats() or {} - - cluster['stats'] = stats - return cluster - -def _group_stats_by_role(cluster): - leader, followers = None, [] - for host, stats in cluster['stats'].items(): - stats['host'] = host - - if stats.get('zk_server_state') == 'leader': - leader = stats - - elif stats.get('zk_server_state') == 'follower': - followers.append(stats) - - return leader, followers - -def index(request): - overview = _get_global_overview() - return render('index.mako', request, - dict(overview=overview)) - -def view(request, id): - cluster = get_cluster_or_404(id) - - cluster = _get_overview(cluster) - leader, followers = _group_stats_by_role(cluster) - - return render('view.mako', request, - dict(cluster=cluster, leader=leader, followers=followers)) - -def clients(request, host): - parts = host.split(':') - if len(parts) != 2: - raise Http404 - - host, port = parts - zks = ZooKeeperStats(host, port) - clients = zks.get_clients() - - return render('clients.mako', request, - dict(host=host, port=port, clients=clients)) - -def tree(request, id, path): - cluster = get_cluster_or_404(id) - zk = ZooKeeper(cluster['rest_gateway']) - - znode = zk.get(path) - children = sorted(zk.get_children_paths(path)) - - return render('tree.mako', request, - dict(cluster=cluster, path=path, \ - znode=znode, children=children)) - -def delete(request, id, path): - cluster = get_cluster_or_404(id) - if request.method == 'POST': - zk = ZooKeeper(cluster['rest_gateway']) - try: - zk.recursive_delete(path) - except ZooKeeper.NotFound: - pass - - return tree(request, id, path[:path.rindex('/')] or '/') - -def create(request, id, path): - cluster = get_cluster_or_404(id) - - if request.method == 'POST': - form = CreateZNodeForm(request.POST) - if form.is_valid(): - zk = ZooKeeper(cluster['rest_gateway']) - - full_path = ("%s/%s" % (path, form.cleaned_data['name']))\ - .replace('//', '/') - - zk.create(full_path, \ - form.cleaned_data['data'], \ - sequence = form.cleaned_data['sequence']) - return tree(request, id, path) - else: - form = CreateZNodeForm() - - return render('create.mako', request, - dict(path=path, form=form)) - -def edit_as_base64(request, id, path): - cluster = get_cluster_or_404(id) - zk = ZooKeeper(cluster['rest_gateway']) - node = zk.get(path) - - if request.method == 'POST': - form = EditZNodeForm(request.POST) - if form.is_valid(): - # TODO is valid base64 string? - data = form.cleaned_data['data'].decode('base64') - zk.set(path, data, form.cleaned_data['version']) - - return tree(request, id, path) - else: - form = EditZNodeForm(dict(\ - data=node.get('data64', ''), - version=node.get('version', '-1'))) - - return render('edit.mako', request, - dict(path=path, form=form)) - -def edit_as_text(request, id, path): - cluster = get_cluster_or_404(id) - zk = ZooKeeper(cluster['rest_gateway']) - node = zk.get(path) - - if request.method == 'POST': - form = EditZNodeForm(request.POST) - if form.is_valid(): - zk.set(path, form.cleaned_data['data']) - - return tree(request, id, path) - else: - form = EditZNodeForm(dict(data=node.get('data64', '')\ - .decode('base64').strip(), - version=node.get('version', '-1'))) - - return render('edit.mako', request, - dict(path=path, form=form)) - - diff --git a/src/contrib/huebrowser/zkui/src/zkui/windmilltests.py b/src/contrib/huebrowser/zkui/src/zkui/windmilltests.py deleted file mode 100644 index ba44e26865f..00000000000 --- a/src/contrib/huebrowser/zkui/src/zkui/windmilltests.py +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from desktop.lib.windmill_util import logged_in_client - -def test_zkui(): - """ launches the default view for zkui """ - client = logged_in_client() - client.click(id='ccs-zkui-menu') - client.waits.forElement(classname='CCS-ZKUI', timeout='2000') diff --git a/src/contrib/loggraph/README.txt b/src/contrib/loggraph/README.txt deleted file mode 100644 index 1865fa5ab0c..00000000000 --- a/src/contrib/loggraph/README.txt +++ /dev/null @@ -1,69 +0,0 @@ -LogGraph README - -1 - About -LogGraph is an application for viewing and filtering zookeeper logs. It can handle transaction logs and message logs. - -2 - Compiling - -Run "ant jar" in src/contrib/loggraph/. This will download all dependencies and compile all the loggraph code. - -Once compilation has finished, you can run it the the loggraph.sh script in src/contrib/loggraph/bin. This will start and embedded web server on your machine. -Navigate to http://localhost:8182/graph/main.html - -3 - Usage -LogGraph presents the user with 4 views, - - a) Simple log view - This view simply displays the log text. This isn't very useful without filters (see "Filtering the logs"). - - b) Server view - The server view shows the interactions between the different servers in an ensemble. The X axis represents time. - * Exceptions show up as red dots. Hovering your mouse over them will give you more details of the exception - * The colour of the line represents the election state of the server. - - orange means LOOKING for leader - - dark green means the server is the leader - - light green means the server is following a leader - - yellow means there isn't enough information to determine the state of the server. - * The gray arrows denote election messages between servers. Pink dashed arrows are messages that were sent but never delivered. - - c) Session view - The session view shows the lifetime of sessions on a server. Use the time filter to narrow down the view. Any more than about 2000 events will take a long time to view in your browser. - The X axis represents time. Each line is a session. The black dots represent events on the session. You can click on the black dots for more details of the event. - - d) Stats view - There is currently only one statistics view, Transactions/minute. Suggestions for other statistic views are very welcome. - -4 - Filtering the logs -The logs can be filtered in 2 ways, by time and by content. - -To filter by time simply move the slider to the desired start time. The time window specifies how many milliseconds after and including the start time will be displayed. - -Content filtering uses a adhoc filtering language, using prefix notation. The language looks somewhat similar to lisp. A statement in the language takes the form (op arg arg ....). A statement resolves to a boolean value. Statements can be nested. - -4.1 - Filter arguments -An argument can be a number, a string or a symbol. A number is any argument which starts with -, + or 0 to 9. If the number starts with 0x it is interpretted as hexidecimal. Otherwise it is interpretted as decimal. If the argument begins with a double-quote, (") it is interpretted as a string. Anything else is interpretted as a symbol. - -4.2 - Filter symbols -The possible filter symbols are: - -client-id : number, the session id of the client who initiated a transaction. -cxid : number, the cxid of a transaction -zxid : number, the zxid of a transaction -operation : string, the operation being performed, for example "setData", "createSession", "closeSession", "error", "create" - -4.3 - Filter operations -The possible filter operations are: - -or : logical or, takes 1 or more arguments which must be other statements. -and : logical and, takes 1 or more arguments which must be other statements. -not : logical not, takes 1 argument which must be another statement. -xor : exclusive or, takes 1 or more arguments which must be other statements. -= : equals, takes 1 or more arguments, which must all be equal to each other to return true. -> : greater than, takes 1 or more arguments, to return true the 1st argument must be greater than the 2nd argument which must be greater than the 3rd argument and so on... -< : less than, takes 1 or more arguments, to return true the 1st argument must be less than the 2nd argument which must be less than the 3rd argument and so on... - -4.3 - Filter examples -Give me all the setData operations with session id 0xdeadbeef or 0xcafeb33r but not with zxid 0x12341234 -> - -(and (= operation "setData") (or (= client-id 0xdeadbeef) (= client-id 0xcafeb33r)) (not (= zxid 0x12341234))) - diff --git a/src/contrib/loggraph/bin/loggraph-dev.sh b/src/contrib/loggraph/bin/loggraph-dev.sh deleted file mode 100755 index 0b82efa35b0..00000000000 --- a/src/contrib/loggraph/bin/loggraph-dev.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -make_canonical () { - cd $1; pwd; -} - -SCRIPTDIR=`dirname $0` -BUILDDIR=`make_canonical $SCRIPTDIR/../../../../build/contrib/loggraph` -LIBDIR=`make_canonical $BUILDDIR/lib` -WEBDIR=`make_canonical $SCRIPTDIR/../web` -ZKDIR=`make_canonical $SCRIPTDIR/../../../../build/` - -if [ ! -x $BUILDDIR ]; then - echo "\n\n*** You need to build loggraph before running it ***\n\n"; - exit; -fi - -for i in `ls $LIBDIR`; do - CLASSPATH=$LIBDIR/$i:$CLASSPATH -done - -for i in $ZKDIR/zookeeper-*.jar; do - CLASSPATH="$i:$CLASSPATH" -done - -CLASSPATH=$BUILDDIR/classes:$WEBDIR:$CLASSPATH -echo $CLASSPATH -java -Dlog4j.configuration=org/apache/zookeeper/graph/log4j.properties -Xdebug -Xrunjdwp:transport=dt_socket,address=4444,server=y,suspend=n -cp $CLASSPATH org.apache.zookeeper.graph.LogServer $* diff --git a/src/contrib/loggraph/bin/loggraph.sh b/src/contrib/loggraph/bin/loggraph.sh deleted file mode 100755 index 381e5ad478a..00000000000 --- a/src/contrib/loggraph/bin/loggraph.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -make_canonical () { - cd $1; pwd; -} - -SCRIPTDIR=`dirname $0` -BUILDDIR=`make_canonical $SCRIPTDIR/../../../../build/contrib/loggraph` -LIBDIR=`make_canonical $BUILDDIR/lib` -ZKDIR=`make_canonical $SCRIPTDIR/../../../../build/` - -if [ ! -x $BUILDDIR ]; then - echo "\n\n*** You need to build loggraph before running it ***\n\n"; - exit; -fi - -for i in `ls $LIBDIR`; do - CLASSPATH=$LIBDIR/$i:$CLASSPATH -done - -for i in `ls $BUILDDIR/*.jar`; do - CLASSPATH=$i:$CLASSPATH -done - -for i in $ZKDIR/zookeeper-*.jar; do - CLASSPATH="$i:$CLASSPATH" -done - -java -cp $CLASSPATH org.apache.zookeeper.graph.LogServer $* - - - - diff --git a/src/contrib/loggraph/build.xml b/src/contrib/loggraph/build.xml deleted file mode 100644 index 5be6970b64d..00000000000 --- a/src/contrib/loggraph/build.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/loggraph/ivy.xml b/src/contrib/loggraph/ivy.xml deleted file mode 100644 index 25a506db319..00000000000 --- a/src/contrib/loggraph/ivy.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - ZooKeeper Graphing - - - - - - - - - - - - - - - - diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterException.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterException.java deleted file mode 100644 index c0912fa7dfd..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterException.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -public class FilterException extends Exception { - public FilterException(String s) { super(s); } -}; diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterOp.java deleted file mode 100644 index 0016610aaea..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterOp.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.util.ArrayList; -import org.apache.zookeeper.graph.filterops.*; - -public abstract class FilterOp { - protected ArrayList subOps; - protected ArrayList args; - - public enum ArgType { - STRING, NUMBER, SYMBOL - } - - public FilterOp() { - subOps = new ArrayList(); - args = new ArrayList(); - } - - public static FilterOp newOp(String op) throws FilterException { - if (op.equals("or")) - return new OrOp(); - if (op.equals("and")) - return new AndOp(); - if (op.equals("not")) - return new NotOp(); - if (op.equals("xor")) - return new XorOp(); - if (op.equals("=")) - return new EqualsOp(); - if (op.equals("<")) - return new LessThanOp(); - if (op.equals(">")) - return new GreaterThanOp(); - - throw new FilterException("Invalid operation '"+op+"'"); - } - - public void addSubOp(FilterOp op) { - subOps.add(op); - } - - public void addArg(Arg arg) { - args.add(arg); - } - - public abstract boolean matches(LogEntry entry) throws FilterException; - - public String toString() { - String op = "(" + getClass().getName(); - for (FilterOp f : subOps) { - op += " " + f; - } - for (Arg a : args) { - op += " " + a; - } - return op + ")"; - } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterParser.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterParser.java deleted file mode 100644 index bb2e669e657..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/FilterParser.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.PushbackReader; -import java.io.StringReader; -import java.io.IOException; -import java.util.ArrayList; - -import org.apache.zookeeper.graph.filterops.*; - -public class FilterParser { - private PushbackReader reader; - - public FilterParser(String s) { - reader = new PushbackReader(new StringReader(s)); - } - - private String readUntilSpace() throws IOException { - StringBuffer buffer = new StringBuffer(); - - int c = reader.read(); - while (!Character.isWhitespace(c) && c != ')' && c != '(') { - buffer.append((char)c); - c = reader.read(); - if (c == -1) { - break; - } - } - reader.unread(c); - - return buffer.toString().trim(); - } - - private StringArg readStringArg() throws IOException, FilterException { - int c = reader.read(); - int last = 0; - if (c != '"') { - throw new FilterException("Check the parser, trying to read a string that doesn't begin with quotes"); - } - StringBuffer buffer = new StringBuffer(); - while (reader.ready()) { - last = c; - c = reader.read(); - if (c == -1) { - break; - } - - if (c == '"' && last != '\\') { - return new StringArg(buffer.toString()); - } else { - buffer.append((char)c); - } - } - throw new FilterException("Unterminated string"); - } - - private NumberArg readNumberArg() throws IOException, FilterException { - String strval = readUntilSpace(); - - try { - if (strval.startsWith("0x")) { - return new NumberArg(Long.valueOf(strval.substring(2), 16)); - } else { - return new NumberArg(Long.valueOf(strval)); - } - } catch (NumberFormatException e) { - throw new FilterException("Not a number [" + strval + "]\n" + e); - } - } - - private SymbolArg readSymbolArg() throws IOException, FilterException { - return new SymbolArg(readUntilSpace()); - } - - public FilterOp parse() throws IOException, FilterException { - int c = reader.read(); - if (c != '(') { - throw new FilterException("Invalid format"); - } - - String opstr = readUntilSpace(); - FilterOp op = FilterOp.newOp(opstr); - - while (reader.ready()) { - c = reader.read(); - if (c == -1) { - break; - } - if (c == '(') { - reader.unread(c); - op.addSubOp(parse()); - } else if (c == ')') { - return op; - } else if (c == '"') { - reader.unread(c); - op.addArg(readStringArg()); - } else if (Character.isDigit(c) || c == '-' || c == '+') { - reader.unread(c); - op.addArg(readNumberArg()); - } else if (Character.isJavaIdentifierStart(c)) { - reader.unread(c); - op.addArg(readSymbolArg()); - } - } - throw new FilterException("Incomplete filter"); - } - - public static void main(String[] args) throws IOException, FilterException { - if (args.length == 1) { - System.out.println(new FilterParser(args[0]).parse()); - } else { - System.out.println(new FilterParser("(or (and (= session foobar) (= session barfoo)) (= session sdfs))").parse()); - } - } -}; \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/JsonGenerator.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/JsonGenerator.java deleted file mode 100644 index afaf3a1c9de..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/JsonGenerator.java +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.io.Writer; -import java.io.OutputStreamWriter; -import java.io.IOException; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.ListIterator; - -public class JsonGenerator { - private JSONObject root; - private HashSet servers; - - private class Message { - private int from; - private int to; - private long zxid; - - public Message(int from, int to, long zxid) { - this.from = from; - this.to = to; - this.zxid = zxid; - } - - public boolean equals(Message m) { - return (m.from == this.from - && m.to == this.to - && m.zxid == this.zxid); - } - }; - - public JSONObject txnEntry(TransactionEntry e) { - JSONObject event = new JSONObject(); - - event.put("time", Long.toString(e.getTimestamp())); - event.put("client", Long.toHexString(e.getClientId())); - event.put("cxid", Long.toHexString(e.getCxid())); - event.put("zxid", Long.toHexString(e.getZxid())); - event.put("op", e.getOp()); - event.put("extra", e.getExtra()); - event.put("type", "transaction"); - - return event; - } - - /** - Assumes entries are sorted by timestamp. - */ - public JsonGenerator(LogIterator iter) { - servers = new HashSet(); - - Pattern stateChangeP = Pattern.compile("- (LOOKING|FOLLOWING|LEADING)"); - Pattern newElectionP = Pattern.compile("New election. My id = (\\d+), Proposed zxid = (\\d+)"); - Pattern receivedProposalP = Pattern.compile("Notification: (\\d+) \\(n.leader\\), (\\d+) \\(n.zxid\\), (\\d+) \\(n.round\\), .+ \\(n.state\\), (\\d+) \\(n.sid\\), .+ \\(my state\\)"); - Pattern exceptionP = Pattern.compile("xception"); - - root = new JSONObject(); - Matcher m = null; - JSONArray events = new JSONArray(); - root.put("events", events); - - long starttime = Long.MAX_VALUE; - long endtime = 0; - - int leader = 0; - long curEpoch = 0; - boolean newEpoch = false; - - while (iter.hasNext()) { - LogEntry ent = iter.next(); - - if (ent.getTimestamp() < starttime) { - starttime = ent.getTimestamp(); - } - if (ent.getTimestamp() > endtime) { - endtime = ent.getTimestamp(); - } - - if (ent.getType() == LogEntry.Type.TXN) { - events.add(txnEntry((TransactionEntry)ent)); - } else { - Log4JEntry e = (Log4JEntry)ent; - servers.add(e.getNode()); - - if ((m = stateChangeP.matcher(e.getEntry())).find()) { - JSONObject stateChange = new JSONObject(); - stateChange.put("type", "stateChange"); - stateChange.put("time", e.getTimestamp()); - stateChange.put("server", e.getNode()); - stateChange.put("state", m.group(1)); - events.add(stateChange); - - if (m.group(1).equals("LEADING")) { - leader = e.getNode(); - } - } else if ((m = newElectionP.matcher(e.getEntry())).find()) { - Iterator iterator = servers.iterator(); - long zxid = Long.valueOf(m.group(2)); - int count = (int)zxid;// & 0xFFFFFFFFL; - int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; - - if (leader != 0 && epoch > curEpoch) { - JSONObject stateChange = new JSONObject(); - stateChange.put("type", "stateChange"); - stateChange.put("time", e.getTimestamp()); - stateChange.put("server", leader); - stateChange.put("state", "INIT"); - events.add(stateChange); - leader = 0; - } - - while (iterator.hasNext()) { - int dst = iterator.next(); - if (dst != e.getNode()) { - JSONObject msg = new JSONObject(); - msg.put("type", "postmessage"); - msg.put("src", e.getNode()); - msg.put("dst", dst); - msg.put("time", e.getTimestamp()); - msg.put("zxid", m.group(2)); - msg.put("count", count); - msg.put("epoch", epoch); - - events.add(msg); - } - } - } else if ((m = receivedProposalP.matcher(e.getEntry())).find()) { - // Pattern.compile("Notification: \\d+, (\\d+), (\\d+), \\d+, [^,]*, [^,]*, (\\d+)");//, LOOKING, LOOKING, 2 - int src = Integer.valueOf(m.group(4)); - long zxid = Long.valueOf(m.group(2)); - int dst = e.getNode(); - long epoch2 = Long.valueOf(m.group(3)); - - int count = (int)zxid;// & 0xFFFFFFFFL; - int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; - - if (leader != 0 && epoch > curEpoch) { - JSONObject stateChange = new JSONObject(); - stateChange.put("type", "stateChange"); - stateChange.put("time", e.getTimestamp()); - stateChange.put("server", leader); - stateChange.put("state", "INIT"); - events.add(stateChange); - leader = 0; - } - - if (src != dst) { - JSONObject msg = new JSONObject(); - msg.put("type", "delivermessage"); - msg.put("src", src); - msg.put("dst", dst); - msg.put("time", e.getTimestamp()); - msg.put("zxid", zxid); - msg.put("epoch", epoch); - msg.put("count", count); - msg.put("epoch2", epoch2); - - events.add(msg); - } - } else if ((m = exceptionP.matcher(e.getEntry())).find()) { - JSONObject ex = new JSONObject(); - ex.put("type", "exception"); - ex.put("server", e.getNode()); - ex.put("time", e.getTimestamp()); - ex.put("text", e.getEntry()); - events.add(ex); - } - } - JSONObject ex = new JSONObject(); - ex.put("type", "text"); - ex.put("time", ent.getTimestamp()); - String txt = ent.toString(); - ex.put("text", txt); - events.add(ex); - } - // System.out.println("pending messages: "+pendingMessages.size()); - root.put("starttime", starttime); - root.put("endtime", endtime); - - JSONArray serversarray = new JSONArray(); - root.put("servers", serversarray); - - Iterator iterator = servers.iterator(); - while (iterator.hasNext()) { - serversarray.add(iterator.next()); - } - } - - public String toString() { - return JSONValue.toJSONString(root); - } - - public static void main(String[] args) throws Exception { - MergedLogSource src = new MergedLogSource(args); - LogIterator iter = src.iterator(); - System.out.println(new JsonGenerator(iter)); - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JEntry.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JEntry.java deleted file mode 100644 index 4f49dca251f..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JEntry.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -public class Log4JEntry extends LogEntry { - public Log4JEntry(long timestamp, int node, String entry) { - super(timestamp); - setAttribute("log-text", entry); - setAttribute("node", new Integer(node)); - } - - public String getEntry() { - return (String) getAttribute("log-text"); - } - - public String toString() { - return "" + getTimestamp() + "::::" + getNode() + "::::" + getEntry(); - } - - public int getNode() { - return (Integer) getAttribute("node"); - } - - public Type getType() { return LogEntry.Type.LOG4J; } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java deleted file mode 100644 index 4a61f980d33..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/Log4JSource.java +++ /dev/null @@ -1,380 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.File; -import java.io.InputStreamReader; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.ArrayList; -import java.util.Date; -import java.text.SimpleDateFormat; -import java.text.ParseException; -import java.util.Calendar; -import java.util.GregorianCalendar; - -import java.io.EOFException; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.apache.log4j.Logger; - -public class Log4JSource implements LogSource { - private static final Logger LOG = Logger.getLogger(Log4JSource.class); - - private static final int skipN = 10000; - private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss,SSS"; - - private LogSkipList skiplist = null; - - private String file = null; - private long starttime = 0; - private long endtime = 0; - private int serverid = 0; - private long size = 0; - - private Pattern timep; - - public boolean overlapsRange(long starttime, long endtime) { - return (starttime <= this.endtime && endtime >= this.starttime); - } - - public long size() { return size; } - public long getStartTime() { return starttime; } - public long getEndTime() { return endtime; } - public LogSkipList getSkipList() { return skiplist; } - - private class Log4JSourceIterator implements LogIterator { - private RandomAccessFileReader in; - private LogEntry next = null; - private long starttime = 0; - private long endtime = 0; - private String buf = ""; - private Log4JSource src = null; - private long skippedAtStart = 0; - private SimpleDateFormat dateformat = null; - private FilterOp filter = null; - - public Log4JSourceIterator(Log4JSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException { - this(src, starttime, endtime, null); - } - - public Log4JSourceIterator(Log4JSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - - this.dateformat = new SimpleDateFormat(DATE_FORMAT); - this.src = src; - this.starttime = starttime; - this.endtime = endtime; - - File f = new File(src.file); - try { - in = new RandomAccessFileReader(f); - } catch (FileNotFoundException e) { - throw new IllegalArgumentException("Bad file passed in (" + src.file +") cannot open:" + e); - } - - // skip to the offset of latest skip point before starttime - LogSkipList.Mark start = src.getSkipList().findMarkBefore(starttime); - try { - in.seek(start.getBytes()); - skippedAtStart = start.getEntriesSkipped(); - } catch (IOException ioe) { - // if we can't skip, we should just read from the start - } - - LogEntry e; - while ((e = readNextEntry()) != null && e.getTimestamp() < endtime) { - if (e.getTimestamp() >= starttime && (filter == null || filter.matches(e))) { - next = e; - return; - } - skippedAtStart++; - } - this.filter = filter; - } - - synchronized public long size() throws IOException { - if (LOG.isTraceEnabled()) { - LOG.trace("size() called"); - } - - if (this.endtime >= src.getEndTime()) { - return src.size() - skippedAtStart; - } - - long pos = in.getPosition(); - - if (LOG.isTraceEnabled()) { - LOG.trace("saved pos () = " + pos); - } - - LogEntry e; - - LogSkipList.Mark lastseg = src.getSkipList().findMarkBefore(this.endtime); - in.seek(lastseg.getBytes()); - buf = ""; // clear the buf so we don't get something we read before we sought - // number of entries skipped to get to the end of the iterator, less the number skipped to get to the start - long count = lastseg.getEntriesSkipped() - skippedAtStart; - - while ((e = readNextEntry()) != null) { - if (LOG.isTraceEnabled()) { - //LOG.trace(e); - } - if (e.getTimestamp() > this.endtime) { - break; - } - count++; - } - in.seek(pos); - buf = ""; - - if (LOG.isTraceEnabled()) { - LOG.trace("size() = " + count); - } - - return count; - } - - synchronized private LogEntry readNextEntry() { - try { - try { - while (true) { - String line = in.readLine(); - if (line == null) { - break; - } - - Matcher m = src.timep.matcher(line); - if (m.lookingAt()) { - if (buf.length() > 0) { - LogEntry e = new Log4JEntry(src.timestampFromText(dateformat, buf), src.getServerId(), buf); - buf = line; - return e; - } - buf = line; - } else if (buf.length() > 0) { - buf += line + "\n"; - } - } - } catch (EOFException eof) { - // ignore, we've simply come to the end of the file - } - if (buf.length() > 0) { - LogEntry e = new Log4JEntry(src.timestampFromText(dateformat, buf), src.getServerId(), buf); - buf = ""; - return e; - } - } catch (Exception e) { - LOG.error("Error reading next entry in file (" + src.file + "): " + e); - return null; - } - return null; - } - - public boolean hasNext() { - return next != null; - } - - public LogEntry next() throws NoSuchElementException { - LogEntry ret = next; - LogEntry e = readNextEntry(); - - if (filter != null) { - try { - while (e != null && !filter.matches(e)) { - e = readNextEntry(); - } - } catch (FilterException fe) { - throw new NoSuchElementException(e.toString()); - } - } - - if (e != null && e.getTimestamp() < endtime) { - next = e; - } else { - next = null; - } - return ret; - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("remove not supported for L4J logs"); - } - - public void close() throws IOException { - in.close(); - } - - public String toString() { - String size; - try { - size = new Long(size()).toString(); - } catch (IOException ioe) { - size = "Unable to read"; - } - return "Log4JSourceIterator(start=" + starttime + ", end=" + endtime + ", size=" + size + ")"; - } - } - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { - try { - return iterator(starttime, endtime, null); - } catch (FilterException fe) { - assert(false); //"This should never happen, you can't have a filter exception without a filter"); - return null; - } - } - - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException{ - // sanitise start and end times - if (endtime < starttime) { - throw new IllegalArgumentException("End time (" + endtime + ") must be greater or equal to starttime (" + starttime + ")"); - } - - return new Log4JSourceIterator(this, starttime, endtime, filter); - } - - public LogIterator iterator() throws IllegalArgumentException { - return iterator(starttime, endtime+1); - } - - public Log4JSource(String file) throws IOException { - this.file=file; - - timep = Pattern.compile("^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3})"); - skiplist = new LogSkipList(); - init(); - } - - private static long timestampFromText(SimpleDateFormat format, String s) { - Date d = null; - try { - d = format.parse(s); - } catch (ParseException e) { - return 0; - } - Calendar c = new GregorianCalendar(); - c.setTime(d); - return c.getTimeInMillis(); - } - - private void init() throws IOException { - File f = new File(file); - RandomAccessFileReader in = new RandomAccessFileReader(f); - SimpleDateFormat dateformat = new SimpleDateFormat(DATE_FORMAT); - Pattern idp = Pattern.compile("\\[myid:(\\d+)\\]"); - - long lastFp = in.getPosition(); - String line = in.readLine(); - Matcher m = null; - - // if we have read data from the file, and it matchs the timep pattern - if ((line != null) && (m = timep.matcher(line)).lookingAt()) { - starttime = timestampFromText(dateformat, m.group(1)); - } else { - throw new IOException("Invalid log4j format. First line doesn't start with time"); - } - - /* - Count number of log entries. Any line starting with a timestamp counts as an entry - */ - String lastentry = line; - try { - while (line != null) { - m = timep.matcher(line); - if (m.lookingAt()) { - if (size % skipN == 0) { - long time = timestampFromText(dateformat, m.group(1)); - skiplist.addMark(time, lastFp, size); - } - size++; - lastentry = line; - } - if (serverid == 0 && (m = idp.matcher(line)).find()) { - serverid = Integer.valueOf(m.group(1)); - } - - lastFp = in.getPosition(); - line = in.readLine(); - } - } catch (EOFException eof) { - // ignore, simply end of file, though really (line!=null) should have caught this - } finally { - in.close(); - } - - m = timep.matcher(lastentry); - if (m.lookingAt()) { - endtime = timestampFromText(dateformat, m.group(1)); - } else { - throw new IOException("Invalid log4j format. Last line doesn't start with time"); - } - } - - public String toString() { - return "Log4JSource(file=" + file + ", size=" + size + ", start=" + starttime + ", end=" + endtime +", id=" + serverid +")"; - } - - public static void main(String[] args) throws IOException { - final Log4JSource s = new Log4JSource(args[0]); - System.out.println(s); - - LogIterator iter; - - if (args.length == 3) { - final long starttime = Long.valueOf(args[1]); - final long endtime = Long.valueOf(args[2]); - iter = s.iterator(starttime, endtime); - - Thread t1 = new Thread() { public void run () { - - LogIterator iter = s.iterator(starttime, endtime); - System.out.println(iter); - }; }; - Thread t2 = new Thread() { public void run () { - - LogIterator iter = s.iterator(starttime, endtime); - System.out.println(iter); - }; }; - Thread t3 = new Thread() { public void run () { - - LogIterator iter = s.iterator(starttime, endtime); - System.out.println(iter); - }; }; - t1.start(); - t2.start(); - // t3.start(); - } else { - iter = s.iterator(); - } - - /*while (iter.hasNext()) { - System.out.println(iter.next()); - }*/ - iter.close(); - } - - public int getServerId() { - return serverid; - } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogEntry.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogEntry.java deleted file mode 100644 index a8252ebb949..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogEntry.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.Serializable; -import java.util.HashMap; - -public abstract class LogEntry implements Serializable { - private HashMap attributes; - - public enum Type { UNKNOWN, LOG4J, TXN }; - - public LogEntry(long timestamp) { - attributes = new HashMap(); - setAttribute("timestamp", new Long(timestamp)); - } - - public long getTimestamp() { - return (Long)getAttribute("timestamp"); - } - - public abstract Type getType(); - - public void setAttribute(String key, Object v) { - attributes.put(key, v); - } - - public Object getAttribute(String key) { - return attributes.get(key); - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogIterator.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogIterator.java deleted file mode 100644 index 9fc34cb4e33..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogIterator.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.Closeable; -import java.util.Iterator; -import java.io.IOException; - -public interface LogIterator extends Iterator, Closeable { - long size() throws IOException;; -}; \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogServer.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogServer.java deleted file mode 100644 index e87dad2f2f9..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogServer.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.ServletException; - -import java.io.IOException; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import org.apache.zookeeper.graph.servlets.*; - -public class LogServer extends ServletContextHandler { - public LogServer(MergedLogSource src) throws Exception { - super(ServletContextHandler.SESSIONS); - setContextPath("/"); - - addServlet(new ServletHolder(new StaticContent()),"/graph/*"); - - addServlet(new ServletHolder(new Fs()),"/fs"); - addServlet(new ServletHolder(new GraphData(src)), "/data"); - addServlet(new ServletHolder(new FileLoader(src)), "/loadfile"); - addServlet(new ServletHolder(new NumEvents(src)), "/info"); - addServlet(new ServletHolder(new Throughput(src)), "/throughput"); - } - - public static void main(String[] args) { - try { - MergedLogSource src = new MergedLogSource(args); - System.out.println(src); - - Server server = new Server(8182); - server.setHandler(new LogServer(src)); - - server.start(); - server.join(); - - } catch (Exception e) { - // Something is wrong. - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSkipList.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSkipList.java deleted file mode 100644 index a6c836a1ae0..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSkipList.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.util.List; -import java.util.LinkedList; -import java.util.NoSuchElementException; - -import org.apache.log4j.Logger; - -/** -Generic skip list for holding a rough index of a log file. When the log file is loaded, this -index is built by adding a mark every n entries. Then when a specific time position is requested -from the file, a point at most n-1 entries before the time position can be jumped to. - -*/ -public class LogSkipList { - private static final Logger LOG = Logger.getLogger(LogSkipList.class); - - private LinkedList marks; - - public class Mark { - private long time; - private long bytes; - private long skipped; - - public Mark(long time, long bytes, long skipped) { - this.time = time; - this.bytes = bytes; - this.skipped = skipped; - } - - public long getTime() { return this.time; } - public long getBytes() { return this.bytes; } - public long getEntriesSkipped() { return this.skipped; } - - public String toString() { - return "Mark(time=" + time + ", bytes=" + bytes + ", skipped=" + skipped + ")"; - } - }; - - public LogSkipList() { - if (LOG.isTraceEnabled()) { - LOG.trace("New skip list"); - } - marks = new LinkedList(); - } - - public void addMark(long time, long bytes, long skipped) { - if (LOG.isTraceEnabled()) { - LOG.trace("addMark (time:" + time + ", bytes: " + bytes + ", skipped: " + skipped + ")"); - } - marks.add(new Mark(time, bytes, skipped)); - } - - /** - Find the last mark in the skip list before time. - */ - public Mark findMarkBefore(long time) throws NoSuchElementException { - if (LOG.isTraceEnabled()) { - LOG.trace("findMarkBefore(" + time + ")"); - } - - Mark last = marks.getFirst(); - for (Mark m: marks) { - if (m.getTime() > time) { - break; - } - last = m; - } - - if (LOG.isTraceEnabled()) { - LOG.trace("return " + last ); - } - - return last; - } - -}; \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSource.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSource.java deleted file mode 100644 index 9845c7f0b32..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/LogSource.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; -import java.util.Iterator; - -public interface LogSource extends Iterable { - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException; - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException; - - public LogIterator iterator() throws IllegalArgumentException; - - public boolean overlapsRange(long starttime, long endtime); - - public long size(); - public long getStartTime(); - public long getEndTime(); -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MeasureThroughput.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MeasureThroughput.java deleted file mode 100644 index 072abdb8f8a..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MeasureThroughput.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.IOException; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.DataOutputStream; -import java.io.PrintStream; - -import java.util.HashSet; - -public class MeasureThroughput { - private static final int MS_PER_SEC = 1000; - private static final int MS_PER_MIN = MS_PER_SEC*60; - private static final int MS_PER_HOUR = MS_PER_MIN*60; - - public static void main(String[] args) throws IOException { - MergedLogSource source = new MergedLogSource(args); - - PrintStream ps_ms = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-ms.out"))); - PrintStream ps_sec = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-sec.out"))); - PrintStream ps_min = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-min.out"))); - PrintStream ps_hour = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-hour.out"))); - LogIterator iter; - - System.out.println(source); - iter = source.iterator(); - long currentms = 0; - long currentsec = 0; - long currentmin = 0; - long currenthour = 0; - HashSet zxids_ms = new HashSet(); - long zxid_sec = 0; - long zxid_min = 0; - long zxid_hour = 0; - - while (iter.hasNext()) { - LogEntry e = iter.next(); - TransactionEntry cxn = (TransactionEntry)e; - - long ms = cxn.getTimestamp(); - long sec = ms/MS_PER_SEC; - long min = ms/MS_PER_MIN; - long hour = ms/MS_PER_HOUR; - - if (currentms != ms && currentms != 0) { - ps_ms.println("" + currentms + " " + zxids_ms.size()); - - zxid_sec += zxids_ms.size(); - zxid_min += zxids_ms.size(); - zxid_hour += zxids_ms.size(); - zxids_ms.clear(); - } - - if (currentsec != sec && currentsec != 0) { - ps_sec.println("" + currentsec*MS_PER_SEC + " " + zxid_sec); - - zxid_sec = 0; - } - - if (currentmin != min && currentmin != 0) { - ps_min.println("" + currentmin*MS_PER_MIN + " " + zxid_min); - - zxid_min = 0; - } - - if (currenthour != hour && currenthour != 0) { - ps_hour.println("" + currenthour*MS_PER_HOUR + " " + zxid_hour); - - zxid_hour = 0; - } - - currentms = ms; - currentsec = sec; - currentmin = min; - currenthour = hour; - - zxids_ms.add(cxn.getZxid()); - } - - iter.close(); - ps_ms.close(); - ps_sec.close(); - ps_min.close(); - ps_hour.close(); - } -}; \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MergedLogSource.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MergedLogSource.java deleted file mode 100644 index 3b7c4667020..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/MergedLogSource.java +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.IOException; -import java.text.DateFormat; -import java.util.Date; -import java.util.zip.Adler32; -import java.util.zip.Checksum; -import java.util.HashMap; - -import org.apache.jute.BinaryInputArchive; -import org.apache.jute.InputArchive; -import org.apache.jute.Record; -import org.apache.zookeeper.server.TraceFormatter; -import org.apache.zookeeper.server.persistence.FileHeader; -import org.apache.zookeeper.server.persistence.FileTxnLog; -import org.apache.zookeeper.server.util.SerializeUtils; -import org.apache.zookeeper.txn.TxnHeader; - -import org.apache.zookeeper.ZooDefs.OpCode; - -import org.apache.zookeeper.txn.CreateSessionTxn; -import org.apache.zookeeper.txn.CreateTxn; -import org.apache.zookeeper.txn.DeleteTxn; -import org.apache.zookeeper.txn.ErrorTxn; -import org.apache.zookeeper.txn.SetACLTxn; -import org.apache.zookeeper.txn.SetDataTxn; -import org.apache.zookeeper.txn.TxnHeader; - -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.util.Vector; -import java.util.Iterator; -import java.util.Collections; -import java.util.NoSuchElementException; - -import org.apache.log4j.Logger; - -public class MergedLogSource implements LogSource { - private static final Logger LOG = Logger.getLogger(MergedLogSource.class); - private Vector sources = null; - private long starttime = 0; - private long endtime = 0; - private long size = 0; - - public boolean overlapsRange(long starttime, long endtime) { - return (starttime <= this.endtime && endtime >= this.starttime); - } - - public long size() { return size; } - public long getStartTime() { return starttime; } - public long getEndTime() { return endtime; } - - private class MergedLogSourceIterator implements LogIterator { - private LogEntry next = null; - private long start = 0; - private long end = 0; - private MergedLogSource src = null; - private LogIterator[] sources = null; - private LogEntry[] nexts = null; - private FilterOp filter = null; - - public MergedLogSourceIterator(MergedLogSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - Vector iters = new Vector(); - for (LogSource s : src.sources) { - if (s.overlapsRange(starttime, endtime)) { - iters.add(s.iterator(starttime, endtime, filter)); - } - } - - sources = new LogIterator[iters.size()]; - sources = iters.toArray(sources); - nexts = new LogEntry[iters.size()]; - for (int i = 0; i < sources.length; i++) { - if (sources[i].hasNext()) - nexts[i] = sources[i].next(); - } - this.filter = filter; - } - - public MergedLogSourceIterator(MergedLogSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException { - this(src, starttime, endtime, null); - } - - public long size() throws IOException { - long size = 0; - for (LogIterator i : sources) { - size += i.size(); - } - return size; - } - - public boolean hasNext() { - for (LogEntry n : nexts) { - if (n != null) return true; - } - return false; - } - - public LogEntry next() { - int min = -1; - for (int i = 0; i < nexts.length; i++) { - if (nexts[i] != null) { - if (min == -1) { - min = i; - } else if (nexts[i].getTimestamp() < nexts[min].getTimestamp()) { - min = i; - } - } - } - if (min == -1) { - return null; - } else { - LogEntry e = nexts[min]; - nexts[min] = sources[min].next(); - return e; - } - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("remove not supported for Merged logs"); - } - - public void close() throws IOException { - for (LogIterator i : sources) { - i.close(); - } - } - } - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { - try { - return iterator(starttime, endtime, null); - } catch (FilterException fe) { - assert(false); // shouldn't happen without filter - return null; - } - } - - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - // sanitise start and end times - if (endtime < starttime) { - throw new IllegalArgumentException("End time (" + endtime + ") must be greater or equal to starttime (" + starttime + ")"); - } - - return new MergedLogSourceIterator(this, starttime, endtime, filter); - } - - public LogIterator iterator() throws IllegalArgumentException { - return iterator(starttime, endtime+1); - } - - public MergedLogSource(String[] files) throws IOException { - sources = new Vector(); - for (String f : files) { - addSource(f); - } - } - - public void addSource(String f) throws IOException { - LogSource s = null; - if (TxnLogSource.isTransactionFile(f)) { - s = new TxnLogSource(f); - } else { - s = new Log4JSource(f); - } - - size += s.size(); - endtime = s.getEndTime() > endtime ? s.getEndTime() : endtime; - starttime = s.getStartTime() < starttime || starttime == 0 ? s.getStartTime() : starttime; - sources.add(s); - } - - public String toString() { - String s = "MergedLogSource(size=" + size + ", start=" + starttime + ", end=" + endtime +")"; - for (LogSource src : sources) { - s += "\n\t- " +src; - } - return s; - } - - public static void main(String[] args) throws IOException { - System.out.println("Time: " + System.currentTimeMillis()); - MergedLogSource s = new MergedLogSource(args); - System.out.println(s); - - LogIterator iter; - - iter = s.iterator(); - System.out.println("Time: " + System.currentTimeMillis()); - System.out.println("Iterator Size: " + iter.size()); - System.out.println("Time: " + System.currentTimeMillis()); - /* while (iter.hasNext()) { - System.out.println(iter.next()); - }*/ - iter.close(); - System.out.println("Time: " + System.currentTimeMillis()); - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java deleted file mode 100644 index e075350e520..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/RandomAccessFileReader.java +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.File; -import java.io.Reader; -import java.io.IOException; -import java.io.EOFException; -import java.io.RandomAccessFile; -import java.io.FileNotFoundException; - -import java.io.DataInputStream; -import java.io.ByteArrayInputStream; -import java.io.DataInput; - -import org.apache.log4j.Logger; - -public class RandomAccessFileReader extends Reader implements DataInput { - private static final Logger LOG = Logger.getLogger(RandomAccessFileReader.class); - private RandomAccessFile file; - private byte[] buffer; - private int buffersize; - private int bufferoffset; - private long fileoffset; - private long fp; - - private static final int DEFAULT_BUFFER_SIZE = 512*1024; // 512k - private int point = 0; - - public RandomAccessFileReader(File f) throws FileNotFoundException { - file = new RandomAccessFile(f, "r"); - if (LOG.isDebugEnabled()) { - try { - LOG.debug("Opened file(" + f + ") with FD (" + file.getFD() + ")"); - } catch (IOException ioe) { - LOG.debug("Opened file(" + f + ") coulds get FD"); - } - } - - buffer = new byte[DEFAULT_BUFFER_SIZE]; - buffersize = 0; - bufferoffset = 0; - fileoffset = 0; - fp = 0; - } - - /** - fill the buffer from the file. - fp keeps track of the file pointer. - fileoffset is the offset into the file to where the buffer came from. - */ - private int fill() throws IOException { - fileoffset = fp; - int read = file.read(buffer, 0, buffer.length); - - if (LOG.isDebugEnabled()) { - String buf = new String(buffer, 0, 40, "UTF-8"); - LOG.debug("fill(buffer=" + buf + ")"); - } - - if (read == -1) { // eof reached - buffersize = 0; - } else { - buffersize = read; - } - fp += buffersize; - bufferoffset = 0; - - return buffersize; - } - - /** - * Reader interface - */ - public boolean markSupported() { return false; } - - /** - copy what we can from buffer. if it's not enough, fill buffer again and copy again - */ - synchronized public int read(char[] cbuf, int off, int len) throws IOException { - // This could be faster, but probably wont be used - byte[] b = new byte[2]; - int bytesread = 0; - while (len > 0) { - int read = read(b, 0, 2); - bytesread += read; - if (read < 2) { - return bytesread; - } - cbuf[off] = (char)((b[0] << 8) | (b[1] & 0xff)); - off += read; - len -= read; - } - - return bytesread; - } - - synchronized public int read(byte[] buf, int off, int len) throws IOException { - if (LOG.isTraceEnabled()) { - LOG.trace("read(buf, off=" + off + ", len=" + len); - } - - int read = 0; - while (len > 0) { - if (buffersize == 0) { - fill(); - if (buffersize == 0) { - break; - } - } - - int tocopy = Math.min(len, buffersize); - if (LOG.isTraceEnabled()) { - LOG.trace("tocopy=" + tocopy); - } - - System.arraycopy(buffer, bufferoffset, buf, off, tocopy); - buffersize -= tocopy; - bufferoffset += tocopy; - - len -= tocopy; - read += tocopy; - off += tocopy; - } - if (LOG.isTraceEnabled()) { - LOG.trace("read=" + read); - } - - return read; - } - - public void close() throws IOException { - file.close(); - } - - /** - * Seek interface - */ - public long getPosition() { - return bufferoffset + fileoffset; - } - - synchronized public void seek(long pos) throws IOException { - if (LOG.isDebugEnabled()) { - LOG.debug("seek(" + pos + ")"); - } - file.seek(pos); - fp = pos; - buffersize = 0; // force a buffer fill on next read - } - - /** - works like the usual readLine but disregards \r to make things easier - */ - synchronized public String readLine() throws IOException { - StringBuffer s = null; - - // go through buffer until i find a \n, if i reach end of buffer first, put whats in buffer into string buffer, - // repeat - buffering: - for (;;) { - if (buffersize == 0) { - fill(); - if (buffersize == 0) { - break; - } - } - - for (int i = 0; i < buffersize; i++) { - if (buffer[bufferoffset + i] == '\n') { - if (i > 0) { // if \n is first char in buffer, leave the string buffer empty - if (s == null) { s = new StringBuffer(); } - s.append(new String(buffer, bufferoffset, i, "UTF-8")); - } - bufferoffset += i+1; - buffersize -= i+1; - break buffering; - } - } - - // We didn't find \n, read the whole buffer into string buffer - if (s == null) { s = new StringBuffer(); } - s.append(new String(buffer, bufferoffset, buffersize, "UTF-8")); - buffersize = 0; - } - - if (s == null) { - return null; - } else { - return s.toString(); - } - } - - /** - DataInput interface - */ - public void readFully(byte[] b) throws IOException { - readFully(b, 0, b.length); - } - - public void readFully(byte[] b, int off, int len) throws IOException - { - while (len > 0) { - int read = read(b, off, len); - len -= read; - off += read; - - if (read == 0) { - throw new EOFException("End of file reached"); - } - } - } - - public int skipBytes(int n) throws IOException { - seek(getPosition() + n); - return n; - } - - public boolean readBoolean() throws IOException { - return (readByte() != 0); - } - - public byte readByte() throws IOException { - byte[] b = new byte[1]; - readFully(b, 0, 1); - return b[0]; - } - - public int readUnsignedByte() throws IOException { - return (int)readByte(); - } - - public short readShort() throws IOException { - byte[] b = new byte[2]; - readFully(b, 0, 2); - return (short)((b[0] << 8) | (b[1] & 0xff)); - } - - public int readUnsignedShort() throws IOException { - byte[] b = new byte[2]; - readFully(b, 0, 2); - return (((b[0] & 0xff) << 8) | (b[1] & 0xff)); - } - - public char readChar() throws IOException { - return (char)readShort(); - } - - public int readInt() throws IOException { - byte[] b = new byte[4]; - readFully(b, 0, 4); - return (((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) | ((b[2] & 0xff) << 8) | (b[3] & 0xff)); - } - - public long readLong() throws IOException { - byte[] b = new byte[8]; - readFully(b, 0, 8); - - return (((long)(b[0] & 0xff) << 56) | ((long)(b[1] & 0xff) << 48) | - ((long)(b[2] & 0xff) << 40) | ((long)(b[3] & 0xff) << 32) | - ((long)(b[4] & 0xff) << 24) | ((long)(b[5] & 0xff) << 16) | - ((long)(b[6] & 0xff) << 8) | ((long)(b[7] & 0xff))); - } - - public float readFloat() throws IOException { - return Float.intBitsToFloat(readInt()); - } - - public double readDouble() throws IOException { - return Double.longBitsToDouble(readLong()); - } - - public String readUTF() throws IOException { - int len = readUnsignedShort(); - byte[] bytes = new byte[len+2]; - bytes[0] = (byte)((len >> 8) & 0xFF); - bytes[1] = (byte)(len & 0xFF); - readFully(bytes, 2, len); - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); - return dis.readUTF(); - } - - public static void main(String[] args) throws IOException { - RandomAccessFileReader f = new RandomAccessFileReader(new File(args[0])); - - long pos0 = f.getPosition(); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - System.out.println("============="); - long pos1 = f.getPosition(); - System.out.println("pos: " + pos1); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - System.out.println("============="); - f.seek(pos1); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - System.out.println("============="); - f.seek(pos0); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - long pos2 = f.getPosition(); - System.out.println("============="); - System.out.println(f.readLine()); - f.seek(pos2); - System.out.println(f.readLine()); - } -}; \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TransactionEntry.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TransactionEntry.java deleted file mode 100644 index 0ca4580aa69..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TransactionEntry.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -public class TransactionEntry extends LogEntry { - public TransactionEntry(long timestamp, long clientId, long Cxid, long Zxid, String op) { - this(timestamp, clientId, Cxid, Zxid, op, ""); - } - - public TransactionEntry(long timestamp, long clientId, long Cxid, long Zxid, String op, String extra) { - super(timestamp); - setAttribute("client-id", new Long(clientId)); - setAttribute("cxid", new Long(Cxid)); - setAttribute("zxid", new Long(Zxid)); - setAttribute("operation", op); - setAttribute("extra", extra); - } - - public long getClientId() { - return (Long)getAttribute("client-id"); - } - - public long getCxid() { - return (Long)getAttribute("cxid"); - } - - public long getZxid() { - return (Long)getAttribute("zxid"); - } - - public String getOp() { - return (String)getAttribute("operation"); - } - - public String getExtra() { - return (String)getAttribute("extra"); - } - - public String toString() { - return getTimestamp() + ":::session(0x" + Long.toHexString(getClientId()) + ") cxid(0x" + Long.toHexString(getCxid()) + ") zxid(0x" + Long.toHexString(getZxid()) + ") op(" + getOp() + ") extra(" + getExtra() +")"; - } - - public Type getType() { return LogEntry.Type.TXN; } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java deleted file mode 100644 index 2b2d9a8efe7..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java +++ /dev/null @@ -1,377 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.IOException; -import java.text.DateFormat; -import java.util.Date; -import java.util.zip.Adler32; -import java.util.zip.Checksum; -import java.util.HashMap; - -import org.apache.jute.BinaryInputArchive; -import org.apache.jute.InputArchive; -import org.apache.jute.Record; -import org.apache.zookeeper.server.TraceFormatter; -import org.apache.zookeeper.server.persistence.FileHeader; -import org.apache.zookeeper.server.persistence.FileTxnLog; -import org.apache.zookeeper.server.util.SerializeUtils; -import org.apache.zookeeper.txn.TxnHeader; - -import org.apache.zookeeper.ZooDefs.OpCode; - -import org.apache.zookeeper.txn.CreateSessionTxn; -import org.apache.zookeeper.txn.CreateTxn; -import org.apache.zookeeper.txn.DeleteTxn; -import org.apache.zookeeper.txn.ErrorTxn; -import org.apache.zookeeper.txn.SetACLTxn; -import org.apache.zookeeper.txn.SetDataTxn; -import org.apache.zookeeper.txn.TxnHeader; - -import java.io.File; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.apache.log4j.Logger; - -public class TxnLogSource implements LogSource { - private static final Logger LOG = Logger.getLogger(TxnLogSource.class); - - private LogSkipList skiplist = null; - private static final int skipN = 10000; - - private String file = null; - private long starttime = 0; - private long endtime = 0; - private long size = 0; - - public boolean overlapsRange(long starttime, long endtime) { - return (starttime <= this.endtime && endtime >= this.starttime); - } - - public long size() { return size; } - public long getStartTime() { return starttime; } - public long getEndTime() { return endtime; } - public LogSkipList getSkipList() { return skiplist; } - - public static boolean isTransactionFile(String file) throws IOException { - RandomAccessFileReader reader = new RandomAccessFileReader(new File(file)); - BinaryInputArchive logStream = new BinaryInputArchive(reader); - FileHeader fhdr = new FileHeader(); - fhdr.deserialize(logStream, "fileheader"); - reader.close(); - - return fhdr.getMagic() == FileTxnLog.TXNLOG_MAGIC; - } - - private class TxnLogSourceIterator implements LogIterator { - private LogEntry next = null; - private long starttime = 0; - private long endtime = 0; - private TxnLogSource src = null; - private RandomAccessFileReader reader = null; - private BinaryInputArchive logStream = null; - private long skippedAtStart = 0; - private FilterOp filter = null; - - public TxnLogSourceIterator(TxnLogSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException { - this(src,starttime,endtime,null); - } - - public TxnLogSourceIterator(TxnLogSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - try { - this.src = src; - this.starttime = starttime; - this.endtime = endtime; - reader = new RandomAccessFileReader(new File(src.file)); - logStream = new BinaryInputArchive(reader); - FileHeader fhdr = new FileHeader(); - fhdr.deserialize(logStream, "fileheader"); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot open transaction log ("+src.file+") :" + e); - } - - LogSkipList.Mark start = src.getSkipList().findMarkBefore(starttime); - try { - reader.seek(start.getBytes()); - skippedAtStart = start.getEntriesSkipped(); - } catch (IOException ioe) { - // if we can't skip, we should just read from the start - } - - this.filter = filter; - - LogEntry e; - while ((e = readNextEntry()) != null && e.getTimestamp() < endtime) { - if (e.getTimestamp() >= starttime && (filter == null || filter.matches(e)) ) { - next = e; - return; - } - skippedAtStart++; - } - - - } - - public long size() throws IOException { - if (this.endtime >= src.getEndTime()) { - return src.size() - skippedAtStart; - } - - long pos = reader.getPosition(); - LogEntry e; - - LogSkipList.Mark lastseg = src.getSkipList().findMarkBefore(this.endtime); - reader.seek(lastseg.getBytes()); - // number of entries skipped to get to the end of the iterator, less the number skipped to get to the start - long count = lastseg.getEntriesSkipped() - skippedAtStart; - - while ((e = readNextEntry()) != null) { - if (e.getTimestamp() > this.endtime) { - break; - } - count++; - } - reader.seek(pos);; - - return count; - } - - private LogEntry readNextEntry() { - LogEntry e = null; - try { - long crcValue; - byte[] bytes; - try { - crcValue = logStream.readLong("crcvalue"); - - bytes = logStream.readBuffer("txnEntry"); - } catch (EOFException ex) { - return null; - } - - if (bytes.length == 0) { - return null; - } - Checksum crc = new Adler32(); - crc.update(bytes, 0, bytes.length); - if (crcValue != crc.getValue()) { - throw new IOException("CRC doesn't match " + crcValue + - " vs " + crc.getValue()); - } - InputArchive iab = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes)); - TxnHeader hdr = new TxnHeader(); - Record r = SerializeUtils.deserializeTxn(iab, hdr); - - switch (hdr.getType()) { - case OpCode.createSession: { - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "createSession"); - } - break; - case OpCode.closeSession: { - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "closeSession"); - } - break; - case OpCode.create: - if (r != null) { - CreateTxn create = (CreateTxn)r; - String path = create.getPath(); - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "create", path); - } - break; - case OpCode.setData: - if (r != null) { - SetDataTxn set = (SetDataTxn)r; - String path = set.getPath(); - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "setData", path); - } - break; - case OpCode.setACL: - if (r != null) { - SetACLTxn setacl = (SetACLTxn)r; - String path = setacl.getPath(); - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "setACL", path); - } - break; - case OpCode.error: - if (r != null) { - ErrorTxn error = (ErrorTxn)r; - - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "error", "Error: " + error.getErr()); - } - break; - default: - LOG.info("Unknown op: " + hdr.getType()); - break; - } - - if (logStream.readByte("EOR") != 'B') { - throw new EOFException("Last transaction was partial."); - } - } catch (Exception ex) { - LOG.error("Error reading transaction from (" + src.file + ") :" + e); - return null; - } - return e; - } - - public boolean hasNext() { - return next != null; - } - - public LogEntry next() throws NoSuchElementException { - LogEntry ret = next; - LogEntry e = readNextEntry(); - - if (filter != null) { - try { - while (e != null && !filter.matches(e)) { - e = readNextEntry(); - } - } catch (FilterException fe) { - throw new NoSuchElementException(fe.toString()); - } - } - if (e != null && e.getTimestamp() < endtime) { - next = e; - } else { - next = null; - } - return ret; - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("remove not supported for Txn logs"); - } - - public void close() throws IOException { - reader.close(); - } - } - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { - try { - return iterator(starttime, endtime, null); - } catch (FilterException fe) { - assert(false); // should never ever happen - return null; - } - } - - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - // sanitise start and end times - if (endtime < starttime) { - throw new IllegalArgumentException("End time (" + endtime + ") must be greater or equal to starttime (" + starttime + ")"); - } - - return new TxnLogSourceIterator(this, starttime, endtime, filter); - } - - public LogIterator iterator() throws IllegalArgumentException { - return iterator(starttime, endtime+1); - } - - public TxnLogSource(String file) throws IOException { - this.file = file; - - skiplist = new LogSkipList(); - - RandomAccessFileReader reader = new RandomAccessFileReader(new File(file)); - try { - BinaryInputArchive logStream = new BinaryInputArchive(reader); - FileHeader fhdr = new FileHeader(); - fhdr.deserialize(logStream, "fileheader"); - - byte[] bytes = null; - while (true) { - long lastFp = reader.getPosition(); - - long crcValue; - - try { - crcValue = logStream.readLong("crcvalue"); - bytes = logStream.readBuffer("txnEntry"); - } catch (EOFException e) { - break; - } - - if (bytes.length == 0) { - break; - } - Checksum crc = new Adler32(); - crc.update(bytes, 0, bytes.length); - if (crcValue != crc.getValue()) { - throw new IOException("CRC doesn't match " + crcValue + - " vs " + crc.getValue()); - } - if (logStream.readByte("EOR") != 'B') { - throw new EOFException("Last transaction was partial."); - } - InputArchive iab = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes)); - TxnHeader hdr = new TxnHeader(); - Record r = SerializeUtils.deserializeTxn(iab, hdr); - - if (starttime == 0) { - starttime = hdr.getTime(); - } - endtime = hdr.getTime(); - - if (size % skipN == 0) { - skiplist.addMark(hdr.getTime(), lastFp, size); - } - size++; - } - if (bytes == null) { - throw new IOException("Nothing read from ("+file+")"); - } - } finally { - reader.close(); - } - } - - public String toString() { - return "TxnLogSource(file=" + file + ", size=" + size + ", start=" + starttime + ", end=" + endtime +")"; - } - - public static void main(String[] args) throws IOException, FilterException { - TxnLogSource s = new TxnLogSource(args[0]); - System.out.println(s); - - LogIterator iter; - - if (args.length == 3) { - long starttime = Long.valueOf(args[1]); - long endtime = Long.valueOf(args[2]); - FilterOp fo = new FilterParser("(or (and (> zxid 0x2f0bd6f5e0) (< zxid 0x2f0bd6f5e9)) (= operation \"error\"))").parse(); - System.out.println("fo: " + fo); - iter = s.iterator(starttime, endtime, fo); - } else { - iter = s.iterator(); - } - System.out.println(iter); - while (iter.hasNext()) { - System.out.println(iter.next()); - } - iter.close(); - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java deleted file mode 100644 index 581bdaacc0a..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class AndOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - for (FilterOp f : subOps) { - if (!f.matches(entry)) { - return false; - } - } - return true; - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java deleted file mode 100644 index 4fda3cf7d66..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.FilterOp.*; - -public class Arg { - private ArgType type; - protected T value; - - protected Arg(ArgType type) { - this.type = type; - } - - public ArgType getType() { return type; } - public T getValue() { return value; } - - public String toString() { - return "[" + type + ":" + value + "]"; - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java deleted file mode 100644 index 409815af212..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class EqualsOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - - Object last = null; - for (Arg a : args) { - Object v = a.getValue(); - if (a.getType() == FilterOp.ArgType.SYMBOL) { - String key = (String)a.getValue(); - v = entry.getAttribute(key); - } - - if (last != null - && !last.equals(v)) { - return false; - } - last = v; - } - - return true; - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java deleted file mode 100644 index 244dd3dabdb..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class GreaterThanOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - Arg first = args.get(0); - - if (first != null) { - FilterOp.ArgType type = first.getType(); - if (type == FilterOp.ArgType.SYMBOL) { - String key = (String)first.getValue(); - Object v = entry.getAttribute(key); - if (v instanceof String) { - type = FilterOp.ArgType.STRING; - } else if (v instanceof Double || v instanceof Long || v instanceof Integer || v instanceof Short) { - type = FilterOp.ArgType.NUMBER; - } else { - throw new FilterException("LessThanOp: Invalid argument, first argument resolves to neither a String nor a Number"); - } - } - - Object last = null; - for (Arg a : args) { - Object v = a.getValue(); - if (a.getType() == FilterOp.ArgType.SYMBOL) { - String key = (String)a.getValue(); - v = entry.getAttribute(key); - } - - if (last != null) { - if (type == FilterOp.ArgType.STRING) { - if (((String)last).compareTo((String)v) <= 0) { - return false; - } - } else if (type == FilterOp.ArgType.NUMBER) { - // System.out.println("last[" + ((Number)last).longValue() + "] v["+ ((Number)v).longValue() + "]"); - if (((Number)last).longValue() <= ((Number)v).longValue()) { - return false; - } - } - } - last = v; - } - return true; - } else { - return true; - } - } - -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java deleted file mode 100644 index b7d9e09ac89..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class LessThanOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - Arg first = args.get(0); - - if (first != null) { - FilterOp.ArgType type = first.getType(); - if (type == FilterOp.ArgType.SYMBOL) { - String key = (String)first.getValue(); - Object v = entry.getAttribute(key); - if (v instanceof String) { - type = FilterOp.ArgType.STRING; - } else if (v instanceof Double || v instanceof Long || v instanceof Integer || v instanceof Short) { - type = FilterOp.ArgType.NUMBER; - } else { - throw new FilterException("LessThanOp: Invalid argument, first argument resolves to neither a String nor a Number"); - } - } - - Object last = null; - for (Arg a : args) { - Object v = a.getValue(); - if (a.getType() == FilterOp.ArgType.SYMBOL) { - String key = (String)a.getValue(); - v = entry.getAttribute(key); - } - - if (last != null) { - if (type == FilterOp.ArgType.STRING) { - if (((String)last).compareTo((String)v) >= 0) { - return false; - } - } else if (type == FilterOp.ArgType.NUMBER) { - if (((Number)last).doubleValue() >= ((Number)v).doubleValue()) { - return false; - } - } - } - last = v; - } - return true; - } else { - return true; - } - } - -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java deleted file mode 100644 index d8ed7573faf..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class NotOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - if (subOps.size() != 1) { - throw new FilterException("Not operation can only take one argument"); - } - return !subOps.get(0).matches(entry); - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java deleted file mode 100644 index d6b584d855f..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.FilterOp.*; - -public class NumberArg extends Arg { - public NumberArg(Long value) { - super(ArgType.NUMBER); - this.value = value; - } -}; - diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java deleted file mode 100644 index d6815894a9a..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class OrOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - for (FilterOp f : subOps) { - if (f.matches(entry)) { - return true; - } - } - return false; - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java deleted file mode 100644 index 7345d3cc09a..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.FilterOp.*; - -public class StringArg extends Arg { - public StringArg(String value) { - super(ArgType.STRING); - this.value = value; - } -}; - diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java deleted file mode 100644 index 077553b1f05..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.FilterOp.*; - -public class SymbolArg extends Arg { - public SymbolArg(String value) { - super(ArgType.SYMBOL); - this.value = value; - } -}; diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java deleted file mode 100644 index 9e778b199d5..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class XorOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - int count = 0; - for (FilterOp f : subOps) { - if (f.matches(entry)) { - count++; - if (count > 1) { - return false; - } - } - } - if (count == 1) { - return true; - } - return false; - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java deleted file mode 100644 index 5cfe5d26960..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import org.apache.zookeeper.graph.*; - -public class FileLoader extends JsonServlet -{ - private MergedLogSource source = null; - - public FileLoader(MergedLogSource src) throws Exception { - source = src; - } - - String handleRequest(JsonRequest request) throws Exception - { - String output = ""; - - String file = request.getString("path", "/"); - JSONObject o = new JSONObject(); - try { - this.source.addSource(file); - o.put("status", "OK"); - - } catch (Exception e) { - o.put("status", "ERR"); - o.put("error", e.toString()); - } - - return JSONValue.toJSONString(o); - } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java deleted file mode 100644 index f8dc8cda9b9..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; -import java.util.Arrays; -import java.util.Comparator; - -public class Fs extends JsonServlet -{ - String handleRequest(JsonRequest request) throws Exception - { - String output = ""; - JSONArray filelist = new JSONArray(); - - File base = new File(request.getString("path", "/")); - if (!base.exists() || !base.isDirectory()) { - throw new FileNotFoundException("Couldn't find [" + request + "]"); - } - File[] files = base.listFiles(); - Arrays.sort(files, new Comparator() { - public int compare(File o1, File o2) { - if (o1.isDirectory() != o2.isDirectory()) { - if (o1.isDirectory()) { - return -1; - } else { - return 1; - } - } - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); - - for (File f : files) { - JSONObject o = new JSONObject(); - o.put("file", f.getName()); - o.put("type", f.isDirectory() ? "D" : "F"); - o.put("path", f.getCanonicalPath()); - filelist.add(o); - } - return JSONValue.toJSONString(filelist); - } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java deleted file mode 100644 index 99113c479cb..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import org.apache.zookeeper.graph.*; -import org.apache.log4j.Logger; - -public class GraphData extends JsonServlet -{ - private static final Logger LOG = Logger.getLogger(GraphData.class); - private static final int DEFAULT_PERIOD = 1000; - - private LogSource source = null; - - public GraphData(LogSource src) throws Exception { - this.source = src; - } - - String handleRequest(JsonRequest request) throws Exception { - - - long starttime = 0; - long endtime = 0; - long period = 0; - FilterOp fo = null; - - starttime = request.getNumber("start", 0); - endtime = request.getNumber("end", 0); - period = request.getNumber("period", 0); - String filterstr = request.getString("filter", ""); - - if (filterstr.length() > 0) { - fo = new FilterParser(filterstr).parse(); - } - - if (starttime == 0) { starttime = source.getStartTime(); } - if (endtime == 0) { - if (period > 0) { - endtime = starttime + period; - } else { - endtime = starttime + DEFAULT_PERIOD; - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", period=" + period + ")"); - } - - LogIterator iterator = (fo != null) ? - source.iterator(starttime, endtime, fo) : source.iterator(starttime, endtime); - return new JsonGenerator(iterator).toString(); - } -} \ No newline at end of file diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java deleted file mode 100644 index 910d44f4ede..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.util.Map; - -abstract public class JsonServlet extends HttpServlet { - abstract String handleRequest(JsonRequest request) throws Exception; - - protected class JsonRequest { - private Map map; - - public JsonRequest(ServletRequest request) { - map = request.getParameterMap(); - } - - public long getNumber(String name, long defaultnum) { - String[] vals = (String[])map.get(name); - if (vals == null || vals.length == 0) { - return defaultnum; - } - - try { - return Long.valueOf(vals[0]); - } catch (NumberFormatException e) { - return defaultnum; - } - } - - public String getString(String name, String defaultstr) { - String[] vals = (String[])map.get(name); - if (vals == null || vals.length == 0) { - return defaultstr; - } else { - return vals[0]; - } - } - } - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - response.setContentType("text/plain;charset=utf-8"); - response.setStatus(HttpServletResponse.SC_OK); - - try { - String req = request.getRequestURI().substring(request.getServletPath().length()); - - response.getWriter().println(handleRequest(new JsonRequest(request))); - } catch (Exception e) { - JSONObject o = new JSONObject(); - o.put("error", e.toString()); - response.getWriter().println(JSONValue.toJSONString(o)); - } catch (java.lang.OutOfMemoryError oom) { - JSONObject o = new JSONObject(); - o.put("error", "Out of memory. Perhaps you've requested too many logs. Try narrowing you're filter criteria."); - response.getWriter().println(JSONValue.toJSONString(o)); - } - } -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java deleted file mode 100644 index 268a79415b7..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import org.apache.zookeeper.graph.*; -import org.apache.log4j.Logger; - - -public class NumEvents extends JsonServlet -{ - private static final Logger LOG = Logger.getLogger(NumEvents.class); - private static final int DEFAULT_PERIOD = 1000; - - private LogSource source = null; - - public NumEvents(LogSource src) throws Exception { - this.source = src; - } - - String handleRequest(JsonRequest request) throws Exception { - String output = ""; - - long starttime = 0; - long endtime = 0; - long period = 0; - - starttime = request.getNumber("start", 0); - endtime = request.getNumber("end", 0); - period = request.getNumber("period", 0); - - if (starttime == 0) { starttime = source.getStartTime(); } - if (endtime == 0) { - if (period > 0) { - endtime = starttime + period; - } else { - endtime = source.getEndTime(); - } - } - - LogIterator iter = source.iterator(starttime, endtime); - JSONObject data = new JSONObject(); - data.put("startTime", starttime); - data.put("endTime", endtime); - long size = 0; - - size = iter.size(); - - data.put("numEntries", size); - if (LOG.isDebugEnabled()) { - LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", numEntries=" + size +")"); - } - return JSONValue.toJSONString(data); - } -} - diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java deleted file mode 100644 index 4af78959a84..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.BufferedReader; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class StaticContent extends HttpServlet { - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - String path = request.getRequestURI().substring(request.getServletPath().length()); - - InputStream resource = ClassLoader.getSystemResourceAsStream("org/apache/zookeeper/graph/resources" + path); - if (resource == null) { - response.getWriter().println(path + " not found!"); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return; - } - - while (resource.available() > 0) { - response.getWriter().write(resource.read()); - } - // response.setContentType("text/plain;charset=utf-8"); - response.setStatus(HttpServletResponse.SC_OK); - } - -} diff --git a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java b/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java deleted file mode 100644 index d40b8d07e0e..00000000000 --- a/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.IOException; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.DataOutputStream; -import java.io.PrintStream; - -import java.util.HashSet; -import java.util.LinkedHashMap; - -import org.apache.zookeeper.graph.*; -import org.apache.log4j.Logger; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - - -public class Throughput extends JsonServlet -{ - private static final int MS_PER_SEC = 1000; - private static final int MS_PER_MIN = MS_PER_SEC*60; - private static final int MS_PER_HOUR = MS_PER_MIN*60; - - private LogSource source = null; - - public Throughput(LogSource src) throws Exception { - this.source = src; - } - - public String handleRequest(JsonRequest request) throws Exception { - long starttime = 0; - long endtime = 0; - long period = 0; - long scale = 0; - - starttime = request.getNumber("start", 0); - endtime = request.getNumber("end", 0); - period = request.getNumber("period", 0); - - - if (starttime == 0) { starttime = source.getStartTime(); } - if (endtime == 0) { - if (period > 0) { - endtime = starttime + period; - } else { - endtime = source.getEndTime(); - } - } - - String scalestr = request.getString("scale", "minutes"); - if (scalestr.equals("seconds")) { - scale = MS_PER_SEC; - } else if (scalestr.equals("hours")) { - scale = MS_PER_HOUR; - } else { - scale = MS_PER_MIN; - } - - LogIterator iter = source.iterator(starttime, endtime); - - long current = 0; - long currentms = 0; - HashSet zxids_ms = new HashSet(); - long zxidcount = 0; - - JSONArray events = new JSONArray(); - while (iter.hasNext()) { - LogEntry e = iter.next(); - if (e.getType() != LogEntry.Type.TXN) { - continue; - } - - TransactionEntry cxn = (TransactionEntry)e; - - long ms = cxn.getTimestamp(); - long inscale = ms/scale; - - if (currentms != ms && currentms != 0) { - zxidcount += zxids_ms.size(); - zxids_ms.clear(); - } - - if (inscale != current && current != 0) { - JSONObject o = new JSONObject(); - o.put("time", current*scale); - o.put("count", zxidcount); - events.add(o); - zxidcount = 0; - } - current = inscale; - currentms = ms; - - zxids_ms.add(cxn.getZxid()); - } - JSONObject o = new JSONObject(); - o.put("time", current*scale); - o.put("count", zxidcount); - events.add(o); - - iter.close(); - - return JSONValue.toJSONString(events); - } - -}; diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties b/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties deleted file mode 100644 index ab8960b0e4f..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootLogger=TRACE, CONSOLE - -# Print the date in ISO 8601 format -log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.Threshold=TRACE -log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n - -log4j.logger.org.apache.zookeeper.graph.LogSkipList=off -log4j.logger.org.apache.zookeeper.graph.RandomAccessFileReader=off -#log4j.logger.org.apache.zookeeper.graph.Log4JSource=off \ No newline at end of file diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js deleted file mode 100644 index 3992c50f73a..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Date Format 1.2.3 - * (c) 2007-2009 Steven Levithan - * MIT license - * - * Includes enhancements by Scott Trenda - * and Kris Kowal - * - * Accepts a date, a mask, or a date and a mask. - * Returns a formatted version of the given date. - * The date defaults to the current date/time. - * The mask defaults to dateFormat.masks.default. - */ - -var dateFormat = function () { - var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, - timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, - timezoneClip = /[^-+\dA-Z]/g, - pad = function (val, len) { - val = String(val); - len = len || 2; - while (val.length < len) val = "0" + val; - return val; - }; - - // Regexes and supporting functions are cached through closure - return function (date, mask, utc) { - var dF = dateFormat; - - // You can't provide utc if you skip other args (use the "UTC:" mask prefix) - if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { - mask = date; - date = undefined; - } - - // Passing date through Date applies Date.parse, if necessary - date = date ? new Date(date) : new Date; - if (isNaN(date)) throw SyntaxError("invalid date"); - - mask = String(dF.masks[mask] || mask || dF.masks["default"]); - - // Allow setting the utc argument via the mask - if (mask.slice(0, 4) == "UTC:") { - mask = mask.slice(4); - utc = true; - } - - var _ = utc ? "getUTC" : "get", - d = date[_ + "Date"](), - D = date[_ + "Day"](), - m = date[_ + "Month"](), - y = date[_ + "FullYear"](), - H = date[_ + "Hours"](), - M = date[_ + "Minutes"](), - s = date[_ + "Seconds"](), - L = date[_ + "Milliseconds"](), - o = utc ? 0 : date.getTimezoneOffset(), - flags = { - d: d, - dd: pad(d), - ddd: dF.i18n.dayNames[D], - dddd: dF.i18n.dayNames[D + 7], - m: m + 1, - mm: pad(m + 1), - mmm: dF.i18n.monthNames[m], - mmmm: dF.i18n.monthNames[m + 12], - yy: String(y).slice(2), - yyyy: y, - h: H % 12 || 12, - hh: pad(H % 12 || 12), - H: H, - HH: pad(H), - M: M, - MM: pad(M), - s: s, - ss: pad(s), - l: pad(L, 3), - L: pad(L > 99 ? Math.round(L / 10) : L), - t: H < 12 ? "a" : "p", - tt: H < 12 ? "am" : "pm", - T: H < 12 ? "A" : "P", - TT: H < 12 ? "AM" : "PM", - Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), - o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), - S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] - }; - - return mask.replace(token, function ($0) { - return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); - }); - }; -}(); - -// Some common format strings -dateFormat.masks = { - "default": "ddd mmm dd yyyy HH:MM:ss", - shortDate: "m/d/yy", - mediumDate: "mmm d, yyyy", - longDate: "mmmm d, yyyy", - fullDate: "dddd, mmmm d, yyyy", - shortTime: "h:MM TT", - mediumTime: "h:MM:ss TT", - longTime: "h:MM:ss TT Z", - isoDate: "yyyy-mm-dd", - isoTime: "HH:MM:ss", - isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", - isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" -}; - -// Internationalization strings -dateFormat.i18n = { - dayNames: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", - "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" - ], - monthNames: [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ] -}; - -// For convenience... -Date.prototype.format = function (mask, utc) { - return dateFormat(this, mask, utc); -}; - diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js deleted file mode 100644 index 2f7212ad61b..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js +++ /dev/null @@ -1,385 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.barchart = function (x, y, width, height, values, opts) { - opts = opts || {}; - var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square", - gutter = parseFloat(opts.gutter || "20%"), - chart = this.set(), - bars = this.set(), - covers = this.set(), - covers2 = this.set(), - total = Math.max.apply(Math, values), - stacktotal = [], - paper = this, - multi = 0, - colors = opts.colors || this.g.colors, - len = values.length; - if (this.raphael.is(values[0], "array")) { - total = []; - multi = len; - len = 0; - for (var i = values.length; i--;) { - bars.push(this.set()); - total.push(Math.max.apply(Math, values[i])); - len = Math.max(len, values[i].length); - } - if (opts.stacked) { - for (var i = len; i--;) { - var tot = 0; - for (var j = values.length; j--;) { - tot +=+ values[j][i] || 0; - } - stacktotal.push(tot); - } - } - for (var i = values.length; i--;) { - if (values[i].length < len) { - for (var j = len; j--;) { - values[i].push(0); - } - } - } - total = Math.max.apply(Math, opts.stacked ? stacktotal : total); - } - - total = (opts.to) || total; - var barwidth = width / (len * (100 + gutter) + gutter) * 100, - barhgutter = barwidth * gutter / 100, - barvgutter = opts.vgutter == null ? 20 : opts.vgutter, - stack = [], - X = x + barhgutter, - Y = (height - 2 * barvgutter) / total; - if (!opts.stretch) { - barhgutter = Math.round(barhgutter); - barwidth = Math.floor(barwidth); - } - !opts.stacked && (barwidth /= multi || 1); - for (var i = 0; i < len; i++) { - stack = []; - for (var j = 0; j < (multi || 1); j++) { - var h = Math.round((multi ? values[j][i] : values[i]) * Y), - top = y + height - barvgutter - h, - bar = this.g.finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type).attr({stroke: colors[multi ? j : i], fill: colors[multi ? j : i]}); - if (multi) { - bars[j].push(bar); - } else { - bars.push(bar); - } - bar.y = top; - bar.x = Math.round(X + barwidth / 2); - bar.w = barwidth; - bar.h = h; - bar.value = multi ? values[j][i] : values[i]; - if (!opts.stacked) { - X += barwidth; - } else { - stack.push(bar); - } - } - if (opts.stacked) { - var cvr; - covers2.push(cvr = this.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(this.g.shim)); - cvr.bars = this.set(); - var size = 0; - for (var s = stack.length; s--;) { - stack[s].toFront(); - } - for (var s = 0, ss = stack.length; s < ss; s++) { - var bar = stack[s], - cover, - h = (size + bar.value) * Y, - path = this.g.finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1); - cvr.bars.push(bar); - size && bar.attr({path: path}); - bar.h = h; - bar.y = y + height - barvgutter - !!size * .5 - h; - covers.push(cover = this.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(this.g.shim)); - cover.bar = bar; - cover.value = bar.value; - size += bar.value; - } - X += barwidth; - } - X += barhgutter; - } - covers2.toFront(); - X = x + barhgutter; - if (!opts.stacked) { - for (var i = 0; i < len; i++) { - for (var j = 0; j < (multi || 1); j++) { - var cover; - covers.push(cover = this.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(this.g.shim)); - cover.bar = multi ? bars[j][i] : bars[i]; - cover.value = cover.bar.value; - X += barwidth; - } - X += barhgutter; - } - } - chart.label = function (labels, isBottom) { - labels = labels || []; - this.labels = paper.set(); - var L, l = -Infinity; - if (opts.stacked) { - for (var i = 0; i < len; i++) { - var tot = 0; - for (var j = 0; j < (multi || 1); j++) { - tot += multi ? values[j][i] : values[i]; - if (j == multi - 1) { - var label = paper.g.labelise(labels[i], tot, total); - L = paper.g.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]); - var bb = L.getBBox(); - if (bb.x - 7 < l) { - L.remove(); - } else { - this.labels.push(L); - l = bb.x + bb.width; - } - } - } - } - } else { - for (var i = 0; i < len; i++) { - for (var j = 0; j < (multi || 1); j++) { - var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total); - L = paper.g.text(bars[i * (multi || 1) + j].x, isBottom ? y + height - barvgutter / 2 : bars[i * (multi || 1) + j].y - 10, label).insertBefore(covers[i * (multi || 1) + j]); - var bb = L.getBBox(); - if (bb.x - 7 < l) { - L.remove(); - } else { - this.labels.push(L); - l = bb.x + bb.width; - } - } - } - } - return this; - }; - chart.hover = function (fin, fout) { - covers2.hide(); - covers.show(); - covers.mouseover(fin).mouseout(fout); - return this; - }; - chart.hoverColumn = function (fin, fout) { - covers.hide(); - covers2.show(); - fout = fout || function () {}; - covers2.mouseover(fin).mouseout(fout); - return this; - }; - chart.click = function (f) { - covers2.hide(); - covers.show(); - covers.click(f); - return this; - }; - chart.each = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers.length; i--;) { - f.call(covers[i]); - } - return this; - }; - chart.eachColumn = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers2.length; i--;) { - f.call(covers2[i]); - } - return this; - }; - chart.clickColumn = function (f) { - covers.hide(); - covers2.show(); - covers2.click(f); - return this; - }; - chart.push(bars, covers, covers2); - chart.bars = bars; - chart.covers = covers; - return chart; -}; -Raphael.fn.g.hbarchart = function (x, y, width, height, values, opts) { - opts = opts || {}; - var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square", - gutter = parseFloat(opts.gutter || "20%"), - chart = this.set(), - bars = this.set(), - covers = this.set(), - covers2 = this.set(), - total = Math.max.apply(Math, values), - stacktotal = [], - paper = this, - multi = 0, - colors = opts.colors || this.g.colors, - len = values.length; - if (this.raphael.is(values[0], "array")) { - total = []; - multi = len; - len = 0; - for (var i = values.length; i--;) { - bars.push(this.set()); - total.push(Math.max.apply(Math, values[i])); - len = Math.max(len, values[i].length); - } - if (opts.stacked) { - for (var i = len; i--;) { - var tot = 0; - for (var j = values.length; j--;) { - tot +=+ values[j][i] || 0; - } - stacktotal.push(tot); - } - } - for (var i = values.length; i--;) { - if (values[i].length < len) { - for (var j = len; j--;) { - values[i].push(0); - } - } - } - total = Math.max.apply(Math, opts.stacked ? stacktotal : total); - } - - total = (opts.to) || total; - var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100), - bargutter = Math.floor(barheight * gutter / 100), - stack = [], - Y = y + bargutter, - X = (width - 1) / total; - !opts.stacked && (barheight /= multi || 1); - for (var i = 0; i < len; i++) { - stack = []; - for (var j = 0; j < (multi || 1); j++) { - var val = multi ? values[j][i] : values[i], - bar = this.g.finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type).attr({stroke: colors[multi ? j : i], fill: colors[multi ? j : i]}); - if (multi) { - bars[j].push(bar); - } else { - bars.push(bar); - } - bar.x = x + Math.round(val * X); - bar.y = Y + barheight / 2; - bar.w = Math.round(val * X); - bar.h = barheight; - bar.value = +val; - if (!opts.stacked) { - Y += barheight; - } else { - stack.push(bar); - } - } - if (opts.stacked) { - var cvr = this.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(this.g.shim); - covers2.push(cvr); - cvr.bars = this.set(); - var size = 0; - for (var s = stack.length; s--;) { - stack[s].toFront(); - } - for (var s = 0, ss = stack.length; s < ss; s++) { - var bar = stack[s], - cover, - val = Math.round((size + bar.value) * X), - path = this.g.finger(x, bar.y, val, barheight - 1, false, type, 1); - cvr.bars.push(bar); - size && bar.attr({path: path}); - bar.w = val; - bar.x = x + val; - covers.push(cover = this.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(this.g.shim)); - cover.bar = bar; - size += bar.value; - } - Y += barheight; - } - Y += bargutter; - } - covers2.toFront(); - Y = y + bargutter; - if (!opts.stacked) { - for (var i = 0; i < len; i++) { - for (var j = 0; j < multi; j++) { - var cover = this.rect(x, Y, width, barheight).attr(this.g.shim); - covers.push(cover); - cover.bar = bars[j][i]; - Y += barheight; - } - Y += bargutter; - } - } - chart.label = function (labels, isRight) { - labels = labels || []; - this.labels = paper.set(); - for (var i = 0; i < len; i++) { - for (var j = 0; j < multi; j++) { - var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total); - var X = isRight ? bars[i * (multi || 1) + j].x - barheight / 2 + 3 : x + 5, - A = isRight ? "end" : "start", - L; - this.labels.push(L = paper.g.text(X, bars[i * (multi || 1) + j].y, label).attr({"text-anchor": A}).insertBefore(covers[0])); - if (L.getBBox().x < x + 5) { - L.attr({x: x + 5, "text-anchor": "start"}); - } else { - bars[i * (multi || 1) + j].label = L; - } - } - } - return this; - }; - chart.hover = function (fin, fout) { - covers2.hide(); - covers.show(); - fout = fout || function () {}; - covers.mouseover(fin).mouseout(fout); - return this; - }; - chart.hoverColumn = function (fin, fout) { - covers.hide(); - covers2.show(); - fout = fout || function () {}; - covers2.mouseover(fin).mouseout(fout); - return this; - }; - chart.each = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers.length; i--;) { - f.call(covers[i]); - } - return this; - }; - chart.eachColumn = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers2.length; i--;) { - f.call(covers2[i]); - } - return this; - }; - chart.click = function (f) { - covers2.hide(); - covers.show(); - covers.click(f); - return this; - }; - chart.clickColumn = function (f) { - covers.hide(); - covers2.show(); - covers2.click(f); - return this; - }; - chart.push(bars, covers, covers2); - chart.bars = bars; - chart.covers = covers; - return chart; -}; diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js deleted file mode 100644 index 2821e62c730..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js +++ /dev/null @@ -1,110 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.dotchart = function (x, y, width, height, valuesx, valuesy, size, opts) { - function drawAxis(ax) { - +ax[0] && (ax[0] = paper.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlabels || null, opts.axisxtype || "t")); - +ax[1] && (ax[1] = paper.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.axisylabels || null, opts.axisytype || "t")); - +ax[2] && (ax[2] = paper.g.axis(x + gutter, y + height - gutter + maxR, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlabels || null, opts.axisxtype || "t")); - +ax[3] && (ax[3] = paper.g.axis(x + gutter - maxR, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.axisylabels || null, opts.axisytype || "t")); - } - opts = opts || {}; - var xdim = this.g.snapEnds(Math.min.apply(Math, valuesx), Math.max.apply(Math, valuesx), valuesx.length - 1), - minx = xdim.from, - maxx = xdim.to, - gutter = opts.gutter || 10, - ydim = this.g.snapEnds(Math.min.apply(Math, valuesy), Math.max.apply(Math, valuesy), valuesy.length - 1), - miny = ydim.from, - maxy = ydim.to, - len = Math.max(valuesx.length, valuesy.length, size.length), - symbol = this.g.markers[opts.symbol] || "disc", - res = this.set(), - series = this.set(), - max = opts.max || 100, - top = Math.max.apply(Math, size), - R = [], - paper = this, - k = Math.sqrt(top / Math.PI) * 2 / max; - - for (var i = 0; i < len; i++) { - R[i] = Math.min(Math.sqrt(size[i] / Math.PI) * 2 / k, max); - } - gutter = Math.max.apply(Math, R.concat(gutter)); - var axis = this.set(), - maxR = Math.max.apply(Math, R); - if (opts.axis) { - var ax = (opts.axis + "").split(/[,\s]+/); - drawAxis(ax); - var g = [], b = []; - for (var i = 0, ii = ax.length; i < ii; i++) { - var bb = ax[i].all ? ax[i].all.getBBox()[["height", "width"][i % 2]] : 0; - g[i] = bb + gutter; - b[i] = bb; - } - gutter = Math.max.apply(Math, g.concat(gutter)); - for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) { - ax[i].remove(); - ax[i] = 1; - } - drawAxis(ax); - for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) { - axis.push(ax[i].all); - } - res.axis = axis; - } - var kx = (width - gutter * 2) / ((maxx - minx) || 1), - ky = (height - gutter * 2) / ((maxy - miny) || 1); - for (var i = 0, ii = valuesy.length; i < ii; i++) { - var sym = this.raphael.is(symbol, "array") ? symbol[i] : symbol, - X = x + gutter + (valuesx[i] - minx) * kx, - Y = y + height - gutter - (valuesy[i] - miny) * ky; - sym && R[i] && series.push(this.g[sym](X, Y, R[i]).attr({fill: opts.heat ? this.g.colorValue(R[i], maxR) : Raphael.fn.g.colors[0], "fill-opacity": opts.opacity ? R[i] / max : 1, stroke: "none"})); - } - var covers = this.set(); - for (var i = 0, ii = valuesy.length; i < ii; i++) { - var X = x + gutter + (valuesx[i] - minx) * kx, - Y = y + height - gutter - (valuesy[i] - miny) * ky; - covers.push(this.circle(X, Y, maxR).attr(this.g.shim)); - opts.href && opts.href[i] && covers[i].attr({href: opts.href[i]}); - covers[i].r = +R[i].toFixed(3); - covers[i].x = +X.toFixed(3); - covers[i].y = +Y.toFixed(3); - covers[i].X = valuesx[i]; - covers[i].Y = valuesy[i]; - covers[i].value = size[i] || 0; - covers[i].dot = series[i]; - } - res.covers = covers; - res.series = series; - res.push(series, axis, covers); - res.hover = function (fin, fout) { - covers.mouseover(fin).mouseout(fout); - return this; - }; - res.click = function (f) { - covers.click(f); - return this; - }; - res.each = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers.length; i--;) { - f.call(covers[i]); - } - return this; - }; - res.href = function (map) { - var cover; - for (var i = covers.length; i--;) { - cover = covers[i]; - if (cover.X == map.x && cover.Y == map.y && cover.value == map.value) { - cover.attr({href: map.href}); - } - } - }; - return res; -}; diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.line.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.line.js deleted file mode 100644 index eb56e5916dc..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.line.js +++ /dev/null @@ -1,230 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.linechart = function (x, y, width, height, valuesx, valuesy, opts) { - function shrink(values, dim) { - var k = values.length / dim, - j = 0, - l = k, - sum = 0, - res = []; - while (j < values.length) { - l--; - if (l < 0) { - sum += values[j] * (1 + l); - res.push(sum / k); - sum = values[j++] * -l; - l += k; - } else { - sum += values[j++]; - } - } - return res; - } - opts = opts || {}; - if (!this.raphael.is(valuesx[0], "array")) { - valuesx = [valuesx]; - } - if (!this.raphael.is(valuesy[0], "array")) { - valuesy = [valuesy]; - } - var allx = Array.prototype.concat.apply([], valuesx), - ally = Array.prototype.concat.apply([], valuesy), - xdim = this.g.snapEnds(Math.min.apply(Math, allx), Math.max.apply(Math, allx), valuesx[0].length - 1), - minx = xdim.from, - maxx = xdim.to, - gutter = opts.gutter || 10, - kx = (width - gutter * 2) / (maxx - minx), - ydim = this.g.snapEnds(Math.min.apply(Math, ally), Math.max.apply(Math, ally), valuesy[0].length - 1), - miny = ydim.from, - maxy = ydim.to, - ky = (height - gutter * 2) / (maxy - miny), - len = Math.max(valuesx[0].length, valuesy[0].length), - symbol = opts.symbol || "", - colors = opts.colors || Raphael.fn.g.colors, - that = this, - columns = null, - dots = null, - chart = this.set(), - path = []; - - for (var i = 0, ii = valuesy.length; i < ii; i++) { - len = Math.max(len, valuesy[i].length); - } - var shades = this.set(); - for (var i = 0, ii = valuesy.length; i < ii; i++) { - if (opts.shade) { - shades.push(this.path().attr({stroke: "none", fill: colors[i], opacity: opts.nostroke ? 1 : .3})); - } - if (valuesy[i].length > width - 2 * gutter) { - valuesy[i] = shrink(valuesy[i], width - 2 * gutter); - len = width - 2 * gutter; - } - if (valuesx[i] && valuesx[i].length > width - 2 * gutter) { - valuesx[i] = shrink(valuesx[i], width - 2 * gutter); - } - } - var axis = this.set(); - if (opts.axis) { - var ax = (opts.axis + "").split(/[,\s]+/); - +ax[0] && axis.push(this.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.northlabels)); - +ax[1] && axis.push(this.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.eastlabels)); - +ax[2] && axis.push(this.g.axis(x + gutter, y + height - gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.southlabels)); - +ax[3] && axis.push(this.g.axis(x + gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.westlabels)); - } - if (opts.northAxisLabel) { - this.g.text(x + gutter + width/2, gutter, opts.northAxisLabel); - } - if (opts.southAxisLabel) { - this.g.text(x + gutter + width/2, y + height + 20, opts.southAxisLabel); - } - if (opts.westAxisLabel) { - this.g.text(gutter, y + gutter + height/2, opts.westAxisLabel).attr({rotation: -90}); - } - if (opts.eastAxisLabel) { - this.g.text(x + gutter + width + 20, y + gutter + height/2, opts.eastAxisLabel).attr({rotation: 90}); - } - - var lines = this.set(), - symbols = this.set(), - line; - for (var i = 0, ii = valuesy.length; i < ii; i++) { - if (!opts.nostroke) { - lines.push(line = this.path().attr({ - stroke: colors[i], - "stroke-width": opts.width || 2, - "stroke-linejoin": "round", - "stroke-linecap": "round", - "stroke-dasharray": opts.dash || "" - })); - } - var sym = this.raphael.is(symbol, "array") ? symbol[i] : symbol, - symset = this.set(); - path = []; - for (var j = 0, jj = valuesy[i].length; j < jj; j++) { - var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx; - var Y = y + height - gutter - (valuesy[i][j] - miny) * ky; - (Raphael.is(sym, "array") ? sym[j] : sym) && symset.push(this.g[Raphael.fn.g.markers[this.raphael.is(sym, "array") ? sym[j] : sym]](X, Y, (opts.width || 2) * 3).attr({fill: colors[i], stroke: "none"})); - path = path.concat([j ? "L" : "M", X, Y]); - } - symbols.push(symset); - if (opts.shade) { - shades[i].attr({path: path.concat(["L", X, y + height - gutter, "L", x + gutter + ((valuesx[i] || valuesx[0])[0] - minx) * kx, y + height - gutter, "z"]).join(",")}); - } - !opts.nostroke && line.attr({path: path.join(",")}); - } - function createColumns(f) { - // unite Xs together - var Xs = []; - for (var i = 0, ii = valuesx.length; i < ii; i++) { - Xs = Xs.concat(valuesx[i]); - } - Xs.sort(); - // remove duplicates - var Xs2 = [], - xs = []; - for (var i = 0, ii = Xs.length; i < ii; i++) { - Xs[i] != Xs[i - 1] && Xs2.push(Xs[i]) && xs.push(x + gutter + (Xs[i] - minx) * kx); - } - Xs = Xs2; - ii = Xs.length; - var cvrs = f || that.set(); - for (var i = 0; i < ii; i++) { - var X = xs[i] - (xs[i] - (xs[i - 1] || x)) / 2, - w = ((xs[i + 1] || x + width) - xs[i]) / 2 + (xs[i] - (xs[i - 1] || x)) / 2, - C; - f ? (C = {}) : cvrs.push(C = that.rect(X - 1, y, Math.max(w + 1, 1), height).attr({stroke: "none", fill: "#000", opacity: 0})); - C.values = []; - C.symbols = that.set(); - C.y = []; - C.x = xs[i]; - C.axis = Xs[i]; - for (var j = 0, jj = valuesy.length; j < jj; j++) { - Xs2 = valuesx[j] || valuesx[0]; - for (var k = 0, kk = Xs2.length; k < kk; k++) { - if (Xs2[k] == Xs[i]) { - C.values.push(valuesy[j][k]); - C.y.push(y + height - gutter - (valuesy[j][k] - miny) * ky); - C.symbols.push(chart.symbols[j][k]); - } - } - } - f && f.call(C); - } - !f && (columns = cvrs); - } - function createDots(f) { - var cvrs = f || that.set(), - C; - for (var i = 0, ii = valuesy.length; i < ii; i++) { - for (var j = 0, jj = valuesy[i].length; j < jj; j++) { - var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx, - nearX = x + gutter + ((valuesx[i] || valuesx[0])[j ? j - 1 : 1] - minx) * kx, - Y = y + height - gutter - (valuesy[i][j] - miny) * ky; - f ? (C = {}) : cvrs.push(C = that.circle(X, Y, Math.abs(nearX - X) / 2).attr({stroke: "none", fill: "#000", opacity: 0})); - C.x = X; - C.y = Y; - C.value = valuesy[i][j]; - C.line = chart.lines[i]; - C.shade = chart.shades[i]; - C.symbol = chart.symbols[i][j]; - C.symbols = chart.symbols[i]; - C.axis = (valuesx[i] || valuesx[0])[j]; - f && f.call(C); - } - } - !f && (dots = cvrs); - } - chart.push(lines, shades, symbols, axis, columns, dots); - chart.lines = lines; - chart.shades = shades; - chart.symbols = symbols; - chart.axis = axis; - chart.hoverColumn = function (fin, fout) { - !columns && createColumns(); - columns.mouseover(fin).mouseout(fout); - return this; - }; - chart.clickColumn = function (f) { - !columns && createColumns(); - columns.click(f); - return this; - }; - chart.hrefColumn = function (cols) { - var hrefs = that.raphael.is(arguments[0], "array") ? arguments[0] : arguments; - if (!(arguments.length - 1) && typeof cols == "object") { - for (var x in cols) { - for (var i = 0, ii = columns.length; i < ii; i++) if (columns[i].axis == x) { - columns[i].attr("href", cols[x]); - } - } - } - !columns && createColumns(); - for (var i = 0, ii = hrefs.length; i < ii; i++) { - columns[i] && columns[i].attr("href", hrefs[i]); - } - return this; - }; - chart.hover = function (fin, fout) { - !dots && createDots(); - dots.mouseover(fin).mouseout(fout); - return this; - }; - chart.click = function (f) { - !dots && createDots(); - dots.click(f); - return this; - }; - chart.each = function (f) { - createDots(f); - return this; - }; - chart.eachColumn = function (f) { - createColumns(f); - return this; - }; - return chart; -}; diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.pie.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.pie.js deleted file mode 100644 index 8d203745528..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.pie.js +++ /dev/null @@ -1,205 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.piechart = function (cx, cy, r, values, opts) { - opts = opts || {}; - var paper = this, - sectors = [], - covers = this.set(), - chart = this.set(), - series = this.set(), - order = [], - len = values.length, - angle = 0, - total = 0, - others = 0, - cut = 9, - defcut = true; - chart.covers = covers; - if (len == 1) { - series.push(this.circle(cx, cy, r).attr({fill: this.g.colors[0], stroke: opt.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth})); - covers.push(this.circle(cx, cy, r).attr(this.g.shim)); - total = values[0]; - values[0] = {value: values[0], order: 0, valueOf: function () { return this.value; }}; - series[0].middle = {x: cx, y: cy}; - series[0].mangle = 180; - } else { - function sector(cx, cy, r, startAngle, endAngle, fill) { - var rad = Math.PI / 180, - x1 = cx + r * Math.cos(-startAngle * rad), - x2 = cx + r * Math.cos(-endAngle * rad), - xm = cx + r / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad), - y1 = cy + r * Math.sin(-startAngle * rad), - y2 = cy + r * Math.sin(-endAngle * rad), - ym = cy + r / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad), - res = ["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2, "z"]; - res.middle = {x: xm, y: ym}; - return res; - } - for (var i = 0; i < len; i++) { - total += values[i]; - values[i] = {value: values[i], order: i, valueOf: function () { return this.value; }}; - } - values.sort(function (a, b) { - return b.value - a.value; - }); - for (var i = 0; i < len; i++) { - if (defcut && values[i] * 360 / total <= 1.5) { - cut = i; - defcut = false; - } - if (i > cut) { - defcut = false; - values[cut].value += values[i]; - values[cut].others = true; - others = values[cut].value; - } - } - len = Math.min(cut + 1, values.length); - others && values.splice(len) && (values[cut].others = true); - for (var i = 0; i < len; i++) { - var mangle = angle - 360 * values[i] / total / 2; - if (!i) { - angle = 90 - mangle; - mangle = angle - 360 * values[i] / total / 2; - } - if (opts.init) { - var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(","); - } - var path = sector(cx, cy, r, angle, angle -= 360 * values[i] / total); - var p = this.path(opts.init ? ipath : path).attr({fill: opts.colors && opts.colors[i] || this.g.colors[i] || "#666", stroke: opts.stroke || "#fff", "stroke-width": (opts.strokewidth == null ? 1 : opts.strokewidth), "stroke-linejoin": "round"}); - p.value = values[i]; - p.middle = path.middle; - p.mangle = mangle; - sectors.push(p); - series.push(p); - opts.init && p.animate({path: path.join(",")}, (+opts.init - 1) || 1000, ">"); - } - for (var i = 0; i < len; i++) { - var p = paper.path(sectors[i].attr("path")).attr(this.g.shim); - opts.href && opts.href[i] && p.attr({href: opts.href[i]}); - p.attr = function () {}; - covers.push(p); - series.push(p); - } - } - - chart.hover = function (fin, fout) { - fout = fout || function () {}; - var that = this; - for (var i = 0; i < len; i++) { - (function (sector, cover, j) { - var o = { - sector: sector, - cover: cover, - cx: cx, - cy: cy, - mx: sector.middle.x, - my: sector.middle.y, - mangle: sector.mangle, - r: r, - value: values[j], - total: total, - label: that.labels && that.labels[j] - }; - cover.mouseover(function () { - fin.call(o); - }).mouseout(function () { - fout.call(o); - }); - })(series[i], covers[i], i); - } - return this; - }; - // x: where label could be put - // y: where label could be put - // value: value to show - // total: total number to count % - chart.each = function (f) { - var that = this; - for (var i = 0; i < len; i++) { - (function (sector, cover, j) { - var o = { - sector: sector, - cover: cover, - cx: cx, - cy: cy, - x: sector.middle.x, - y: sector.middle.y, - mangle: sector.mangle, - r: r, - value: values[j], - total: total, - label: that.labels && that.labels[j] - }; - f.call(o); - })(series[i], covers[i], i); - } - return this; - }; - chart.click = function (f) { - var that = this; - for (var i = 0; i < len; i++) { - (function (sector, cover, j) { - var o = { - sector: sector, - cover: cover, - cx: cx, - cy: cy, - mx: sector.middle.x, - my: sector.middle.y, - mangle: sector.mangle, - r: r, - value: values[j], - total: total, - label: that.labels && that.labels[j] - }; - cover.click(function () { f.call(o); }); - })(series[i], covers[i], i); - } - return this; - }; - chart.inject = function (element) { - element.insertBefore(covers[0]); - }; - var legend = function (labels, otherslabel, mark, dir) { - var x = cx + r + r / 5, - y = cy, - h = y + 10; - labels = labels || []; - dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east"; - mark = paper.g.markers[mark && mark.toLowerCase()] || "disc"; - chart.labels = paper.set(); - for (var i = 0; i < len; i++) { - var clr = series[i].attr("fill"), - j = values[i].order, - txt; - values[i].others && (labels[j] = otherslabel || "Others"); - labels[j] = paper.g.labelise(labels[j], values[i], total); - chart.labels.push(paper.set()); - chart.labels[i].push(paper.g[mark](x + 5, h, 5).attr({fill: clr, stroke: "none"})); - chart.labels[i].push(txt = paper.text(x + 20, h, labels[j] || values[j]).attr(paper.g.txtattr).attr({fill: opts.legendcolor || "#000", "text-anchor": "start"})); - covers[i].label = chart.labels[i]; - h += txt.getBBox().height * 1.2; - } - var bb = chart.labels.getBBox(), - tr = { - east: [0, -bb.height / 2], - west: [-bb.width - 2 * r - 20, -bb.height / 2], - north: [-r - bb.width / 2, -r - bb.height - 10], - south: [-r - bb.width / 2, r + 10] - }[dir]; - chart.labels.translate.apply(chart.labels, tr); - chart.push(chart.labels); - }; - if (opts.legend) { - legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos); - } - chart.push(series, covers); - chart.series = series; - chart.covers = covers; - return chart; -}; diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.raphael.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.raphael.js deleted file mode 100644 index 8e94c36cc07..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.raphael.js +++ /dev/null @@ -1,481 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ - - -(function () { - Raphael.fn.g = Raphael.fn.g || {}; - Raphael.fn.g.markers = { - disc: "disc", - o: "disc", - flower: "flower", - f: "flower", - diamond: "diamond", - d: "diamond", - square: "square", - s: "square", - triangle: "triangle", - t: "triangle", - star: "star", - "*": "star", - cross: "cross", - x: "cross", - plus: "plus", - "+": "plus", - arrow: "arrow", - "->": "arrow" - }; - Raphael.fn.g.shim = {stroke: "none", fill: "#000", "fill-opacity": 0}; - Raphael.fn.g.txtattr = {font: "12px Arial, sans-serif"}; - Raphael.fn.g.colors = []; - var hues = [.6, .2, .05, .1333, .75, 0]; - for (var i = 0; i < 10; i++) { - if (i < hues.length) { - Raphael.fn.g.colors.push("hsb(" + hues[i] + ", .75, .75)"); - } else { - Raphael.fn.g.colors.push("hsb(" + hues[i - hues.length] + ", 1, .5)"); - } - } - Raphael.fn.g.text = function (x, y, text) { - return this.text(x, y, text).attr(this.g.txtattr); - }; - Raphael.fn.g.labelise = function (label, val, total) { - if (label) { - return (label + "").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g, function (all, value, percent) { - if (value) { - return (+val).toFixed(value.replace(/^#+\.?/g, "").length); - } - if (percent) { - return (val * 100 / total).toFixed(percent.replace(/^%+\.?/g, "").length) + "%"; - } - }); - } else { - return (+val).toFixed(0); - } - }; - - Raphael.fn.g.finger = function (x, y, width, height, dir, ending, isPath) { - // dir 0 for horisontal and 1 for vertical - if ((dir && !height) || (!dir && !width)) { - return isPath ? "" : this.path(); - } - ending = {square: "square", sharp: "sharp", soft: "soft"}[ending] || "round"; - var path; - height = Math.round(height); - width = Math.round(width); - x = Math.round(x); - y = Math.round(y); - switch (ending) { - case "round": - if (!dir) { - var r = Math.floor(height / 2); - if (width < r) { - r = width; - path = ["M", x + .5, y + .5 - Math.floor(height / 2), "l", 0, 0, "a", r, Math.floor(height / 2), 0, 0, 1, 0, height, "l", 0, 0, "z"]; - } else { - path = ["M", x + .5, y + .5 - r, "l", width - r, 0, "a", r, r, 0, 1, 1, 0, height, "l", r - width, 0, "z"]; - } - } else { - var r = Math.floor(width / 2); - if (height < r) { - r = height; - path = ["M", x - Math.floor(width / 2), y, "l", 0, 0, "a", Math.floor(width / 2), r, 0, 0, 1, width, 0, "l", 0, 0, "z"]; - } else { - path = ["M", x - r, y, "l", 0, r - height, "a", r, r, 0, 1, 1, width, 0, "l", 0, height - r, "z"]; - } - } - break; - case "sharp": - if (!dir) { - var half = Math.floor(height / 2); - path = ["M", x, y + half, "l", 0, -height, Math.max(width - half, 0), 0, Math.min(half, width), half, -Math.min(half, width), half + (half * 2 < height), "z"]; - } else { - var half = Math.floor(width / 2); - path = ["M", x + half, y, "l", -width, 0, 0, -Math.max(height - half, 0), half, -Math.min(half, height), half, Math.min(half, height), half, "z"]; - } - break; - case "square": - if (!dir) { - path = ["M", x, y + Math.floor(height / 2), "l", 0, -height, width, 0, 0, height, "z"]; - } else { - path = ["M", x + Math.floor(width / 2), y, "l", 1 - width, 0, 0, -height, width - 1, 0, "z"]; - } - break; - case "soft": - var r; - if (!dir) { - r = Math.min(width, Math.round(height / 5)); - path = ["M", x + .5, y + .5 - Math.floor(height / 2), "l", width - r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r * 2, "a", r, r, 0, 0, 1, -r, r, "l", r - width, 0, "z"]; - } else { - r = Math.min(Math.round(width / 5), height); - path = ["M", x - Math.floor(width / 2), y, "l", 0, r - height, "a", r, r, 0, 0, 1, r, -r, "l", width - 2 * r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r, "z"]; - } - } - if (isPath) { - return path.join(","); - } else { - return this.path(path); - } - }; - - // Symbols - Raphael.fn.g.disc = function (cx, cy, r) { - return this.circle(cx, cy, r); - }; - Raphael.fn.g.line = function (cx, cy, r) { - return this.rect(cx - r, cy - r / 5, 2 * r, 2 * r / 5); - }; - Raphael.fn.g.square = function (cx, cy, r) { - r = r * .7; - return this.rect(cx - r, cy - r, 2 * r, 2 * r); - }; - Raphael.fn.g.triangle = function (cx, cy, r) { - r *= 1.75; - return this.path("M".concat(cx, ",", cy, "m0-", r * .58, "l", r * .5, ",", r * .87, "-", r, ",0z")); - }; - Raphael.fn.g.diamond = function (cx, cy, r) { - return this.path(["M", cx, cy - r, "l", r, r, -r, r, -r, -r, r, -r, "z"]); - }; - Raphael.fn.g.flower = function (cx, cy, r, n) { - r = r * 1.25; - var rout = r, - rin = rout * .5; - n = +n < 3 || !n ? 5 : n; - var points = ["M", cx, cy + rin, "Q"], - R; - for (var i = 1; i < n * 2 + 1; i++) { - R = i % 2 ? rout : rin; - points = points.concat([+(cx + R * Math.sin(i * Math.PI / n)).toFixed(3), +(cy + R * Math.cos(i * Math.PI / n)).toFixed(3)]); - } - points.push("z"); - return this.path(points.join(",")); - }; - Raphael.fn.g.star = function (cx, cy, r, r2) { - r2 = r2 || r * .5; - var points = ["M", cx, cy + r2, "L"], - R; - for (var i = 1; i < 10; i++) { - R = i % 2 ? r : r2; - points = points.concat([(cx + R * Math.sin(i * Math.PI * .2)).toFixed(3), (cy + R * Math.cos(i * Math.PI * .2)).toFixed(3)]); - } - points.push("z"); - return this.path(points.join(",")); - }; - Raphael.fn.g.cross = function (cx, cy, r) { - r = r / 2.5; - return this.path("M".concat(cx - r, ",", cy, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"])); - }; - Raphael.fn.g.plus = function (cx, cy, r) { - r = r / 2; - return this.path("M".concat(cx - r / 2, ",", cy - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"])); - }; - Raphael.fn.g.arrow = function (cx, cy, r) { - return this.path("M".concat(cx - r * .7, ",", cy - r * .4, "l", [r * .6, 0, 0, -r * .4, r, r * .8, -r, r * .8, 0, -r * .4, -r * .6, 0], "z")); - }; - - // Tooltips - Raphael.fn.g.tag = function (x, y, text, angle, r) { - angle = angle || 0; - r = r == null ? 5 : r; - text = text == null ? "$9.99" : text; - var R = .5522 * r, - res = this.set(), - d = 3; - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function () { - this.rotate(0, x, y); - var bb = this[1].getBBox(); - if (bb.height >= r * 2) { - this[0].attr({path: ["M", x, y + r, "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2, "m", 0, -r * 2 -d, "a", r + d, r + d, 0, 1, 0, 0, (r + d) * 2, "L", x + r + d, y + bb.height / 2 + d, "l", bb.width + 2 * d, 0, 0, -bb.height - 2 * d, -bb.width - 2 * d, 0, "L", x, y - r - d].join(",")}); - } else { - var dx = Math.sqrt(Math.pow(r + d, 2) - Math.pow(bb.height / 2 + d, 2)); - // ["c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r] - // "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2, - this[0].attr({path: ["M", x, y + r, "c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r, "M", x + dx, y - bb.height / 2 - d, "a", r + d, r + d, 0, 1, 0, 0, bb.height + 2 * d, "l", r + d - dx + bb.width + 2 * d, 0, 0, -bb.height - 2 * d, "L", x + dx, y - bb.height / 2 - d].join(",")}); - } - this[1].attr({x: x + r + d + bb.width / 2, y: y}); - angle = (360 - angle) % 360; - this.rotate(angle, x, y); - angle > 90 && angle < 270 && this[1].attr({x: x - r - d - bb.width / 2, y: y, rotation: [180 + angle, x, y]}); - return this; - }; - res.update(); - return res; - }; - Raphael.fn.g.popupit = function (x, y, set, dir, size) { - dir = dir == null ? 2 : dir; - size = size || 5; - x = Math.round(x) + .5; - y = Math.round(y) + .5; - var bb = set.getBBox(), - w = Math.round(bb.width / 2), - h = Math.round(bb.height / 2), - dx = [0, w + size * 2, 0, -w - size * 2], - dy = [-h * 2 - size * 3, -h - size, 0, -h - size], - p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, - "l", 0, -Math.max(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -Math.max(h - size, 0), "a", size, size, 0, 0, 1, size, -size, - "l", Math.max(w - size, 0), 0, size, !dir * -size, size, !dir * size, Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, - "l", 0, Math.max(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, Math.max(h - size, 0), "a", size, size, 0, 0, 1, -size, size, - "l", -Math.max(w - size, 0), 0, "z"].join(","), - xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir]; - set.translate(xy.x - w - bb.x, xy.y - h - bb.y); - return this.path(p).attr({fill: "#000", stroke: "none"}).insertBefore(set.node ? set : set[0]); - }; - Raphael.fn.g.popup = function (x, y, text, dir, size) { - dir = dir == null ? 2 : dir; - size = size || 5; - text = text || "$9.99"; - var res = this.set(), - d = 3; - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function (X, Y, withAnimation) { - X = X || x; - Y = Y || y; - var bb = this[1].getBBox(), - w = bb.width / 2, - h = bb.height / 2, - dx = [0, w + size * 2, 0, -w - size * 2], - dy = [-h * 2 - size * 3, -h - size, 0, -h - size], - p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, - "l", 0, -Math.max(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -Math.max(h - size, 0), "a", size, size, 0, 0, 1, size, -size, - "l", Math.max(w - size, 0), 0, size, !dir * -size, size, !dir * size, Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, - "l", 0, Math.max(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, Math.max(h - size, 0), "a", size, size, 0, 0, 1, -size, size, - "l", -Math.max(w - size, 0), 0, "z"].join(","), - xy = [{x: X, y: Y + size * 2 + h}, {x: X - size * 2 - w, y: Y}, {x: X, y: Y - size * 2 - h}, {x: X + size * 2 + w, y: Y}][dir]; - if (withAnimation) { - this[0].animate({path: p}, 500, ">"); - this[1].animate(xy, 500, ">"); - } else { - this[0].attr({path: p}); - this[1].attr(xy); - } - return this; - }; - return res.update(x, y); - }; - Raphael.fn.g.flag = function (x, y, text, angle) { - angle = angle || 0; - text = text || "$9.99"; - var res = this.set(), - d = 3; - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function (x, y) { - this.rotate(0, x, y); - var bb = this[1].getBBox(), - h = bb.height / 2; - this[0].attr({path: ["M", x, y, "l", h + d, -h - d, bb.width + 2 * d, 0, 0, bb.height + 2 * d, -bb.width - 2 * d, 0, "z"].join(",")}); - this[1].attr({x: x + h + d + bb.width / 2, y: y}); - angle = 360 - angle; - this.rotate(angle, x, y); - angle > 90 && angle < 270 && this[1].attr({x: x - r - d - bb.width / 2, y: y, rotation: [180 + angle, x, y]}); - return this; - }; - return res.update(x, y); - }; - Raphael.fn.g.label = function (x, y, text) { - var res = this.set(); - res.push(this.rect(x, y, 10, 10).attr({stroke: "none", fill: "#000"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function () { - var bb = this[1].getBBox(), - r = Math.min(bb.width + 10, bb.height + 10) / 2; - this[0].attr({x: bb.x - r / 2, y: bb.y - r / 2, width: bb.width + r, height: bb.height + r, r: r}); - }; - res.update(); - return res; - }; - Raphael.fn.g.labelit = function (set) { - var bb = set.getBBox(), - r = Math.min(20, bb.width + 10, bb.height + 10) / 2; - return this.rect(bb.x - r / 2, bb.y - r / 2, bb.width + r, bb.height + r, r).attr({stroke: "none", fill: "#000"}).insertBefore(set[0]); - }; - Raphael.fn.g.drop = function (x, y, text, size, angle) { - size = size || 30; - angle = angle || 0; - var res = this.set(); - res.push(this.path(["M", x, y, "l", size, 0, "A", size * .4, size * .4, 0, 1, 0, x + size * .7, y - size * .7, "z"]).attr({fill: "#000", stroke: "none", rotation: [22.5 - angle, x, y]})); - angle = (angle + 90) * Math.PI / 180; - res.push(this.text(x + size * Math.sin(angle), y + size * Math.cos(angle), text).attr(this.g.txtattr).attr({"font-size": size * 12 / 30, fill: "#fff"})); - res.drop = res[0]; - res.text = res[1]; - return res; - }; - Raphael.fn.g.blob = function (x, y, text, angle, size) { - angle = (+angle + 1 ? angle : 45) + 90; - size = size || 12; - var rad = Math.PI / 180, - fontSize = size * 12 / 12; - var res = this.set(); - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x + size * Math.sin((angle) * rad), y + size * Math.cos((angle) * rad) - fontSize / 2, text).attr(this.g.txtattr).attr({"font-size": fontSize, fill: "#fff"})); - res.update = function (X, Y, withAnimation) { - X = X || x; - Y = Y || y; - var bb = this[1].getBBox(), - w = Math.max(bb.width + fontSize, size * 25 / 12), - h = Math.max(bb.height + fontSize, size * 25 / 12), - x2 = X + size * Math.sin((angle - 22.5) * rad), - y2 = Y + size * Math.cos((angle - 22.5) * rad), - x1 = X + size * Math.sin((angle + 22.5) * rad), - y1 = Y + size * Math.cos((angle + 22.5) * rad), - dx = (x1 - x2) / 2, - dy = (y1 - y2) / 2, - rx = w / 2, - ry = h / 2, - k = -Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * dy * dy - ry * ry * dx * dx) / (rx * rx * dy * dy + ry * ry * dx * dx)), - cx = k * rx * dy / ry + (x1 + x2) / 2, - cy = k * -ry * dx / rx + (y1 + y2) / 2; - if (withAnimation) { - this.animate({x: cx, y: cy, path: ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2, "z"].join(",")}, 500, ">"); - } else { - this.attr({x: cx, y: cy, path: ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2, "z"].join(",")}); - } - return this; - }; - res.update(x, y); - return res; - }; - - Raphael.fn.g.colorValue = function (value, total, s, b) { - return "hsb(" + [Math.min((1 - value / total) * .4, 1), s || .75, b || .75] + ")"; - }; - - Raphael.fn.g.snapEnds = function (from, to, steps) { - var f = from, - t = to; - if (f == t) { - return {from: f, to: t, power: 0}; - } - function round(a) { - return Math.abs(a - .5) < .25 ? Math.floor(a) + .5 : Math.round(a); - } - var d = (t - f) / steps, - r = Math.floor(d), - R = r, - i = 0; - if (r) { - while (R) { - i--; - R = Math.floor(d * Math.pow(10, i)) / Math.pow(10, i); - } - i ++; - } else { - while (!r) { - i = i || 1; - r = Math.floor(d * Math.pow(10, i)) / Math.pow(10, i); - i++; - } - i && i--; - } - var t = round(to * Math.pow(10, i)) / Math.pow(10, i); - if (t < to) { - t = round((to + .5) * Math.pow(10, i)) / Math.pow(10, i); - } - var f = round((from - (i > 0 ? 0 : .5)) * Math.pow(10, i)) / Math.pow(10, i); - return {from: f, to: t, power: i}; - }; - Raphael.fn.g.axis = function (x, y, length, from, to, steps, orientation, labels, type, dashsize) { - dashsize = dashsize == null ? 3 : dashsize; - type = type || "t"; - steps = steps || 10; - var path = type == "|" || type == " " ? ["M", x + .5, y, "l", 0, .001] : orientation == 1 || orientation == 3 ? ["M", x + .5, y, "l", 0, -length] : ["M", x, y + .5, "l", length, 0], - ends = this.g.snapEnds(from, to, steps), - f = ends.from, - t = ends.to, - i = ends.power, - j = 0, - text = this.set(); - d = (t - f) / steps; - var label = f, - rnd = i > 0 ? i : 0; - dx = length / steps; - if (+orientation == 1 || +orientation == 3) { - var Y = y, - addon = (orientation - 1 ? 1 : -1) * (dashsize + 3 + !!(orientation - 1)); - while (Y >= y - length) { - type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), Y + .5, "l", dashsize * 2 + 1, 0])); - text.push(this.text(x + addon, Y, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr).attr({"text-anchor": orientation - 1 ? "start" : "end"})); - label += d; - Y -= dx; - } - if (Math.round(Y + dx - (y - length))) { - type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), y - length + .5, "l", dashsize * 2 + 1, 0])); - text.push(this.text(x + addon, y - length, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr).attr({"text-anchor": orientation - 1 ? "start" : "end"})); - } - } else { - var X = x, - label = f, - rnd = i > 0 ? i : 0, - addon = (orientation ? -1 : 1) * (dashsize + 9 + !orientation), - dx = length / steps, - txt = 0, - prev = 0; - while (X <= x + length) { - - text.push(txt = this.text(X, y + addon, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr)); - var bb = txt.getBBox(); - var ds = dashsize; - if (prev >= bb.x - 5) { - text.pop(text.length - 1).remove(); - ds = 1; - } else { - prev = bb.x + bb.width; - } - - type != "-" && type != " " && (path = path.concat(["M", X + .5, y - (type == "+" ? ds : !!orientation * ds * 2), "l", 0, ds * 2 + 1])); - - label += d; - X += dx; - } - if (Math.round(X - dx - x - length)) { - type != "-" && type != " " && (path = path.concat(["M", x + length + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1])); - text.push(this.text(x + length, y + addon, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr)); - } - } - var res = this.path(path); - res.text = text; - res.all = this.set([res, text]); - res.remove = function () { - this.text.remove(); - this.constructor.prototype.remove.call(this); - }; - return res; - }; - - Raphael.el.lighter = function (times) { - times = times || 2; - var fs = [this.attrs.fill, this.attrs.stroke]; - this.fs = this.fs || [fs[0], fs[1]]; - fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex); - fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex); - fs[0].b = Math.min(fs[0].b * times, 1); - fs[0].s = fs[0].s / times; - fs[1].b = Math.min(fs[1].b * times, 1); - fs[1].s = fs[1].s / times; - this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"}); - }; - Raphael.el.darker = function (times) { - times = times || 2; - var fs = [this.attrs.fill, this.attrs.stroke]; - this.fs = this.fs || [fs[0], fs[1]]; - fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex); - fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex); - fs[0].s = Math.min(fs[0].s * times, 1); - fs[0].b = fs[0].b / times; - fs[1].s = Math.min(fs[1].s * times, 1); - fs[1].b = fs[1].b / times; - this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"}); - }; - Raphael.el.original = function () { - if (this.fs) { - this.attr({fill: this.fs[0], stroke: this.fs[1]}); - delete this.fs; - } - }; -})(); \ No newline at end of file diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/load-big.gif b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/load-big.gif deleted file mode 100644 index ddb7ff1aac1b2fc825667c157873c2c55bbe8db8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1924 zcmciDX;70#00!U)Urs41ND>$3-?7cZa-C@o`?~}E+$na_sVK~an51Y7o6Am#OJGNBi-|v-0K)zTz!9cZZmYYj zeiQ^4Hx2}<@9us*(zz1hB8-YDCx|yPvMXzQM{_K$!h*iJ(7x2j*bY>E)v;gG zt15c?^oIWX1BxEuJ?E}-N%%X2$C&X)nYafzWBB27BZK9$hH1k@-(v5=%efcJKAYA8 zj{X%G-GGGnp>xBKmtR{qyXkbXi>i|w&^A+i%*o~apB=fnp1EvhyA8i)zT0!ZJK|=f zGujFu=8Q?f@YHB=vJ&Q(Q2ooMB$CxnsQ@;Cl88SakbL=c9-bR}Cd`4o_e-(kkx-G` z1gxmM5nm)f#c{~lR_t33s5hr)1~uEaw)kjKO}BP*)>VmXx>RQ1;1H?TbtLMnaSW#; zj9+XgO*q{nKEpjQb`O`&-Jk8B)-S0R`xoZlkUo^CY4otNIEG*Z>9xBj^;cd~XE!if z=JK3SR%Kh!+&qgwp0nLox=TA*jYs?se%u2xUeoK&1u^$dFrFz~jrhLs@UeBBdpx|M zY)D3CZeBjWKwtxdli{#JJ47_Xo<)bn10rN1&s7)?M_u%xDH4!1ac=bhs)DA9u4+yx zQz*rfNSWBSxTLdtwZlQ%QWxV{Y9-?#zL7M}ttCzze@xWkN1wM3@#hRCqG74qOeC&J z9C^RRzb6jvbS)W0Nd^=G0Xde#v6Z!xx5H+VUZrNmDp2GD$ZAyrrw%!G2FskYEUNL97Eu}MQR$IyrnBkg(h1rW4yIQEM3J@S3&;1KqjvtHc)u##|B-p#vH)WPhuYi^mQjj0C~N7S!=n7H-h z)-{F0|AmmcM>roo8TLt*J%^c#&ZiY1FVl9S>(#+iza>x4q)s+Z@gD1* z@P^eR(w@FoxHWz6T{O`R|GvS0@PW)Hmm#QRgaoFdRCosCuGa!^PcBA73HQUcl65Dr zO|`ePK2pY_S=Cuk0cLN5NYeIMmj@%E-?ilgU&#f%3S_8AdaDu z`F^)`6_9BI%cf+|5|L?q0A9#NmQZLMl$0#@LkamJueh-M5`ik%{!(^MZk{k*qitzz zdsl7?nA3Imb6iI@->x5 zwXc~EZZLz-aKH4hJ%XN<9U8*$f;BXGrEQ^o=8paJ`*iQfV=)}tj7M$B9-E(t0}WI5 ziiH<<@gb})LXWcd=N1SGi;6?d9SD)&II^XLelX2e#DZKujgFz0`cy~Oo<~(uRW#l% zG_=kVdRXZIt%P()d)_@2TRL5aa4qGGD&I4tJNR+p1a_1(jW-f!2MXqTpXs0WExsHx Pn1F;$An(;T&^-PCGf5vc diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/load.gif b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/load.gif deleted file mode 100644 index d0bce1542342e912da81a2c260562df172f30d73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nnmm28Kh24mmkF0U1e2Nli^nlO|14{Lk&@8WQa67~pE8 zXTZz|lvDgC+Z`3#dv5h=E26FfcG1 zbL_hF&)}42ws10s6^G;;cE1^EoUR)U5A70}d2pLv!jVIT7j&Z~EblI3x0K*v_sV|m z0kj3v921Z^em#l`(k(o@H$3ZdDRc@9NidXDNbqrumReCGv$gd8+e8WW28HVqkJ_9i zH>s*<31KtHjANIPvi2#*6BEu%3Dak5O_t&NBI)H?V$TxT}#l{vOTn5naXTfF^&~Hhq+NX@#Ccc>y7T?;vjI&jdhsDsPJyAw*m0Qz>i}K7# zL9w50Ng{fT}A5JUe8lRK1h7_Y2;BWJDd=c6f&i?Wv5(5q?6|P zQw{>maxZP<537OA37Uk}7@%_$4o$EWe_Zl>&#id|lE-BpDC#+Fn|msJ%_2h{Hg1vP z#N8WAzfWasG}yq|xqE)DrWaOofX=z|?*pgc%{ig5vl!pqDlC|q&~Z0$&Rvsft&VO- z4MZj+%-+Vx%W}v;V76hyp=;+R;x+~t^Q%*xuFTQAF2})fSfTHDAs>sO!OBw`)&)o$ c0!CNZt))x~rAZP^^P&YOFfdqy5)K#u0POD40{{R3 diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.css b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.css deleted file mode 100644 index a84d90e07c4..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.css +++ /dev/null @@ -1,54 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -body { font-family: sans-serif; } - -div.fileSelector { border: solid 3px black; position: absolute; background: white; - -moz-border-radius: 10px; border-radius: 10px; - padding: 5px; font-family: sans-serif; - right: 10px; top: 10px; - } -div.fileSelector a { cursor: pointer; } -.fileSelector li.selectedFile { background: lightgreen; } - -div.selector { border: solid 3px black; position: absolute; background: white; - -moz-border-radius: 10px; border-radius: 10px; - padding: 5px; - right: 10px; top: 10px; background: #aaaaaa; opacity: 0.7; - } -div.selector a { cursor: pointer; } -.fileSelector li.selectedFile { background: lightgreen; } - -#fileLoader { -moz-border-radius: 10px; border-radius: 10px; background: #aaaaaa; opacity: 0.7; position: absolute; left: 20px; top: 20px; } -#loadingScreen { position: absolute; top: 100px; margin-left: 40%; margin-right: 40%; width: 500px; background: #aaaaaa; opacity: 0.7; -moz-border-radius: 10px; border-radius: 10px; text-align: center } -#filterinput { width: 500px; height: 100px; } -/* main interface */ -#actions { float: right; } -#views { float: left; } - -.closebutton { position: absolute; right: 5px; float: right; display: block; cursor: pointer; } - -.actionbutton { color: blue; text-decoration: none; padding: 3px; cursor: pointer; } -span:hover.actionbutton { background: lightblue; } - -#status { text-align: center; } - -#canvas { width: 100%; height: 1000px; } - -#logtable { width: 100%; } -.popUp { border: 3px solid black; -moz-border-radius: 10px; border-radius: 10px; position: absolute; background: white; padding: 10px; min-width: 300px; } - -.errorpage { position: absolute; top: 100px; margin-left: 40%; margin-right: 40%; width: 500px; background: #aaaaaa; opacity: 0.7; -moz-border-radius: 10px; border-radius: 10px; padding: 10px; } \ No newline at end of file diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.js deleted file mode 100644 index 87bb7d89da2..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph = function(canvas, status) { - this.canvas = document.getElementById(canvas); - this.status = document.getElementById(status); - this.starttime = 0; - this.endtime = 0; - this.period = 0; - this.numEntries = 0; - this.currentRender = 0; - this.filter = ""; - - this.saveFilters = function () { - localStorage.starttime = this.starttime; - localStorage.endtime = this.endtime; - localStorage.period = this.period; - localStorage.filter = this.filter; - - }; - this.loadFilters = function () { - if (localStorage.starttime) { this.starttime = parseInt(localStorage.starttime); } - if (localStorage.endtime) { this.endtime = parseInt(localStorage.endtime); } - if (localStorage.period) { this.period = parseInt(localStorage.period); } - if (localStorage.filter) { this.filter = localStorage.filter; } - }; - this.loadFilters(); - var self = this; - - var updateStatus = function (starttime, period, filter, numEntries) { - self.starttime = starttime; - self.endtime = starttime + period; - self.period = period; - self.filter = filter; - self.saveFilters(); - - self.status.innerHTML = dateFormat(starttime, "HH:MM:ss,l") + " ⇒ " + dateFormat(self.endtime, "HH:MM:ss,l") + "    |    " + numEntries + " entries    |    " + (filter ? filter : "No filter"); - - if (self.currentRender) { - self.currentRender(); - } - }; - - YUI().use("io-base", function(Y) { - var uri = "/info"; - if (self.starttime) { - var uri = "/info?start=" + self.starttime + "&period=" + self.period + "&filter=" + self.filter; - } - - function complete(id, o, args) { - var data = eval("(" + o.responseText + ")"); // Response data. - var period = data.endTime - data.startTime; - updateStatus(data.startTime, period, self.filter, data.numEntries); - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - - this.addLogs = function() { - new LogGraph.fileSelector(function (files) { new LogGraph.fileLoader(files); }); - }; - - this.editFilters = function() { - new LogGraph.filterSelector(this.starttime, this.period, this.filter, updateStatus); - }; - - this.getCleanCanvas = function () { - this.canvas.innerHTML = ""; - return this.canvas; - }; - - this.showLoadingScreen = function () { - this.loadingScreen = document.createElement("div"); - this.loadingScreen.id = "loadingScreen"; - this.loadingScreen.innerHTML = "

Loading...

"; - document.body.appendChild(this.loadingScreen); - }; - - this.hideLoadingScreen = function () { - document.body.removeChild(this.loadingScreen); - this.loadingScreen.style.visibility = "hidden"; - }; - - - /*** - * TODO: refactor these to load the data first, before handing to a draw funciton. - * We shouldn't pass the async q into the drawing function - */ - this.showLogs = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var loggraph = new LogGraph.LogTable(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.showLogs; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; - - this.serverGraph = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var servergraph = new LogGraph.ServerGraph(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.showLogs; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; - - this.sessionGraph = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var sessiongraph = new LogGraph.SessionGraph(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.sessionGraph; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; - - this.showStats = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var statgraph = new LogGraph.StatsGraph(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.showStats; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; -}; - -LogGraph.error = function(description) { - var errorPage = document.createElement("div"); - errorPage.className = "errorpage"; - var p = document.createElement("p"); - p.innerHTML = description; - errorPage.appendChild(p); - - var span = document.createElement("span"); - p = document.createElement("p"); - span.className = "actionButton"; - span.innerHTML = "OK"; - span.onclick = function (evt) { - document.body.removeChild(errorPage); - delete errorPage; - } - p.appendChild(span); - errorPage.appendChild(p); - - document.body.appendChild(errorPage); -}; - -LogGraph.ticker =function(allow_dups) { - this.ticks = new Array(); - this.current_tick = 0; - this.allow_dups = allow_dups;; - - this.tick = function(time) { - if (time == this.ticks[this.ticks.length - 1] && this.allow_dups == true) - return this.current_tick; - - this.ticks.push(time); - return this.current_tick++; - }; - - this.current = function() { - return this.current_tick; - }; - - this.reset = function() { - while (this.ticks.length) { - this.ticks.pop(); - } - this.current_tick = 0; - }; -}; - - -LogGraph.timescale = function(starttime, endtime) { - this.starttime = starttime; - this.endtime = endtime; - this.millis = endtime - starttime; - - this.draw = function(paper) { - var scale = paper.set(); - scale.push(paper.path("M0 0 L" + paper.width + " 0")); - - for (var i = 0; i < paper.width; i += 100) { - scale.push(paper.path("M" + i + " 0 L" + i + " 5")); - // var time = dateFormat((this.starttime + (i*ms_per_pixel)), "h:MM:ss,l"); - // paper.text(i + 5, 10, time); - } - - scale.attr({"stroke-width": 2}); - }; -}; - -/* - Fetch data from an uri and process it, the process data func returns true if any of the data is useful -*/ -LogGraph.loadData = function (asyncq, uri, processdata) { - YUI().use("io-base", function(Y) { - function success(id, o, args) { - var data = eval("(" + o.responseText + ")"); // Response data. - if (data.error) { - LogGraph.error(data.error); - } else { - if (!processdata(data)) { - LogGraph.error("No data. Perhaps you should loosen your filter criteria."); - } - } - asyncq.run(); - }; - function failure(id, o, args) { - LogGraph.error("Error contacting server: (" + o.status + ") " + o.statusText); - asyncq.run(); - }; - - Y.on('io:success', success, Y, []); - Y.on('io:failure', failure, Y, []); - - var request = Y.io(uri); - }); -} \ No newline at end of file diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.log.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.log.js deleted file mode 100644 index 551ea4b2373..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.log.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.LogTable = function (asyncq, canvas, starttime, endtime, filter) { - this.starttime = starttime; - this.endtime = endtime; - this.filter = filter; - - var table = document.createElement("table"); - table.id = "logtable"; - canvas.appendChild(table); - - this.addLogLine = function(time, text) { - var tr = document.createElement("tr"); - table.appendChild(tr); - - var td = document.createElement("td"); - td.innerHTML = dateFormat(time, "h:MM:ss,l"); - tr.appendChild(td); - - td = document.createElement("td"); - td.innerHTML = text; - tr.appendChild(td); - } - - var self = this; - var processdata = function(data) { - var events = data["events"]; - var count = 0; - for (var i in events) { - var e = events[i]; - if (e.type == "text") { - self.addLogLine(e.time, e.text); - count++; - } - } - return count != 0; - }; - - var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + self.filter; - LogGraph.loadData(asyncq, uri, processdata); -}; diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.server.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.server.js deleted file mode 100644 index 0a74b5c24b8..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.server.js +++ /dev/null @@ -1,329 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.ServerGraph = function(asyncq, canvas, starttime, endtime, filter) { - this.starttime = starttime; - this.endtime = endtime; - this.millis = endtime - starttime; - this.nextserverid = 0; - this.serveroffset = 100; - this.filter = filter; - - this.pixels_per_tick = 20; - this.ticker = new LogGraph.ticker(); - - - var paper = Raphael(canvas, 1, 1); - - var self = this; - - this.timescale = new LogGraph.timescale(starttime, endtime); - this.objects = new Array(); - - this.add = function(obj) { - this.objects.push(obj); - } - - this.tick_to_x = function (timestamp) { - var x = timestamp * this.pixels_per_tick; - return x; - }; - - this._drawTime = function(paper, x, time) { - var p = paper.path("M" + x + " 0 L" + x + " " + paper.height); - var t = paper.text(x, 10, dateFormat(time, "h:MM:ss,l")); - - t.hide(); - p.mouseover(function(evt) { - t.show(); - p.attr({stroke: "red"}); - }); - p.mouseout(function(evt) { - t.hide(); - p.attr({stroke: "lightgray"}); - }); - - return p; - }; - - this.draw = function(paper) { - var grid = paper.set(); - for (var i = 0; i < paper.height; i += 20) { - grid.push(paper.path("M0 " + i + " L" + paper.width + " " + i)); - } - var lasttick = this.starttime; - var scale = 500; // 500 ms - - var y = 0; - - for (var t = 0, len = this.ticker.ticks.length; t < len; t++) { - var basex = t * this.pixels_per_tick; - var thistick = this.ticker.ticks[t]; - var nexttick = t + 1 == this.ticker.ticks.length ? this.endtime : this.ticker.ticks[t+1]; - if (nexttick == thistick) { - continue; - } - var time = thistick - lasttick; - var first = scale - (lasttick % scale); - - /* for (var i = 0; (first+scale*i) < time; i++) { - - var toffset = first+scale*i; - var x = basex + LogGraph._pixels_per_tick * toffset/time; - grid.push(this._drawTime(paper, x, lasttick + toffset, grid)); - - }*/ - - - //grid.push(paper.path("M" + i + " 0 L" + i + " " + paper.height)); - lasttick = thistick; - } - grid.attr({stroke: "lightgray"}); - this.timescale.draw(paper); - - for (o in this.objects) { - this.objects[o].draw(paper); - } - }; - - - var processdata = function(data) { - var servermap = {}; - var servers = data.servers; - var count = 0; - for (s in servers) { - var server = new LogGraph.ServerGraph.server(self, "Server " + servers[s]); - servermap[servers[s]] = server; - self.add(server); - count++; - } - - var messages = {}; - var events = data.events; - for (var i in events) { - var e = events[i]; - var t = e.time; - if (e.type == "stateChange") { - servermap[e.server].addState(e.state, self.ticker.tick(e.time)); - } - if (e.type == "postmessage") { - src = servermap[e.src]; - dst = servermap[e.dst]; - var key = "key:s" + e.src + ",d" + e.dst + ",z" + e.zxid; - - var m = new LogGraph.ServerGraph.message(self, src, self.ticker.tick(e.time), dst, e.zxid); - messages[key] = m; - } - if (e.type == "delivermessage") { - var key = "key:s" + e.src + ",d" + e.dst + ",z" + e.zxid; - - var m = messages[key]; - if (m) { - m.dsttime = self.ticker.tick(e.time); - m.name = "Propose"; - self.add(m); - delete messages[key]; - } - } - if (e.type == "exception") { - servermap[e.server].addException(self.ticker.tick(e.time), e.text, e.time); - } - count++; - } - - for (var i in messages) { - var m = messages[i]; - m.markIncomplete(); - self.add(m); - count++; - } - - if (count != 0) { - paper.setSize(self.tick_to_x(self.ticker.current()), 1000); - - var line = paper.path("M0 0 L0 1000"); - line.attr({"stroke": "red", "stroke-dasharray": "- "}); - var base = canvas.offsetLeft;// + ((canvas.offsetWidth - paper.width)/2); - canvas.onmousemove = function (evt) { - var x = evt.screenX - base; - - line.attr({"path": "M" + x + " 0 L"+ x +" 1000"}); - - }; - - self.draw(paper); - return true; - } else { - return false; - } - }; - - var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + filter; - - LogGraph.loadData(asyncq, uri, processdata); -}; - -LogGraph.ServerGraph.server = function (graph, name) { - this.graph = graph; - this.serverid = graph.nextserverid++; - this.name = name; - this.y = (this.serverid * 300 + graph.serveroffset); - this.states = new Array(); - this.exception = new Array(); - - this.addState = function(state, time) { - this.states.push([state, time]); - } - - this.addException = function(tick, exception, time) { - this.exception.push(new LogGraph.ServerGraph.exception(this.graph, tick, exception, time)); - } - - this.draw = function(paper) { - var st = paper.set(); - st.push(paper.path("M0 " + this.y + " L" + paper.width + " " + this.y)); - st.push(paper.text(20, this.y - 10, this.name)); - st.attr({stroke: "gray"}); - - var numstates = this.states.length; - - for (s = 0; s < numstates; s++) { - var style = {}; - switch (this.states[s][0]) { - case "INIT": style = {stroke: "yellow", "stroke-width":3}; break; - case "FOLLOWING": style = {stroke: "lightgreen", "stroke-width":7}; break; - case "LEADING": style = {stroke: "green", "stroke-width":10}; break; - case "LOOKING": style = {stroke: "orange", "stroke-width":5}; break; - } - var startx = this.graph.tick_to_x(this.states[s][1]); - var endx = s + 1 < numstates ? this.graph.tick_to_x(this.states[(s+1)][1]) : paper.width; - var p = paper.path("M" + startx + " " + this.y + " L" + endx + " " + this.y); - p.attr(style); - } - - for (e in this.exception) { - this.exception[e].draw(paper, this); - } - } -}; - -LogGraph.ServerGraph.message = function(graph, src, srctime, dst, zxid) { - this.graph = graph; - this.src = src; - this.srctime = srctime; - this.dst = dst; - this.dsttime = 0; //dsttime; - this.name = "Unknown"; - this.zxid = zxid; - this.moreinfo = "No extra information"; - this.incomplete = false; - - this.markIncomplete = function() { - this.incomplete = true; - this.dsttime = this.srctime; - } - - this.draw = function(paper) { - var srcx = this.graph.tick_to_x(this.srctime); - var dstx = this.graph.tick_to_x(this.dsttime); - - var arrow = paper.set(); - var p = paper.path("M" + srcx + " " + this.src.y + " L" + dstx + " " + this.dst.y); - arrow.push(p); - - var tx = (srcx + dstx)/2; - var ty = (this.src.y + this.dst.y)/2; - var t = paper.text(tx, ty, this.name); - - var gradiant = (this.dst.y - this.src.y)/(dstx - srcx); - var angle = Math.atan(gradiant) * 57.2958; - t.rotate(angle, true); - - var arrowl = paper.path("M" + dstx + " " + this.dst.y + " L" + (dstx - 10) +" " + this.dst.y); - arrowl.rotate(angle + 20, dstx, this.dst.y); - arrow.push(arrowl); - var arrowr = paper.path("M" + dstx + " " + this.dst.y + " L" + (dstx - 10) +" " + this.dst.y); - arrowr.rotate(angle - 20, dstx, this.dst.y); - arrow.push(arrowr); - - arrow.attr({"stroke-width": 2, stroke: "gray"}); - if (this.incomplete) { - arrow.attr({"stroke-dasharray": "- .", stroke: "pink", "stroke-width": 2}); - } - arrow.mouseover(function(evt) { - t.attr({"font-size": 20}); - arrow.attr({stroke: "red", "stroke-width": 3}); - }); - arrow.mouseout(function(evt) { - t.attr({"font-size": 10}); - - if (this.incomplete) { - arrow.attr({stroke: "pink", "stroke-width": 2}); - } else { - arrow.attr({stroke: "gray", "stroke-width": 2}); - } - }); - - - - arrow.click(function(evt) { - var popup = document.createElement("div"); - popup.className = "popUp"; - popup.innerHTML = "zxid: " + parseInt(this.zxid).toString(16); - - popup.style.top = evt.clientY; - popup.style.left = evt.clientX; - document.body.appendChild(popup); - - popup.onclick = function(evt) { - document.body.removeChild(popup); - }; - }); - } -}; - -LogGraph.ServerGraph.exception = function(graph, tick, exceptiontext, time) { - this.graph = graph; - this.time = time; - this.text = exceptiontext; - this.tick = tick; - - var self = this; - - this.draw = function(paper, server) { - var center = this.graph.tick_to_x(this.tick); - var p = paper.circle(center, server.y, 5); - p.attr({stroke: "orange", fill: "red"}); - - p.mouseover(function(evt) { - p.popup = document.createElement("div"); - p.popup.className = "popUp"; - p.popup.innerHTML = self.text.replace("\n", "
");; - p.popup.style.top = server.y + 50; - p.popup.style.left = center + 25; - document.body.appendChild(p.popup); - - p.animate({r: 10}, 500, "elastic"); - }); - p.mouseout(function(evt) { - document.body.removeChild(p.popup); - p.animate({r: 5}, 100); - }); - } -}; - diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.session.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.session.js deleted file mode 100644 index 5a314d8a120..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.session.js +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.SessionGraph = function (asyncq, canvas, starttime, endtime, filter) { - this.sessions = new Array(); - this.counter = 0; - this.exceptions = new Array(); - - this.pix_per_ticks = 4; - this.pix_per_session = 7; - - var paper = Raphael(canvas, 1, 1); - this.ticker = new LogGraph.ticker(); - var self = this; - - this.starttime = starttime; - this.endtime = endtime; - this.filter = filter; - - this.findOrCreateSession = function(id) { - if (this.sessions[id] == undefined) { - this.sessions[id] = new LogGraph.SessionGraph.session(this, ++this.counter, id); - } - return this.sessions[id]; - } - - this.height = function () { return this.counter * this.pix_per_session + 10; }; - this.width = function () { return (self.ticker.current() * this.pix_per_ticks); }; - - this.draw = function(paper) { - - - var line = paper.path("M0 0 L0 " + this.height()); - line.attr({"stroke": "red", "stroke-dasharray": "- "}); - var base = canvas.offsetLeft; - var width = this.width(); - canvas.onmousemove = function (evt) { - var x = evt.clientX - base; - - line.attr({"path": "M" + x + " 0 L" + x + " " + self.height() }); - }; - - for (var i in this.sessions) { - var s = this.sessions[i]; - s.draw(paper); - } - }; - - var processdata = function(data) { - var count = 0; - for (var i in data.events) { - var e = data.events[i]; - if (e.type == "transaction") { - e.tick = self.ticker.tick(e.time, true); - var session = self.findOrCreateSession(e.client); - session.addEvent(e); - count++; - } - } - paper.setSize(self.width(), self.height()); - - if (count != 0) { - self.draw(paper); - return true; - } else { - return false; - } - }; - - var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + filter; - - LogGraph.loadData(asyncq, uri, processdata); -}; - -LogGraph.SessionGraph.sessionevent = function () { - this.time = time; - this.type = type; - this.client = client; - this.cxid = cxid; - this.zxid = zxid; - this.op = op; - this.extra = extra; -}; - -LogGraph.SessionGraph.sessionEventPopup = function (obj, e, x, y) { - obj.click(function(evt) { - var popup = document.createElement("div"); - popup.className = "popUp"; - - var closebutton = document.createElement("div"); - closebutton.className = "closebutton"; - closebutton.title = "Close popup"; - closebutton.innerHTML = "×"; - popup.appendChild(closebutton); - closebutton.onclick= function(evt) { popup.style.visibility = "hidden"; document.body.removeChild(popup) }; - var txt = document.createElement("span"); - txt.innerHTML = "session: " + e.client + "
op: " + e.op + "
zxid: " + e.zxid + "
time: " + e.time + "
extra: " + e.extra; - popup.appendChild(txt); - - popup.style.top = y; - popup.style.left = x; - document.body.appendChild(popup); - - YUI().use('dd-drag', function(Y) { - //Selector of the node to make draggable - var dd = new Y.DD.Drag({ - node: popup - }); - }); - }); -}; - -LogGraph.SessionGraph.session = function (graph, index, id) { - this.index = index; - this.id = id; - this.graph = graph; - - this.events = new Array(); - this.starttick = 0; - this.endtick = undefined; - - this.addEvent = function(e) { - this.events.push(e); - - if (e.op == "createSession") { - // document.write("createSession for " + id.toString(16)); - this.starttick = e.tick; - } else if (e.op == "closeSession") { - this.endtick = e.tick; - } - }, - - this._attach_action = function (sess, label) { - sess.mouseover(function(evt) { - label.show(); - sess.attr({stroke: "gray"}); - }); - - sess.mouseout(function(evt) { - label.hide(); - sess.attr({stroke: "black"}); - }); - }, - - this.drawEvent = function (paper, y, e) { - var x = e.tick * this.graph.pix_per_ticks;; - var s = paper.path("M" + x + " " + (y - 3) + " L" + x + " " + (y + 3)); - s.attr({"stroke-width": 2}); - if (e.op == "error") { - s.attr({"stroke": "red"}); - } - s.mouseover(function(evt) { - s.attr({"stroke-width": 5}); - }); - - s.mouseout(function(evt) { - s.attr({"stroke-width": 2}); - }); - - LogGraph.SessionGraph.sessionEventPopup(s, e, x, y); - }, - - this.draw = function(paper) { - var y = this.index*this.graph.pix_per_session;; - var start = this.starttick * this.graph.pix_per_ticks; - var end = this.endtick * this.graph.pix_per_ticks; - - var sess = paper.set(); - - if (this.endtick == undefined) { - end = this.graph.width(); - } - - sess.push(paper.path("M" + start + " " + y + " L" + end + " " + y)); - for (var i in this.events) { - var e = this.events[i]; - this.drawEvent(paper, y, e); - } - - //sess.attr({"stroke-width": 3}); - label = paper.text(start + 100, y, this.id); - label.attr({"font-size": "14px"}); - label.hide(); - this._attach_action(sess, label); - } -}; - diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.stats.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.stats.js deleted file mode 100644 index 0a8ac4fcce0..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.stats.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.StatsGraph = function (asyncq, canvas, starttime, endtime, filter) { - var processdata = function(data) { - var r = Raphael(canvas); - var x = data.map(function (x) { return x.time; }); - var y = data.map(function (x) { return x.count; }); - var xlabels = data.map(function (x) { return dateFormat(x.time, "HH:MM:ss,l"); } ); - var h1 = function () { - this.tags = r.set(); - for (var i = 0, ii = this.y.length; i < ii; i++) { - this.tags.push(r.g.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{fill: "#fff"}, {fill: this.symbols[i].attr("fill")}])); - } - }; - var h2 = function () { - this.tags && this.tags.remove(); - }; - r.g.linechart(40, 40, 1000, 500, x, y, {shade: true, axis: "0 0 1 1", symbol: "x", southlabels: xlabels, axisxstep: xlabels.length - 1 , westAxisLabel: "Write requests", southAxisLabel: "Time (min)"}).hoverColumn(h1, h2); - - return true; - //r.g.barchart(0, 0, 1000, 100, y, {shade: true, symbol: "x"}).hoverColumn(h1, h2); - }; - - var uri = "/throughput?scale=minutes"; - LogGraph.loadData(asyncq, uri, processdata); -}; - - diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.ui.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.ui.js deleted file mode 100644 index 819765a3e4b..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/loggraph.ui.js +++ /dev/null @@ -1,377 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Opens a window to load files into the engine -LogGraph.fileSelector = function(callback) { - var self = this; - this.callback = callback; - this.selectedFiles = new Array(); - - var divTag = document.createElement("div"); - divTag.id = "fileSelector" + Math.round(Math.random()*100000); - // divTag.className = "popUp"; - divTag.className = "selector fileSelector"; - document.body.appendChild(divTag); - - YUI().use('dd-drag', function(Y) { - //Selector of the node to make draggable - var dd = new Y.DD.Drag({ - node: '#' + divTag.id - }); - }); - - var list = document.createElement("ul"); - divTag.appendChild(list); - var selectedList = document.createElement("selectedlist"); - divTag.appendChild(selectedList); - - var clearanchor = document.createElement("span"); - clearanchor.innerHTML = "Remove All"; - clearanchor.className = "actionbutton"; - clearanchor.style.cssFloat = "right"; - clearanchor.onclick = function () { - self.selectedFiles = new Array(); - self.updateSelectedList(); - }; - divTag.appendChild(clearanchor); - - var doneanchor = document.createElement("span"); - doneanchor.innerHTML = "Process Files"; - doneanchor.className = "actionbutton"; - doneanchor.style.cssFloat = "left"; - doneanchor.onclick = function () { - self.callback(self.selectedFiles); - document.body.removeChild(divTag); - delete divTag; - }; - divTag.appendChild(doneanchor); - - var cancelanchor = document.createElement("span"); - cancelanchor.innerHTML = "Cancel"; - cancelanchor.className = "actionbutton"; - cancelanchor.style.cssFloat = "left"; - cancelanchor.onclick = function () { - document.body.removeChild(divTag); - delete divTag; - }; - divTag.appendChild(cancelanchor); - - this.createFileListItem = function (file) { - var li = document.createElement("li"); - var a = document.createElement("a"); - if (file.type == "D") { - a.innerHTML = file.file + "/"; - a.onclick = function () { self.updateList(file.path); }; - } else { - a.innerHTML = file.file; - a.onclick = function () { self.addSelectedFile(file.path); }; - } - - a.fullpath = file.path;; - li.appendChild(a); - return li; - }; - - this.addSelectedFile = function (file) { - if (this.selectedFiles.indexOf(file) == -1) { - this.selectedFiles.push(file); - this.updateSelectedList(); - } - }; - - this.removeSelectedFile = function (file) { - this.selectedFiles = this.selectedFiles.filter(function(f) { return !(file == f); }); - this.updateSelectedList(); - }; - - this.createSelectedListItem = function (file) { - var li = document.createElement("li"); - var a = document.createElement("a"); - li.className = "selectedFile"; - a.onclick = function () { self.removeSelectedFile(file); }; - a.innerHTML = file; - li.appendChild(a); - return li; - }; - - this.updateSelectedList = function () { - while (selectedList.firstChild) { selectedList.removeChild(selectedList.firstChild); } - - for (var i in this.selectedFiles) { - var f = this.selectedFiles[i]; - selectedList.appendChild(this.createSelectedListItem(f)); - } - }; - - this.updateList = function (base) { - while (list.firstChild) list.removeChild(list.firstChild); - - // Create a YUI instance using io-base module. - YUI().use("io-base", function(Y) { - var uri = "/fs?path=" + base; - - // Define a function to handle the response data. - function complete(id, o, args) { - var id = id; // Transaction ID. - var data = eval("(" + o.responseText + ")"); // Response data. - var parts = base.split("/").slice(0,-1); - var parent = "" - if (parts.length < 2) { - parent = "/"; - } else { - parent = parts.join("/"); - } - if (base != "/") { - var li = self.createFileListItem({"file": "..", type: "D", path: parent}); - list.appendChild(li); - } - for (var i in data) { - var f = data[i]; - if (f.file[0] != '.') { - var li = self.createFileListItem(f); - list.appendChild(li); - } - } - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - }; - - this.updateList("/"); -}; - -// Open a window which loads files into the engine -LogGraph.fileLoader = function(files) { - var div = document.createElement("div"); - div.id = "fileLoader"; - - var imgArray = new Array(); - var pArray = new Array(); - for (var index in files) { - var f = files[index]; - var p = document.createElement("p"); - var i = document.createElement("img"); - i.src = "load.gif"; - i.style.visibility = "hidden"; - imgArray.push(i); - pArray.push(p); - var span = document.createElement("span"); - span.innerHTML = f; - - p.appendChild(span); - p.appendChild(i); - - div.appendChild(p); - } - - var loadFile = function (index) { - // Create a YUI instance using io-base module. - YUI().use("io-base", function(Y) { - var file = files[index]; - var uri = "/loadfile?path=" + file; - imgArray[index].style.visibility = "visible"; - - // Define a function to handle the response data. - function complete(id, o, args) { - var id = id; // Transaction ID. - var data = eval("(" + o.responseText + ")"); // Response data. - if (data.status == "ERR") { - var err = document.createElement("div"); - err.innerHTML = data.error; - pArray[index].appendChild(err); - } else if (data.status == "OK") { - var ok = document.createElement("div"); - ok.innerHTML = "OK"; - pArray[index].appendChild(ok); - } - - imgArray[index].style.visibility = "hidden"; - if (index + 1 < files.length) { - loadFile(index + 1); - } else { - //alert("DONE"); - } - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - }; - - var doneanchor = document.createElement("a"); - doneanchor.className = "actionbutton"; - doneanchor.innerHTML = "Done"; - doneanchor.onclick = function () { - document.body.removeChild(div); - delete div; - }; - - document.body.appendChild(div); - if (files.length > 0) { - loadFile(0); - } else { - div.innerHTML ="No files to load"; - } - div.appendChild(doneanchor); -} - -// select a time period -LogGraph.filterSelector = function(starttime, period, filter, callback) { - var self = this; - this.callback = callback; - - // Container other widgets will be in - var container = document.createElement("div"); - container.id = "filterSelector" + Math.round(Math.random()*100000); - container.className = "selector filterSelector"; - document.body.appendChild(container); - - YUI().use('dd-drag', function(Y) { - //Selector of the node to make draggable - var dd = new Y.DD.Drag({ - node: '#' + container.id - }); - }); - - // Temporary loading screen - var loadingp = document.createElement("p"); - loadingp.innerHTML = "Loading..."; - var loadimg = document.createElement("img"); - loadimg.src = "load.gif"; - loadingp.appendChild(loadimg); - container.appendChild(loadingp); - - var addWithLabel = function (container, labeltxt, object) { - var p = document.createElement("p"); - var label = document.createElement("label"); - label.innerHTML = labeltxt + ":"; - p.appendChild(label); - p.appendChild(object); - container.appendChild(p); - }; - var draw = function(minstart, maxstart, entries) { - container.removeChild(loadingp); - var inittime = minstart > starttime ? minstart : starttime; - - var numEntries = 0; - var startspan = document.createElement("span"); - addWithLabel(container, "Start time", startspan); - var startinput = document.createElement("input"); - startinput.type = "hidden"; - startinput.value = inittime; - container.appendChild(startinput); - var sliderspan = document.createElement("span"); - container.appendChild(sliderspan); - - var countspan = document.createElement("p"); - countspan.innerHTML = entries + " entries";; - container.appendChild(countspan); - - var windowinput = document.createElement("input"); - windowinput.type = "text"; - windowinput.value = period; - addWithLabel(container, "Time window (ms)", windowinput); - - var filterinput = document.createElement("textarea"); - filterinput.id = "filterinput"; - filterinput.value = filter; - addWithLabel(container, "Filter", filterinput); - - /* done link, when clicked time is updated, */ - var doneanchor = document.createElement("a"); - doneanchor.className = "actionbutton"; - doneanchor.innerHTML = "Done"; - doneanchor.onclick = function () { - var start = parseInt(startinput.value); - var period = parseInt(windowinput.value); - var filter = filterinput.value; - document.body.removeChild(container); - delete container; - - update(start, period, filter, function() { - callback(start, period, filter, numEntries); - }); - }; - container.appendChild(doneanchor); - - var update = function(start, period, filter, thenrun) { - startspan.innerHTML = dateFormat(start, "HH:MM:ss,l"); - // get the min and max start time - YUI().use("io-base", function(Y) { - var uri = "/info?start=" + start + "&period=" + period + "&filter=" + filter; - function complete(id, o, args) { - var data = eval("(" + o.responseText + ")"); - countspan.innerHTML = data.numEntries + " entries"; - numEntries = data.numEntries; - if (thenrun) { - thenrun(); - } - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - }; - - var updatewindow = function(evt) { - var start = parseInt(startinput.value); - var period = parseInt(windowinput.value); - var filter = filterinput.value; - update(start, period, filter); - }; - windowinput.onkeyup = updatewindow; - - - YUI().use("slider", function (Y) { - var input, slider; - - function updateInput( e ) { - this.set( "value", e.newVal ); - - update(parseInt(startinput.value), parseInt(windowinput.value), filterinput.value); - } - - xSlider = new Y.Slider({min: minstart, max: maxstart, value: inittime, length: "1000px" }); - - // Link the input value to the Slider - xInput = Y.one( startinput ); - xInput.setData( { slider: xSlider } ); - - // Pass the input as the 'this' object inside updateInput - xSlider.after( "valueChange", updateInput, xInput ); - - // Render the Slider next to the input - xSlider.render(sliderspan); - }); - update(inittime, windowinput.value, filterinput); - }; - - // get the min and max start time - YUI().use("io-base", function(Y) { - var uri = "/info"; - function complete(id, o, args) { - var data = eval("(" + o.responseText + ")"); - draw(data.startTime, data.endTime, data.numEntries); - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); -} \ No newline at end of file diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/main.html b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/main.html deleted file mode 100644 index b9affe66585..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/main.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -
- Edit Filters - Add logs -
-
- Log view - Servers view - Sessions view - Statistics -
-
-
-
-
- - diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/raphael.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/raphael.js deleted file mode 100644 index 3740d0f02ad..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/raphael.js +++ /dev/null @@ -1,3296 +0,0 @@ -/*! - * Raphael 1.3.2 - JavaScript Vector Library - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ - -Raphael = (function () { - var separator = /[, ]+/, - elements = /^(circle|rect|path|ellipse|text|image)$/, - proto = "prototype", - has = "hasOwnProperty", - doc = document, - win = window, - oldRaphael = { - was: Object[proto][has].call(win, "Raphael"), - is: win.Raphael - }, - R = function () { - if (R.is(arguments[0], "array")) { - var a = arguments[0], - cnv = create[apply](R, a.splice(0, 3 + R.is(a[0], nu))), - res = cnv.set(); - for (var i = 0, ii = a[length]; i < ii; i++) { - var j = a[i] || {}; - elements.test(j.type) && res[push](cnv[j.type]().attr(j)); - } - return res; - } - return create[apply](R, arguments); - }, - Paper = function () {}, - appendChild = "appendChild", - apply = "apply", - concat = "concat", - E = "", - S = " ", - split = "split", - events = "click dblclick mousedown mousemove mouseout mouseover mouseup"[split](S), - join = "join", - length = "length", - lowerCase = String[proto].toLowerCase, - math = Math, - mmax = math.max, - mmin = math.min, - nu = "number", - toString = "toString", - objectToString = Object[proto][toString], - paper = {}, - pow = math.pow, - push = "push", - rg = /^(?=[\da-f]$)/, - ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i, //" - colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|rgb\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i, - round = math.round, - setAttribute = "setAttribute", - toFloat = parseFloat, - toInt = parseInt, - upperCase = String[proto].toUpperCase, - availableAttrs = {blur: 0, "clip-rect": "0 0 1e9 1e9", cursor: "default", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", translation: "0 0", width: 0, x: 0, y: 0}, - availableAnimAttrs = {along: "along", blur: nu, "clip-rect": "csv", cx: nu, cy: nu, fill: "colour", "fill-opacity": nu, "font-size": nu, height: nu, opacity: nu, path: "path", r: nu, rotation: "csv", rx: nu, ry: nu, scale: "csv", stroke: "colour", "stroke-opacity": nu, "stroke-width": nu, translation: "csv", width: nu, x: nu, y: nu}, - rp = "replace"; - R.version = "1.3.2"; - R.type = (win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML"); - if (R.type == "VML") { - var d = doc.createElement("div"); - d.innerHTML = ''; - if (d.childNodes[length] != 2) { - return R.type = null; - } - d = null; - } - R.svg = !(R.vml = R.type == "VML"); - Paper[proto] = R[proto]; - R._id = 0; - R._oid = 0; - R.fn = {}; - R.is = function (o, type) { - type = lowerCase.call(type); - return ((type == "object" || type == "undefined") && typeof o == type) || (o == null && type == "null") || lowerCase.call(objectToString.call(o).slice(8, -1)) == type; - }; - R.setWindow = function (newwin) { - win = newwin; - doc = win.document; - }; - // colour utilities - var toHex = function (color) { - if (R.vml) { - // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/ - var trim = /^\s+|\s+$/g; - toHex = cacher(function (color) { - var bod; - color = (color + E)[rp](trim, E); - try { - var docum = new win.ActiveXObject("htmlfile"); - docum.write(""); - docum.close(); - bod = docum.body; - } catch(e) { - bod = win.createPopup().document.body; - } - var range = bod.createTextRange(); - try { - bod.style.color = color; - var value = range.queryCommandValue("ForeColor"); - value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16); - return "#" + ("000000" + value[toString](16)).slice(-6); - } catch(e) { - return "none"; - } - }); - } else { - var i = doc.createElement("i"); - i.title = "Rapha\xebl Colour Picker"; - i.style.display = "none"; - doc.body[appendChild](i); - toHex = cacher(function (color) { - i.style.color = color; - return doc.defaultView.getComputedStyle(i, E).getPropertyValue("color"); - }); - } - return toHex(color); - }; - var hsbtoString = function () { - return "hsb(" + [this.h, this.s, this.b] + ")"; - }, - rgbtoString = function () { - return this.hex; - }; - R.hsb2rgb = cacher(function (hue, saturation, brightness) { - if (R.is(hue, "object") && "h" in hue && "s" in hue && "b" in hue) { - brightness = hue.b; - saturation = hue.s; - hue = hue.h; - } - var red, - green, - blue; - if (brightness == 0) { - return {r: 0, g: 0, b: 0, hex: "#000"}; - } - if (hue > 1 || saturation > 1 || brightness > 1) { - hue /= 255; - saturation /= 255; - brightness /= 255; - } - var i = ~~(hue * 6), - f = (hue * 6) - i, - p = brightness * (1 - saturation), - q = brightness * (1 - (saturation * f)), - t = brightness * (1 - (saturation * (1 - f))); - red = [brightness, q, p, p, t, brightness, brightness][i]; - green = [t, brightness, brightness, q, p, p, t][i]; - blue = [p, p, t, brightness, brightness, q, p][i]; - red *= 255; - green *= 255; - blue *= 255; - var rgb = {r: red, g: green, b: blue, toString: rgbtoString}, - r = (~~red)[toString](16), - g = (~~green)[toString](16), - b = (~~blue)[toString](16); - r = r[rp](rg, "0"); - g = g[rp](rg, "0"); - b = b[rp](rg, "0"); - rgb.hex = "#" + r + g + b; - return rgb; - }, R); - R.rgb2hsb = cacher(function (red, green, blue) { - if (R.is(red, "object") && "r" in red && "g" in red && "b" in red) { - blue = red.b; - green = red.g; - red = red.r; - } - if (R.is(red, "string")) { - var clr = R.getRGB(red); - red = clr.r; - green = clr.g; - blue = clr.b; - } - if (red > 1 || green > 1 || blue > 1) { - red /= 255; - green /= 255; - blue /= 255; - } - var max = mmax(red, green, blue), - min = mmin(red, green, blue), - hue, - saturation, - brightness = max; - if (min == max) { - return {h: 0, s: 0, b: max}; - } else { - var delta = (max - min); - saturation = delta / max; - if (red == max) { - hue = (green - blue) / delta; - } else if (green == max) { - hue = 2 + ((blue - red) / delta); - } else { - hue = 4 + ((red - green) / delta); - } - hue /= 6; - hue < 0 && hue++; - hue > 1 && hue--; - } - return {h: hue, s: saturation, b: brightness, toString: hsbtoString}; - }, R); - var p2s = /,?([achlmqrstvxz]),?/gi; - R._path2string = function () { - return this.join(",")[rp](p2s, "$1"); - }; - function cacher(f, scope, postprocessor) { - function newf() { - var arg = Array[proto].slice.call(arguments, 0), - args = arg[join]("\u25ba"), - cache = newf.cache = newf.cache || {}, - count = newf.count = newf.count || []; - if (cache[has](args)) { - return postprocessor ? postprocessor(cache[args]) : cache[args]; - } - count[length] >= 1e3 && delete cache[count.shift()]; - count[push](args); - cache[args] = f[apply](scope, arg); - return postprocessor ? postprocessor(cache[args]) : cache[args]; - } - return newf; - } - - R.getRGB = cacher(function (colour) { - if (!colour || !!((colour = colour + E).indexOf("-") + 1)) { - return {r: -1, g: -1, b: -1, hex: "none", error: 1}; - } - if (colour == "none") { - return {r: -1, g: -1, b: -1, hex: "none"}; - } - !(({hs: 1, rg: 1})[has](colour.substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour)); - var res, - red, - green, - blue, - t, - rgb = colour.match(colourRegExp); - if (rgb) { - if (rgb[2]) { - blue = toInt(rgb[2].substring(5), 16); - green = toInt(rgb[2].substring(3, 5), 16); - red = toInt(rgb[2].substring(1, 3), 16); - } - if (rgb[3]) { - blue = toInt((t = rgb[3].charAt(3)) + t, 16); - green = toInt((t = rgb[3].charAt(2)) + t, 16); - red = toInt((t = rgb[3].charAt(1)) + t, 16); - } - if (rgb[4]) { - rgb = rgb[4][split](/\s*,\s*/); - red = toFloat(rgb[0]); - green = toFloat(rgb[1]); - blue = toFloat(rgb[2]); - } - if (rgb[5]) { - rgb = rgb[5][split](/\s*,\s*/); - red = toFloat(rgb[0]) * 2.55; - green = toFloat(rgb[1]) * 2.55; - blue = toFloat(rgb[2]) * 2.55; - } - if (rgb[6]) { - rgb = rgb[6][split](/\s*,\s*/); - red = toFloat(rgb[0]); - green = toFloat(rgb[1]); - blue = toFloat(rgb[2]); - return R.hsb2rgb(red, green, blue); - } - if (rgb[7]) { - rgb = rgb[7][split](/\s*,\s*/); - red = toFloat(rgb[0]) * 2.55; - green = toFloat(rgb[1]) * 2.55; - blue = toFloat(rgb[2]) * 2.55; - return R.hsb2rgb(red, green, blue); - } - rgb = {r: red, g: green, b: blue}; - var r = (~~red)[toString](16), - g = (~~green)[toString](16), - b = (~~blue)[toString](16); - r = r[rp](rg, "0"); - g = g[rp](rg, "0"); - b = b[rp](rg, "0"); - rgb.hex = "#" + r + g + b; - return rgb; - } - return {r: -1, g: -1, b: -1, hex: "none", error: 1}; - }, R); - R.getColor = function (value) { - var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75}, - rgb = this.hsb2rgb(start.h, start.s, start.b); - start.h += .075; - if (start.h > 1) { - start.h = 0; - start.s -= .2; - start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b}); - } - return rgb.hex; - }; - R.getColor.reset = function () { - delete this.start; - }; - // path utilities - var pathCommand = /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, - pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig; - R.parsePathString = cacher(function (pathString) { - if (!pathString) { - return null; - } - var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}, - data = []; - if (R.is(pathString, "array") && R.is(pathString[0], "array")) { // rough assumption - data = pathClone(pathString); - } - if (!data[length]) { - (pathString + E)[rp](pathCommand, function (a, b, c) { - var params = [], - name = lowerCase.call(b); - c[rp](pathValues, function (a, b) { - b && params[push](+b); - }); - if (name == "m" && params[length] > 2) { - data[push]([b][concat](params.splice(0, 2))); - name = "l"; - b = b == "m" ? "l" : "L"; - } - while (params[length] >= paramCounts[name]) { - data[push]([b][concat](params.splice(0, paramCounts[name]))); - if (!paramCounts[name]) { - break; - } - } - }); - } - data[toString] = R._path2string; - return data; - }); - R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { - var t1 = 1 - t, - x = pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, - y = pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y, - mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x), - my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y), - nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x), - ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y), - ax = (1 - t) * p1x + t * c1x, - ay = (1 - t) * p1y + t * c1y, - cx = (1 - t) * c2x + t * p2x, - cy = (1 - t) * c2y + t * p2y, - alpha = (90 - math.atan((mx - nx) / (my - ny)) * 180 / math.PI); - (mx > nx || my < ny) && (alpha += 180); - return {x: x, y: y, m: {x: mx, y: my}, n: {x: nx, y: ny}, start: {x: ax, y: ay}, end: {x: cx, y: cy}, alpha: alpha}; - }; - var pathDimensions = cacher(function (path) { - if (!path) { - return {x: 0, y: 0, width: 0, height: 0}; - } - path = path2curve(path); - var x = 0, - y = 0, - X = [], - Y = [], - p; - for (var i = 0, ii = path[length]; i < ii; i++) { - p = path[i]; - if (p[0] == "M") { - x = p[1]; - y = p[2]; - X[push](x); - Y[push](y); - } else { - var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); - X = X[concat](dim.min.x, dim.max.x); - Y = Y[concat](dim.min.y, dim.max.y); - x = p[5]; - y = p[6]; - } - } - var xmin = mmin[apply](0, X), - ymin = mmin[apply](0, Y); - return { - x: xmin, - y: ymin, - width: mmax[apply](0, X) - xmin, - height: mmax[apply](0, Y) - ymin - }; - }), - pathClone = function (pathArray) { - var res = []; - if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = R.parsePathString(pathArray); - } - for (var i = 0, ii = pathArray[length]; i < ii; i++) { - res[i] = []; - for (var j = 0, jj = pathArray[i][length]; j < jj; j++) { - res[i][j] = pathArray[i][j]; - } - } - res[toString] = R._path2string; - return res; - }, - pathToRelative = cacher(function (pathArray) { - if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = R.parsePathString(pathArray); - } - var res = [], - x = 0, - y = 0, - mx = 0, - my = 0, - start = 0; - if (pathArray[0][0] == "M") { - x = pathArray[0][1]; - y = pathArray[0][2]; - mx = x; - my = y; - start++; - res[push](["M", x, y]); - } - for (var i = start, ii = pathArray[length]; i < ii; i++) { - var r = res[i] = [], - pa = pathArray[i]; - if (pa[0] != lowerCase.call(pa[0])) { - r[0] = lowerCase.call(pa[0]); - switch (r[0]) { - case "a": - r[1] = pa[1]; - r[2] = pa[2]; - r[3] = pa[3]; - r[4] = pa[4]; - r[5] = pa[5]; - r[6] = +(pa[6] - x).toFixed(3); - r[7] = +(pa[7] - y).toFixed(3); - break; - case "v": - r[1] = +(pa[1] - y).toFixed(3); - break; - case "m": - mx = pa[1]; - my = pa[2]; - default: - for (var j = 1, jj = pa[length]; j < jj; j++) { - r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); - } - } - } else { - r = res[i] = []; - if (pa[0] == "m") { - mx = pa[1] + x; - my = pa[2] + y; - } - for (var k = 0, kk = pa[length]; k < kk; k++) { - res[i][k] = pa[k]; - } - } - var len = res[i][length]; - switch (res[i][0]) { - case "z": - x = mx; - y = my; - break; - case "h": - x += +res[i][len - 1]; - break; - case "v": - y += +res[i][len - 1]; - break; - default: - x += +res[i][len - 2]; - y += +res[i][len - 1]; - } - } - res[toString] = R._path2string; - return res; - }, 0, pathClone), - pathToAbsolute = cacher(function (pathArray) { - if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = R.parsePathString(pathArray); - } - var res = [], - x = 0, - y = 0, - mx = 0, - my = 0, - start = 0; - if (pathArray[0][0] == "M") { - x = +pathArray[0][1]; - y = +pathArray[0][2]; - mx = x; - my = y; - start++; - res[0] = ["M", x, y]; - } - for (var i = start, ii = pathArray[length]; i < ii; i++) { - var r = res[i] = [], - pa = pathArray[i]; - if (pa[0] != upperCase.call(pa[0])) { - r[0] = upperCase.call(pa[0]); - switch (r[0]) { - case "A": - r[1] = pa[1]; - r[2] = pa[2]; - r[3] = pa[3]; - r[4] = pa[4]; - r[5] = pa[5]; - r[6] = +(pa[6] + x); - r[7] = +(pa[7] + y); - break; - case "V": - r[1] = +pa[1] + y; - break; - case "H": - r[1] = +pa[1] + x; - break; - case "M": - mx = +pa[1] + x; - my = +pa[2] + y; - default: - for (var j = 1, jj = pa[length]; j < jj; j++) { - r[j] = +pa[j] + ((j % 2) ? x : y); - } - } - } else { - for (var k = 0, kk = pa[length]; k < kk; k++) { - res[i][k] = pa[k]; - } - } - switch (r[0]) { - case "Z": - x = mx; - y = my; - break; - case "H": - x = r[1]; - break; - case "V": - y = r[1]; - break; - default: - x = res[i][res[i][length] - 2]; - y = res[i][res[i][length] - 1]; - } - } - res[toString] = R._path2string; - return res; - }, null, pathClone), - l2c = function (x1, y1, x2, y2) { - return [x1, y1, x2, y2, x2, y2]; - }, - q2c = function (x1, y1, ax, ay, x2, y2) { - var _13 = 1 / 3, - _23 = 2 / 3; - return [ - _13 * x1 + _23 * ax, - _13 * y1 + _23 * ay, - _13 * x2 + _23 * ax, - _13 * y2 + _23 * ay, - x2, - y2 - ]; - }, - a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { - // for more information of where this math came from visit: - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - var PI = math.PI, - _120 = PI * 120 / 180, - rad = PI / 180 * (+angle || 0), - res = [], - xy, - rotate = cacher(function (x, y, rad) { - var X = x * math.cos(rad) - y * math.sin(rad), - Y = x * math.sin(rad) + y * math.cos(rad); - return {x: X, y: Y}; - }); - if (!recursive) { - xy = rotate(x1, y1, -rad); - x1 = xy.x; - y1 = xy.y; - xy = rotate(x2, y2, -rad); - x2 = xy.x; - y2 = xy.y; - var cos = math.cos(PI / 180 * angle), - sin = math.sin(PI / 180 * angle), - x = (x1 - x2) / 2, - y = (y1 - y2) / 2; - // rx = mmax(rx, math.abs(x)); - // ry = mmax(ry, math.abs(y)); - var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); - if (h > 1) { - h = math.sqrt(h); - rx = h * rx; - ry = h * ry; - } - var rx2 = rx * rx, - ry2 = ry * ry, - k = (large_arc_flag == sweep_flag ? -1 : 1) * - math.sqrt(math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), - cx = k * rx * y / ry + (x1 + x2) / 2, - cy = k * -ry * x / rx + (y1 + y2) / 2, - f1 = math.asin(((y1 - cy) / ry).toFixed(7)), - f2 = math.asin(((y2 - cy) / ry).toFixed(7)); - - f1 = x1 < cx ? PI - f1 : f1; - f2 = x2 < cx ? PI - f2 : f2; - f1 < 0 && (f1 = PI * 2 + f1); - f2 < 0 && (f2 = PI * 2 + f2); - if (sweep_flag && f1 > f2) { - f1 = f1 - PI * 2; - } - if (!sweep_flag && f2 > f1) { - f2 = f2 - PI * 2; - } - } else { - f1 = recursive[0]; - f2 = recursive[1]; - cx = recursive[2]; - cy = recursive[3]; - } - var df = f2 - f1; - if (math.abs(df) > _120) { - var f2old = f2, - x2old = x2, - y2old = y2; - f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); - x2 = cx + rx * math.cos(f2); - y2 = cy + ry * math.sin(f2); - res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); - } - df = f2 - f1; - var c1 = math.cos(f1), - s1 = math.sin(f1), - c2 = math.cos(f2), - s2 = math.sin(f2), - t = math.tan(df / 4), - hx = 4 / 3 * rx * t, - hy = 4 / 3 * ry * t, - m1 = [x1, y1], - m2 = [x1 + hx * s1, y1 - hy * c1], - m3 = [x2 + hx * s2, y2 - hy * c2], - m4 = [x2, y2]; - m2[0] = 2 * m1[0] - m2[0]; - m2[1] = 2 * m1[1] - m2[1]; - if (recursive) { - return [m2, m3, m4][concat](res); - } else { - res = [m2, m3, m4][concat](res)[join]()[split](","); - var newres = []; - for (var i = 0, ii = res[length]; i < ii; i++) { - newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; - } - // alert(newres); - return newres; - } - }, - findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { - var t1 = 1 - t; - return { - x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, - y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y - }; - }, - curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { - var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x), - b = 2 * (c1x - p1x) - 2 * (c2x - c1x), - c = p1x - c1x, - t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a, - t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a, - y = [p1y, p2y], - x = [p1x, p2x], - dot; - math.abs(t1) > 1e12 && (t1 = .5); - math.abs(t2) > 1e12 && (t2 = .5); - if (t1 > 0 && t1 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); - x[push](dot.x); - y[push](dot.y); - } - if (t2 > 0 && t2 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); - x[push](dot.x); - y[push](dot.y); - } - a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y); - b = 2 * (c1y - p1y) - 2 * (c2y - c1y); - c = p1y - c1y; - t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a; - t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a; - math.abs(t1) > 1e12 && (t1 = .5); - math.abs(t2) > 1e12 && (t2 = .5); - if (t1 > 0 && t1 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); - x[push](dot.x); - y[push](dot.y); - } - if (t2 > 0 && t2 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); - x[push](dot.x); - y[push](dot.y); - } - return { - min: {x: mmin[apply](0, x), y: mmin[apply](0, y)}, - max: {x: mmax[apply](0, x), y: mmax[apply](0, y)} - }; - }), - path2curve = cacher(function (path, path2) { - var p = pathToAbsolute(path), - p2 = path2 && pathToAbsolute(path2), - attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, - attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, - processPath = function (path, d) { - var nx, ny; - if (!path) { - return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; - } - !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null); - switch (path[0]) { - case "M": - d.X = path[1]; - d.Y = path[2]; - break; - case "A": - path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1)))); - break; - case "S": - nx = d.x + (d.x - (d.bx || d.x)); - ny = d.y + (d.y - (d.by || d.y)); - path = ["C", nx, ny][concat](path.slice(1)); - break; - case "T": - d.qx = d.x + (d.x - (d.qx || d.x)); - d.qy = d.y + (d.y - (d.qy || d.y)); - path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); - break; - case "Q": - d.qx = path[1]; - d.qy = path[2]; - path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4])); - break; - case "L": - path = ["C"][concat](l2c(d.x, d.y, path[1], path[2])); - break; - case "H": - path = ["C"][concat](l2c(d.x, d.y, path[1], d.y)); - break; - case "V": - path = ["C"][concat](l2c(d.x, d.y, d.x, path[1])); - break; - case "Z": - path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y)); - break; - } - return path; - }, - fixArc = function (pp, i) { - if (pp[i][length] > 7) { - pp[i].shift(); - var pi = pp[i]; - while (pi[length]) { - pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6))); - } - pp.splice(i, 1); - ii = mmax(p[length], p2 && p2[length] || 0); - } - }, - fixM = function (path1, path2, a1, a2, i) { - if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { - path2.splice(i, 0, ["M", a2.x, a2.y]); - a1.bx = 0; - a1.by = 0; - a1.x = path1[i][1]; - a1.y = path1[i][2]; - ii = mmax(p[length], p2 && p2[length] || 0); - } - }; - for (var i = 0, ii = mmax(p[length], p2 && p2[length] || 0); i < ii; i++) { - p[i] = processPath(p[i], attrs); - fixArc(p, i); - p2 && (p2[i] = processPath(p2[i], attrs2)); - p2 && fixArc(p2, i); - fixM(p, p2, attrs, attrs2, i); - fixM(p2, p, attrs2, attrs, i); - var seg = p[i], - seg2 = p2 && p2[i], - seglen = seg[length], - seg2len = p2 && seg2[length]; - attrs.x = seg[seglen - 2]; - attrs.y = seg[seglen - 1]; - attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; - attrs.by = toFloat(seg[seglen - 3]) || attrs.y; - attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); - attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); - attrs2.x = p2 && seg2[seg2len - 2]; - attrs2.y = p2 && seg2[seg2len - 1]; - } - return p2 ? [p, p2] : p; - }, null, pathClone), - parseDots = cacher(function (gradient) { - var dots = []; - for (var i = 0, ii = gradient[length]; i < ii; i++) { - var dot = {}, - par = gradient[i].match(/^([^:]*):?([\d\.]*)/); - dot.color = R.getRGB(par[1]); - if (dot.color.error) { - return null; - } - dot.color = dot.color.hex; - par[2] && (dot.offset = par[2] + "%"); - dots[push](dot); - } - for (i = 1, ii = dots[length] - 1; i < ii; i++) { - if (!dots[i].offset) { - var start = toFloat(dots[i - 1].offset || 0), - end = 0; - for (var j = i + 1; j < ii; j++) { - if (dots[j].offset) { - end = dots[j].offset; - break; - } - } - if (!end) { - end = 100; - j = ii; - } - end = toFloat(end); - var d = (end - start) / (j - i + 1); - for (; i < j; i++) { - start += d; - dots[i].offset = start + "%"; - } - } - } - return dots; - }), - getContainer = function (x, y, w, h) { - var container; - if (R.is(x, "string") || R.is(x, "object")) { - container = R.is(x, "string") ? doc.getElementById(x) : x; - if (container.tagName) { - if (y == null) { - return { - container: container, - width: container.style.pixelWidth || container.offsetWidth, - height: container.style.pixelHeight || container.offsetHeight - }; - } else { - return {container: container, width: y, height: w}; - } - } - } else if (R.is(x, nu) && h != null) { - return {container: 1, x: x, y: y, width: w, height: h}; - } - }, - plugins = function (con, add) { - var that = this; - for (var prop in add) { - if (add[has](prop) && !(prop in con)) { - switch (typeof add[prop]) { - case "function": - (function (f) { - con[prop] = con === that ? f : function () { return f[apply](that, arguments); }; - })(add[prop]); - break; - case "object": - con[prop] = con[prop] || {}; - plugins.call(this, con[prop], add[prop]); - break; - default: - con[prop] = add[prop]; - break; - } - } - } - }, - tear = function (el, paper) { - el == paper.top && (paper.top = el.prev); - el == paper.bottom && (paper.bottom = el.next); - el.next && (el.next.prev = el.prev); - el.prev && (el.prev.next = el.next); - }, - tofront = function (el, paper) { - if (paper.top === el) { - return; - } - tear(el, paper); - el.next = null; - el.prev = paper.top; - paper.top.next = el; - paper.top = el; - }, - toback = function (el, paper) { - if (paper.bottom === el) { - return; - } - tear(el, paper); - el.next = paper.bottom; - el.prev = null; - paper.bottom.prev = el; - paper.bottom = el; - }, - insertafter = function (el, el2, paper) { - tear(el, paper); - el2 == paper.top && (paper.top = el); - el2.next && (el2.next.prev = el); - el.next = el2.next; - el.prev = el2; - el2.next = el; - }, - insertbefore = function (el, el2, paper) { - tear(el, paper); - el2 == paper.bottom && (paper.bottom = el); - el2.prev && (el2.prev.next = el); - el.prev = el2.prev; - el2.prev = el; - el.next = el2; - }, - removed = function (methodname) { - return function () { - throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object"); - }; - }, - radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/; - - // SVG - if (R.svg) { - Paper[proto].svgns = "http://www.w3.org/2000/svg"; - Paper[proto].xlink = "http://www.w3.org/1999/xlink"; - round = function (num) { - return +num + (~~num === num) * .5; - }; - var roundPath = function (path) { - for (var i = 0, ii = path[length]; i < ii; i++) { - if (lowerCase.call(path[i][0]) != "a") { - for (var j = 1, jj = path[i][length]; j < jj; j++) { - path[i][j] = round(path[i][j]); - } - } else { - path[i][6] = round(path[i][6]); - path[i][7] = round(path[i][7]); - } - } - return path; - }, - $ = function (el, attr) { - if (attr) { - for (var key in attr) { - if (attr[has](key)) { - el[setAttribute](key, attr[key] + E); - } - } - } else { - return doc.createElementNS(Paper[proto].svgns, el); - } - }; - R[toString] = function () { - return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version; - }; - var thePath = function (pathString, SVG) { - var el = $("path"); - SVG.canvas && SVG.canvas[appendChild](el); - var p = new Element(el, SVG); - p.type = "path"; - setFillAndStroke(p, {fill: "none", stroke: "#000", path: pathString}); - return p; - }; - var addGradientFill = function (o, gradient, SVG) { - var type = "linear", - fx = .5, fy = .5, - s = o.style; - gradient = (gradient + E)[rp](radial_gradient, function (all, _fx, _fy) { - type = "radial"; - if (_fx && _fy) { - fx = toFloat(_fx); - fy = toFloat(_fy); - var dir = ((fy > .5) * 2 - 1); - pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && - (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) && - fy != .5 && - (fy = fy.toFixed(5) - 1e-5 * dir); - } - return E; - }); - gradient = gradient[split](/\s*\-\s*/); - if (type == "linear") { - var angle = gradient.shift(); - angle = -toFloat(angle); - if (isNaN(angle)) { - return null; - } - var vector = [0, 0, math.cos(angle * math.PI / 180), math.sin(angle * math.PI / 180)], - max = 1 / (mmax(math.abs(vector[2]), math.abs(vector[3])) || 1); - vector[2] *= max; - vector[3] *= max; - if (vector[2] < 0) { - vector[0] = -vector[2]; - vector[2] = 0; - } - if (vector[3] < 0) { - vector[1] = -vector[3]; - vector[3] = 0; - } - } - var dots = parseDots(gradient); - if (!dots) { - return null; - } - var id = o.getAttribute("fill"); - id = id.match(/^url\(#(.*)\)$/); - id && SVG.defs.removeChild(doc.getElementById(id[1])); - - var el = $(type + "Gradient"); - el.id = "r" + (R._id++)[toString](36); - $(el, type == "radial" ? {fx: fx, fy: fy} : {x1: vector[0], y1: vector[1], x2: vector[2], y2: vector[3]}); - SVG.defs[appendChild](el); - for (var i = 0, ii = dots[length]; i < ii; i++) { - var stop = $("stop"); - $(stop, { - offset: dots[i].offset ? dots[i].offset : !i ? "0%" : "100%", - "stop-color": dots[i].color || "#fff" - }); - el[appendChild](stop); - } - $(o, { - fill: "url(#" + el.id + ")", - opacity: 1, - "fill-opacity": 1 - }); - s.fill = E; - s.opacity = 1; - s.fillOpacity = 1; - return 1; - }; - var updatePosition = function (o) { - var bbox = o.getBBox(); - $(o.pattern, {patternTransform: R.format("translate({0},{1})", bbox.x, bbox.y)}); - }; - var setFillAndStroke = function (o, params) { - var dasharray = { - "": [0], - "none": [0], - "-": [3, 1], - ".": [1, 1], - "-.": [3, 1, 1, 1], - "-..": [3, 1, 1, 1, 1, 1], - ". ": [1, 3], - "- ": [4, 3], - "--": [8, 3], - "- .": [4, 3, 1, 3], - "--.": [8, 3, 1, 3], - "--..": [8, 3, 1, 3, 1, 3] - }, - node = o.node, - attrs = o.attrs, - rot = o.rotate(), - addDashes = function (o, value) { - value = dasharray[lowerCase.call(value)]; - if (value) { - var width = o.attrs["stroke-width"] || "1", - butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0, - dashes = []; - var i = value[length]; - while (i--) { - dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt; - } - $(node, {"stroke-dasharray": dashes[join](",")}); - } - }; - params[has]("rotation") && (rot = params.rotation); - var rotxy = (rot + E)[split](separator); - if (!(rotxy.length - 1)) { - rotxy = null; - } else { - rotxy[1] = +rotxy[1]; - rotxy[2] = +rotxy[2]; - } - toFloat(rot) && o.rotate(0, true); - for (var att in params) { - if (params[has](att)) { - if (!availableAttrs[has](att)) { - continue; - } - var value = params[att]; - attrs[att] = value; - switch (att) { - case "blur": - o.blur(value); - break; - case "rotation": - o.rotate(value, true); - break; - // Hyperlink - case "href": - case "title": - case "target": - var pn = node.parentNode; - if (lowerCase.call(pn.tagName) != "a") { - var hl = $("a"); - pn.insertBefore(hl, node); - hl[appendChild](node); - pn = hl; - } - pn.setAttributeNS(o.paper.xlink, att, value); - break; - case "cursor": - node.style.cursor = value; - break; - case "clip-rect": - var rect = (value + E)[split](separator); - if (rect[length] == 4) { - o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode); - var el = $("clipPath"), - rc = $("rect"); - el.id = "r" + (R._id++)[toString](36); - $(rc, { - x: rect[0], - y: rect[1], - width: rect[2], - height: rect[3] - }); - el[appendChild](rc); - o.paper.defs[appendChild](el); - $(node, {"clip-path": "url(#" + el.id + ")"}); - o.clip = rc; - } - if (!value) { - var clip = doc.getElementById(node.getAttribute("clip-path")[rp](/(^url\(#|\)$)/g, E)); - clip && clip.parentNode.removeChild(clip); - $(node, {"clip-path": E}); - delete o.clip; - } - break; - case "path": - if (o.type == "path") { - $(node, {d: value ? attrs.path = roundPath(pathToAbsolute(value)) : "M0,0"}); - } - break; - case "width": - node[setAttribute](att, value); - if (attrs.fx) { - att = "x"; - value = attrs.x; - } else { - break; - } - case "x": - if (attrs.fx) { - value = -attrs.x - (attrs.width || 0); - } - case "rx": - if (att == "rx" && o.type == "rect") { - break; - } - case "cx": - rotxy && (att == "x" || att == "cx") && (rotxy[1] += value - attrs[att]); - node[setAttribute](att, round(value)); - o.pattern && updatePosition(o); - break; - case "height": - node[setAttribute](att, value); - if (attrs.fy) { - att = "y"; - value = attrs.y; - } else { - break; - } - case "y": - if (attrs.fy) { - value = -attrs.y - (attrs.height || 0); - } - case "ry": - if (att == "ry" && o.type == "rect") { - break; - } - case "cy": - rotxy && (att == "y" || att == "cy") && (rotxy[2] += value - attrs[att]); - node[setAttribute](att, round(value)); - o.pattern && updatePosition(o); - break; - case "r": - if (o.type == "rect") { - $(node, {rx: value, ry: value}); - } else { - node[setAttribute](att, value); - } - break; - case "src": - if (o.type == "image") { - node.setAttributeNS(o.paper.xlink, "href", value); - } - break; - case "stroke-width": - node.style.strokeWidth = value; - // Need following line for Firefox - node[setAttribute](att, value); - if (attrs["stroke-dasharray"]) { - addDashes(o, attrs["stroke-dasharray"]); - } - break; - case "stroke-dasharray": - addDashes(o, value); - break; - case "translation": - var xy = (value + E)[split](separator); - xy[0] = +xy[0] || 0; - xy[1] = +xy[1] || 0; - if (rotxy) { - rotxy[1] += xy[0]; - rotxy[2] += xy[1]; - } - translate.call(o, xy[0], xy[1]); - break; - case "scale": - xy = (value + E)[split](separator); - o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, isNaN(toFloat(xy[2])) ? null : +xy[2], isNaN(toFloat(xy[3])) ? null : +xy[3]); - break; - case "fill": - var isURL = (value + E).match(ISURL); - if (isURL) { - el = $("pattern"); - var ig = $("image"); - el.id = "r" + (R._id++)[toString](36); - $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1}); - $(ig, {x: 0, y: 0}); - ig.setAttributeNS(o.paper.xlink, "href", isURL[1]); - el[appendChild](ig); - - var img = doc.createElement("img"); - img.style.cssText = "position:absolute;left:-9999em;top-9999em"; - img.onload = function () { - $(el, {width: this.offsetWidth, height: this.offsetHeight}); - $(ig, {width: this.offsetWidth, height: this.offsetHeight}); - doc.body.removeChild(this); - o.paper.safari(); - }; - doc.body[appendChild](img); - img.src = isURL[1]; - o.paper.defs[appendChild](el); - node.style.fill = "url(#" + el.id + ")"; - $(node, {fill: "url(#" + el.id + ")"}); - o.pattern = el; - o.pattern && updatePosition(o); - break; - } - if (!R.getRGB(value).error) { - delete params.gradient; - delete attrs.gradient; - !R.is(attrs.opacity, "undefined") && - R.is(params.opacity, "undefined") && - $(node, {opacity: attrs.opacity}); - !R.is(attrs["fill-opacity"], "undefined") && - R.is(params["fill-opacity"], "undefined") && - $(node, {"fill-opacity": attrs["fill-opacity"]}); - } else if ((({circle: 1, ellipse: 1})[has](o.type) || (value + E).charAt() != "r") && addGradientFill(node, value, o.paper)) { - attrs.gradient = value; - attrs.fill = "none"; - break; - } - case "stroke": - node[setAttribute](att, R.getRGB(value).hex); - break; - case "gradient": - (({circle: 1, ellipse: 1})[has](o.type) || (value + E).charAt() != "r") && addGradientFill(node, value, o.paper); - break; - case "opacity": - case "fill-opacity": - if (attrs.gradient) { - var gradient = doc.getElementById(node.getAttribute("fill")[rp](/^url\(#|\)$/g, E)); - if (gradient) { - var stops = gradient.getElementsByTagName("stop"); - stops[stops[length] - 1][setAttribute]("stop-opacity", value); - } - break; - } - default: - att == "font-size" && (value = toInt(value, 10) + "px"); - var cssrule = att[rp](/(\-.)/g, function (w) { - return upperCase.call(w.substring(1)); - }); - node.style[cssrule] = value; - // Need following line for Firefox - node[setAttribute](att, value); - break; - } - } - } - - tuneText(o, params); - if (rotxy) { - o.rotate(rotxy.join(S)); - } else { - toFloat(rot) && o.rotate(rot, true); - } - }; - var leading = 1.2, - tuneText = function (el, params) { - if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) { - return; - } - var a = el.attrs, - node = el.node, - fontSize = node.firstChild ? toInt(doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10; - - if (params[has]("text")) { - a.text = params.text; - while (node.firstChild) { - node.removeChild(node.firstChild); - } - var texts = (params.text + E)[split]("\n"); - for (var i = 0, ii = texts[length]; i < ii; i++) if (texts[i]) { - var tspan = $("tspan"); - i && $(tspan, {dy: fontSize * leading, x: a.x}); - tspan[appendChild](doc.createTextNode(texts[i])); - node[appendChild](tspan); - } - } else { - texts = node.getElementsByTagName("tspan"); - for (i = 0, ii = texts[length]; i < ii; i++) { - i && $(texts[i], {dy: fontSize * leading, x: a.x}); - } - } - $(node, {y: a.y}); - var bb = el.getBBox(), - dif = a.y - (bb.y + bb.height / 2); - dif && isFinite(dif) && $(node, {y: a.y + dif}); - }, - Element = function (node, svg) { - var X = 0, - Y = 0; - this[0] = node; - this.id = R._oid++; - this.node = node; - node.raphael = this; - this.paper = svg; - this.attrs = this.attrs || {}; - this.transformations = []; // rotate, translate, scale - this._ = { - tx: 0, - ty: 0, - rt: {deg: 0, cx: 0, cy: 0}, - sx: 1, - sy: 1 - }; - !svg.bottom && (svg.bottom = this); - this.prev = svg.top; - svg.top && (svg.top.next = this); - svg.top = this; - this.next = null; - }; - Element[proto].rotate = function (deg, cx, cy) { - if (this.removed) { - return this; - } - if (deg == null) { - if (this._.rt.cx) { - return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S); - } - return this._.rt.deg; - } - var bbox = this.getBBox(); - deg = (deg + E)[split](separator); - if (deg[length] - 1) { - cx = toFloat(deg[1]); - cy = toFloat(deg[2]); - } - deg = toFloat(deg[0]); - if (cx != null) { - this._.rt.deg = deg; - } else { - this._.rt.deg += deg; - } - (cy == null) && (cx = null); - this._.rt.cx = cx; - this._.rt.cy = cy; - cx = cx == null ? bbox.x + bbox.width / 2 : cx; - cy = cy == null ? bbox.y + bbox.height / 2 : cy; - if (this._.rt.deg) { - this.transformations[0] = R.format("rotate({0} {1} {2})", this._.rt.deg, cx, cy); - this.clip && $(this.clip, {transform: R.format("rotate({0} {1} {2})", -this._.rt.deg, cx, cy)}); - } else { - this.transformations[0] = E; - this.clip && $(this.clip, {transform: E}); - } - $(this.node, {transform: this.transformations[join](S)}); - return this; - }; - Element[proto].hide = function () { - !this.removed && (this.node.style.display = "none"); - return this; - }; - Element[proto].show = function () { - !this.removed && (this.node.style.display = ""); - return this; - }; - Element[proto].remove = function () { - if (this.removed) { - return; - } - tear(this, this.paper); - this.node.parentNode.removeChild(this.node); - for (var i in this) { - delete this[i]; - } - this.removed = true; - }; - Element[proto].getBBox = function () { - if (this.removed) { - return this; - } - if (this.type == "path") { - return pathDimensions(this.attrs.path); - } - if (this.node.style.display == "none") { - this.show(); - var hide = true; - } - var bbox = {}; - try { - bbox = this.node.getBBox(); - } catch(e) { - // Firefox 3.0.x plays badly here - } finally { - bbox = bbox || {}; - } - if (this.type == "text") { - bbox = {x: bbox.x, y: Infinity, width: 0, height: 0}; - for (var i = 0, ii = this.node.getNumberOfChars(); i < ii; i++) { - var bb = this.node.getExtentOfChar(i); - (bb.y < bbox.y) && (bbox.y = bb.y); - (bb.y + bb.height - bbox.y > bbox.height) && (bbox.height = bb.y + bb.height - bbox.y); - (bb.x + bb.width - bbox.x > bbox.width) && (bbox.width = bb.x + bb.width - bbox.x); - } - } - hide && this.hide(); - return bbox; - }; - Element[proto].attr = function (name, value) { - if (this.removed) { - return this; - } - if (name == null) { - var res = {}; - for (var i in this.attrs) if (this.attrs[has](i)) { - res[i] = this.attrs[i]; - } - this._.rt.deg && (res.rotation = this.rotate()); - (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale()); - res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; - return res; - } - if (value == null && R.is(name, "string")) { - if (name == "translation") { - return translate.call(this); - } - if (name == "rotation") { - return this.rotate(); - } - if (name == "scale") { - return this.scale(); - } - if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) { - return this.attrs.gradient; - } - return this.attrs[name]; - } - if (value == null && R.is(name, "array")) { - var values = {}; - for (var j = 0, jj = name.length; j < jj; j++) { - values[name[j]] = this.attr(name[j]); - } - return values; - } - if (value != null) { - var params = {}; - params[name] = value; - setFillAndStroke(this, params); - } else if (name != null && R.is(name, "object")) { - setFillAndStroke(this, name); - } - return this; - }; - Element[proto].toFront = function () { - if (this.removed) { - return this; - } - this.node.parentNode[appendChild](this.node); - var svg = this.paper; - svg.top != this && tofront(this, svg); - return this; - }; - Element[proto].toBack = function () { - if (this.removed) { - return this; - } - if (this.node.parentNode.firstChild != this.node) { - this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild); - toback(this, this.paper); - var svg = this.paper; - } - return this; - }; - Element[proto].insertAfter = function (element) { - if (this.removed) { - return this; - } - var node = element.node; - if (node.nextSibling) { - node.parentNode.insertBefore(this.node, node.nextSibling); - } else { - node.parentNode[appendChild](this.node); - } - insertafter(this, element, this.paper); - return this; - }; - Element[proto].insertBefore = function (element) { - if (this.removed) { - return this; - } - var node = element.node; - node.parentNode.insertBefore(this.node, node); - insertbefore(this, element, this.paper); - return this; - }; - Element[proto].blur = function (size) { - // Experimental. No Safari support. Use it on your own risk. - var t = this; - if (+size !== 0) { - var fltr = $("filter"), - blur = $("feGaussianBlur"); - t.attrs.blur = size; - fltr.id = "r" + (R._id++)[toString](36); - $(blur, {stdDeviation: +size || 1.5}); - fltr.appendChild(blur); - t.paper.defs.appendChild(fltr); - t._blur = fltr; - $(t.node, {filter: "url(#" + fltr.id + ")"}); - } else { - if (t._blur) { - t._blur.parentNode.removeChild(t._blur); - delete t._blur; - delete t.attrs.blur; - } - t.node.removeAttribute("filter"); - } - }; - var theCircle = function (svg, x, y, r) { - x = round(x); - y = round(y); - var el = $("circle"); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"}; - res.type = "circle"; - $(el, res.attrs); - return res; - }; - var theRect = function (svg, x, y, w, h, r) { - x = round(x); - y = round(y); - var el = $("rect"); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"}; - res.type = "rect"; - $(el, res.attrs); - return res; - }; - var theEllipse = function (svg, x, y, rx, ry) { - x = round(x); - y = round(y); - var el = $("ellipse"); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"}; - res.type = "ellipse"; - $(el, res.attrs); - return res; - }; - var theImage = function (svg, src, x, y, w, h) { - var el = $("image"); - $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"}); - el.setAttributeNS(svg.xlink, "href", src); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {x: x, y: y, width: w, height: h, src: src}; - res.type = "image"; - return res; - }; - var theText = function (svg, x, y, text) { - var el = $("text"); - $(el, {x: x, y: y, "text-anchor": "middle"}); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {x: x, y: y, "text-anchor": "middle", text: text, font: availableAttrs.font, stroke: "none", fill: "#000"}; - res.type = "text"; - setFillAndStroke(res, res.attrs); - return res; - }; - var setSize = function (width, height) { - this.width = width || this.width; - this.height = height || this.height; - this.canvas[setAttribute]("width", this.width); - this.canvas[setAttribute]("height", this.height); - return this; - }; - var create = function () { - var con = getContainer[apply](0, arguments), - container = con && con.container, - x = con.x, - y = con.y, - width = con.width, - height = con.height; - if (!container) { - throw new Error("SVG container not found."); - } - var cnvs = $("svg"); - width = width || 512; - height = height || 342; - $(cnvs, { - xmlns: "http://www.w3.org/2000/svg", - version: 1.1, - width: width, - height: height - }); - if (container == 1) { - cnvs.style.cssText = "position:absolute;left:" + x + "px;top:" + y + "px"; - doc.body[appendChild](cnvs); - } else { - if (container.firstChild) { - container.insertBefore(cnvs, container.firstChild); - } else { - container[appendChild](cnvs); - } - } - container = new Paper; - container.width = width; - container.height = height; - container.canvas = cnvs; - plugins.call(container, container, R.fn); - container.clear(); - return container; - }; - Paper[proto].clear = function () { - var c = this.canvas; - while (c.firstChild) { - c.removeChild(c.firstChild); - } - this.bottom = this.top = null; - (this.desc = $("desc"))[appendChild](doc.createTextNode("Created with Rapha\xebl")); - c[appendChild](this.desc); - c[appendChild](this.defs = $("defs")); - }; - Paper[proto].remove = function () { - this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); - for (var i in this) { - this[i] = removed(i); - } - }; - } - - // VML - if (R.vml) { - var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"}, - bites = /([clmz]),?([^clmz]*)/gi, - val = /-?[^,\s-]+/g, - coordsize = 1e3 + S + 1e3, - zoom = 10, - path2vml = function (path) { - var total = /[ahqstv]/ig, - command = pathToAbsolute; - (path + E).match(total) && (command = path2curve); - total = /[clmz]/g; - if (command == pathToAbsolute && !(path + E).match(total)) { - var res = (path + E)[rp](bites, function (all, command, args) { - var vals = [], - isMove = lowerCase.call(command) == "m", - res = map[command]; - args[rp](val, function (value) { - if (isMove && vals[length] == 2) { - res += vals + map[command == "m" ? "l" : "L"]; - vals = []; - } - vals[push](round(value * zoom)); - }); - return res + vals; - }); - return res; - } - var pa = command(path), p, r; - res = []; - for (var i = 0, ii = pa[length]; i < ii; i++) { - p = pa[i]; - r = lowerCase.call(pa[i][0]); - r == "z" && (r = "x"); - for (var j = 1, jj = p[length]; j < jj; j++) { - r += round(p[j] * zoom) + (j != jj - 1 ? "," : E); - } - res[push](r); - } - return res[join](S); - }; - - R[toString] = function () { - return "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version; - }; - thePath = function (pathString, vml) { - var g = createNode("group"); - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = vml.coordsize; - g.coordorigin = vml.coordorigin; - var el = createNode("shape"), ol = el.style; - ol.width = vml.width + "px"; - ol.height = vml.height + "px"; - el.coordsize = coordsize; - el.coordorigin = vml.coordorigin; - g[appendChild](el); - var p = new Element(el, g, vml), - attr = {fill: "none", stroke: "#000"}; - pathString && (attr.path = pathString); - p.isAbsolute = true; - p.type = "path"; - p.path = []; - p.Path = E; - setFillAndStroke(p, attr); - vml.canvas[appendChild](g); - return p; - }; - setFillAndStroke = function (o, params) { - o.attrs = o.attrs || {}; - var node = o.node, - a = o.attrs, - s = node.style, - xy, - res = o; - for (var par in params) if (params[has](par)) { - a[par] = params[par]; - } - params.href && (node.href = params.href); - params.title && (node.title = params.title); - params.target && (node.target = params.target); - params.cursor && (s.cursor = params.cursor); - "blur" in params && o.blur(params.blur); - if (params.path && o.type == "path") { - a.path = params.path; - node.path = path2vml(a.path); - } - if (params.rotation != null) { - o.rotate(params.rotation, true); - } - if (params.translation) { - xy = (params.translation + E)[split](separator); - translate.call(o, xy[0], xy[1]); - if (o._.rt.cx != null) { - o._.rt.cx +=+ xy[0]; - o._.rt.cy +=+ xy[1]; - o.setBox(o.attrs, xy[0], xy[1]); - } - } - if (params.scale) { - xy = (params.scale + E)[split](separator); - o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null); - } - if ("clip-rect" in params) { - var rect = (params["clip-rect"] + E)[split](separator); - if (rect[length] == 4) { - rect[2] = +rect[2] + (+rect[0]); - rect[3] = +rect[3] + (+rect[1]); - var div = node.clipRect || doc.createElement("div"), - dstyle = div.style, - group = node.parentNode; - dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect); - if (!node.clipRect) { - dstyle.position = "absolute"; - dstyle.top = 0; - dstyle.left = 0; - dstyle.width = o.paper.width + "px"; - dstyle.height = o.paper.height + "px"; - group.parentNode.insertBefore(div, group); - div[appendChild](group); - node.clipRect = div; - } - } - if (!params["clip-rect"]) { - node.clipRect && (node.clipRect.style.clip = E); - } - } - if (o.type == "image" && params.src) { - node.src = params.src; - } - if (o.type == "image" && params.opacity) { - node.filterOpacity = " progid:DXImageTransform.Microsoft.Alpha(opacity=" + (params.opacity * 100) + ")"; - s.filter = (node.filterMatrix || E) + (node.filterOpacity || E); - } - params.font && (s.font = params.font); - params["font-family"] && (s.fontFamily = '"' + params["font-family"][split](",")[0][rp](/^['"]+|['"]+$/g, E) + '"'); //' - params["font-size"] && (s.fontSize = params["font-size"]); - params["font-weight"] && (s.fontWeight = params["font-weight"]); - params["font-style"] && (s.fontStyle = params["font-style"]); - if (params.opacity != null || - params["stroke-width"] != null || - params.fill != null || - params.stroke != null || - params["stroke-width"] != null || - params["stroke-opacity"] != null || - params["fill-opacity"] != null || - params["stroke-dasharray"] != null || - params["stroke-miterlimit"] != null || - params["stroke-linejoin"] != null || - params["stroke-linecap"] != null) { - node = o.shape || node; - var fill = (node.getElementsByTagName("fill") && node.getElementsByTagName("fill")[0]), - newfill = false; - !fill && (newfill = fill = createNode("fill")); - if ("fill-opacity" in params || "opacity" in params) { - var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1); - opacity < 0 && (opacity = 0); - opacity > 1 && (opacity = 1); - fill.opacity = opacity; - } - params.fill && (fill.on = true); - if (fill.on == null || params.fill == "none") { - fill.on = false; - } - if (fill.on && params.fill) { - var isURL = params.fill.match(ISURL); - if (isURL) { - fill.src = isURL[1]; - fill.type = "tile"; - } else { - fill.color = R.getRGB(params.fill).hex; - fill.src = E; - fill.type = "solid"; - if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || (params.fill + E).charAt() != "r") && addGradientFill(res, params.fill)) { - a.fill = "none"; - a.gradient = params.fill; - } - } - } - newfill && node[appendChild](fill); - var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]), - newstroke = false; - !stroke && (newstroke = stroke = createNode("stroke")); - if ((params.stroke && params.stroke != "none") || - params["stroke-width"] || - params["stroke-opacity"] != null || - params["stroke-dasharray"] || - params["stroke-miterlimit"] || - params["stroke-linejoin"] || - params["stroke-linecap"]) { - stroke.on = true; - } - (params.stroke == "none" || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false); - stroke.on && params.stroke && (stroke.color = R.getRGB(params.stroke).hex); - opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1); - var width = (toFloat(params["stroke-width"]) || 1) * .75; - opacity < 0 && (opacity = 0); - opacity > 1 && (opacity = 1); - params["stroke-width"] == null && (width = a["stroke-width"]); - params["stroke-width"] && (stroke.weight = width); - width && width < 1 && (opacity *= width) && (stroke.weight = 1); - stroke.opacity = opacity; - - params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter"); - stroke.miterlimit = params["stroke-miterlimit"] || 8; - params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round"); - if (params["stroke-dasharray"]) { - var dasharray = { - "-": "shortdash", - ".": "shortdot", - "-.": "shortdashdot", - "-..": "shortdashdotdot", - ". ": "dot", - "- ": "dash", - "--": "longdash", - "- .": "dashdot", - "--.": "longdashdot", - "--..": "longdashdotdot" - }; - stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E; - } - newstroke && node[appendChild](stroke); - } - if (res.type == "text") { - s = res.paper.span.style; - a.font && (s.font = a.font); - a["font-family"] && (s.fontFamily = a["font-family"]); - a["font-size"] && (s.fontSize = a["font-size"]); - a["font-weight"] && (s.fontWeight = a["font-weight"]); - a["font-style"] && (s.fontStyle = a["font-style"]); - res.node.string && (res.paper.span.innerHTML = (res.node.string + E)[rp](/")); - res.W = a.w = res.paper.span.offsetWidth; - res.H = a.h = res.paper.span.offsetHeight; - res.X = a.x; - res.Y = a.y + round(res.H / 2); - - // text-anchor emulationm - switch (a["text-anchor"]) { - case "start": - res.node.style["v-text-align"] = "left"; - res.bbx = round(res.W / 2); - break; - case "end": - res.node.style["v-text-align"] = "right"; - res.bbx = -round(res.W / 2); - break; - default: - res.node.style["v-text-align"] = "center"; - break; - } - } - }; - addGradientFill = function (o, gradient) { - o.attrs = o.attrs || {}; - var attrs = o.attrs, - fill = o.node.getElementsByTagName("fill"), - type = "linear", - fxfy = ".5 .5"; - o.attrs.gradient = gradient; - gradient = (gradient + E)[rp](radial_gradient, function (all, fx, fy) { - type = "radial"; - if (fx && fy) { - fx = toFloat(fx); - fy = toFloat(fy); - pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5); - fxfy = fx + S + fy; - } - return E; - }); - gradient = gradient[split](/\s*\-\s*/); - if (type == "linear") { - var angle = gradient.shift(); - angle = -toFloat(angle); - if (isNaN(angle)) { - return null; - } - } - var dots = parseDots(gradient); - if (!dots) { - return null; - } - o = o.shape || o.node; - fill = fill[0] || createNode("fill"); - if (dots[length]) { - fill.on = true; - fill.method = "none"; - fill.type = (type == "radial") ? "gradientradial" : "gradient"; - fill.color = dots[0].color; - fill.color2 = dots[dots[length] - 1].color; - var clrs = []; - for (var i = 0, ii = dots[length]; i < ii; i++) { - dots[i].offset && clrs[push](dots[i].offset + S + dots[i].color); - } - fill.colors && (fill.colors.value = clrs[length] ? clrs[join](",") : "0% " + fill.color); - if (type == "radial") { - fill.focus = "100%"; - fill.focussize = fxfy; - fill.focusposition = fxfy; - } else { - fill.angle = (270 - angle) % 360; - } - } - return 1; - }; - Element = function (node, group, vml) { - var Rotation = 0, - RotX = 0, - RotY = 0, - Scale = 1; - this[0] = node; - this.id = R._oid++; - this.node = node; - node.raphael = this; - this.X = 0; - this.Y = 0; - this.attrs = {}; - this.Group = group; - this.paper = vml; - this._ = { - tx: 0, - ty: 0, - rt: {deg:0}, - sx: 1, - sy: 1 - }; - !vml.bottom && (vml.bottom = this); - this.prev = vml.top; - vml.top && (vml.top.next = this); - vml.top = this; - this.next = null; - }; - Element[proto].rotate = function (deg, cx, cy) { - if (this.removed) { - return this; - } - if (deg == null) { - if (this._.rt.cx) { - return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S); - } - return this._.rt.deg; - } - deg = (deg + E)[split](separator); - if (deg[length] - 1) { - cx = toFloat(deg[1]); - cy = toFloat(deg[2]); - } - deg = toFloat(deg[0]); - if (cx != null) { - this._.rt.deg = deg; - } else { - this._.rt.deg += deg; - } - cy == null && (cx = null); - this._.rt.cx = cx; - this._.rt.cy = cy; - this.setBox(this.attrs, cx, cy); - this.Group.style.rotation = this._.rt.deg; - // gradient fix for rotation. TODO - // var fill = (this.shape || this.node).getElementsByTagName("fill"); - // fill = fill[0] || {}; - // var b = ((360 - this._.rt.deg) - 270) % 360; - // !R.is(fill.angle, "undefined") && (fill.angle = b); - return this; - }; - Element[proto].setBox = function (params, cx, cy) { - if (this.removed) { - return this; - } - var gs = this.Group.style, - os = (this.shape && this.shape.style) || this.node.style; - params = params || {}; - for (var i in params) if (params[has](i)) { - this.attrs[i] = params[i]; - } - cx = cx || this._.rt.cx; - cy = cy || this._.rt.cy; - var attr = this.attrs, - x, - y, - w, - h; - switch (this.type) { - case "circle": - x = attr.cx - attr.r; - y = attr.cy - attr.r; - w = h = attr.r * 2; - break; - case "ellipse": - x = attr.cx - attr.rx; - y = attr.cy - attr.ry; - w = attr.rx * 2; - h = attr.ry * 2; - break; - case "rect": - case "image": - x = +attr.x; - y = +attr.y; - w = attr.width || 0; - h = attr.height || 0; - break; - case "text": - this.textpath.v = ["m", round(attr.x), ", ", round(attr.y - 2), "l", round(attr.x) + 1, ", ", round(attr.y - 2)][join](E); - x = attr.x - round(this.W / 2); - y = attr.y - this.H / 2; - w = this.W; - h = this.H; - break; - case "path": - if (!this.attrs.path) { - x = 0; - y = 0; - w = this.paper.width; - h = this.paper.height; - } else { - var dim = pathDimensions(this.attrs.path); - x = dim.x; - y = dim.y; - w = dim.width; - h = dim.height; - } - break; - default: - x = 0; - y = 0; - w = this.paper.width; - h = this.paper.height; - break; - } - cx = (cx == null) ? x + w / 2 : cx; - cy = (cy == null) ? y + h / 2 : cy; - var left = cx - this.paper.width / 2, - top = cy - this.paper.height / 2, t; - gs.left != (t = left + "px") && (gs.left = t); - gs.top != (t = top + "px") && (gs.top = t); - this.X = this.type == "path" ? -left : x; - this.Y = this.type == "path" ? -top : y; - this.W = w; - this.H = h; - if (this.type == "path") { - os.left != (t = -left * zoom + "px") && (os.left = t); - os.top != (t = -top * zoom + "px") && (os.top = t); - } else if (this.type == "text") { - os.left != (t = -left + "px") && (os.left = t); - os.top != (t = -top + "px") && (os.top = t); - } else { - gs.width != (t = this.paper.width + "px") && (gs.width = t); - gs.height != (t = this.paper.height + "px") && (gs.height = t); - os.left != (t = x - left + "px") && (os.left = t); - os.top != (t = y - top + "px") && (os.top = t); - os.width != (t = w + "px") && (os.width = t); - os.height != (t = h + "px") && (os.height = t); - var arcsize = (+params.r || 0) / mmin(w, h); - if (this.type == "rect" && this.arcsize.toFixed(4) != arcsize.toFixed(4) && (arcsize || this.arcsize)) { - // We should replace element with the new one - var o = createNode("roundrect"), - a = {}, - ii = this.events && this.events[length]; - i = 0; - o.arcsize = arcsize; - o.raphael = this; - this.Group[appendChild](o); - this.Group.removeChild(this.node); - this[0] = this.node = o; - this.arcsize = arcsize; - for (i in attr) { - a[i] = attr[i]; - } - delete a.scale; - this.attr(a); - if (this.events) for (; i < ii; i++) { - this.events[i].unbind = addEvent(this.node, this.events[i].name, this.events[i].f, this); - } - } - } - }; - Element[proto].hide = function () { - !this.removed && (this.Group.style.display = "none"); - return this; - }; - Element[proto].show = function () { - !this.removed && (this.Group.style.display = "block"); - return this; - }; - Element[proto].getBBox = function () { - if (this.removed) { - return this; - } - if (this.type == "path") { - return pathDimensions(this.attrs.path); - } - return { - x: this.X + (this.bbx || 0), - y: this.Y, - width: this.W, - height: this.H - }; - }; - Element[proto].remove = function () { - if (this.removed) { - return; - } - tear(this, this.paper); - this.node.parentNode.removeChild(this.node); - this.Group.parentNode.removeChild(this.Group); - this.shape && this.shape.parentNode.removeChild(this.shape); - for (var i in this) { - delete this[i]; - } - this.removed = true; - }; - Element[proto].attr = function (name, value) { - if (this.removed) { - return this; - } - if (name == null) { - var res = {}; - for (var i in this.attrs) if (this.attrs[has](i)) { - res[i] = this.attrs[i]; - } - this._.rt.deg && (res.rotation = this.rotate()); - (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale()); - res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; - return res; - } - if (value == null && R.is(name, "string")) { - if (name == "translation") { - return translate.call(this); - } - if (name == "rotation") { - return this.rotate(); - } - if (name == "scale") { - return this.scale(); - } - if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) { - return this.attrs.gradient; - } - return this.attrs[name]; - } - if (this.attrs && value == null && R.is(name, "array")) { - var ii, values = {}; - for (i = 0, ii = name[length]; i < ii; i++) { - values[name[i]] = this.attr(name[i]); - } - return values; - } - var params; - if (value != null) { - params = {}; - params[name] = value; - } - value == null && R.is(name, "object") && (params = name); - if (params) { - if (params.text && this.type == "text") { - this.node.string = params.text; - } - setFillAndStroke(this, params); - if (params.gradient && (({circle: 1, ellipse: 1})[has](this.type) || (params.gradient + E).charAt() != "r")) { - addGradientFill(this, params.gradient); - } - (this.type != "path" || this._.rt.deg) && this.setBox(this.attrs); - } - return this; - }; - Element[proto].toFront = function () { - !this.removed && this.Group.parentNode[appendChild](this.Group); - this.paper.top != this && tofront(this, this.paper); - return this; - }; - Element[proto].toBack = function () { - if (this.removed) { - return this; - } - if (this.Group.parentNode.firstChild != this.Group) { - this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild); - toback(this, this.paper); - } - return this; - }; - Element[proto].insertAfter = function (element) { - if (this.removed) { - return this; - } - if (element.Group.nextSibling) { - element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling); - } else { - element.Group.parentNode[appendChild](this.Group); - } - insertafter(this, element, this.paper); - return this; - }; - Element[proto].insertBefore = function (element) { - if (this.removed) { - return this; - } - element.Group.parentNode.insertBefore(this.Group, element.Group); - insertbefore(this, element, this.paper); - return this; - }; - var blurregexp = / progid:\S+Blur\([^\)]+\)/g; - Element[proto].blur = function (size) { - var s = this.node.style, - f = s.filter; - f = f.replace(blurregexp, ""); - if (+size !== 0) { - this.attrs.blur = size; - s.filter = f + " progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (+size || 1.5) + ")"; - s.margin = Raphael.format("-{0}px 0 0 -{0}px", Math.round(+size || 1.5)); - } else { - s.filter = f; - s.margin = 0; - delete this.attrs.blur; - } - }; - - theCircle = function (vml, x, y, r) { - var g = createNode("group"), - o = createNode("oval"), - ol = o.style; - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - g[appendChild](o); - var res = new Element(o, g, vml); - res.type = "circle"; - setFillAndStroke(res, {stroke: "#000", fill: "none"}); - res.attrs.cx = x; - res.attrs.cy = y; - res.attrs.r = r; - res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2}); - vml.canvas[appendChild](g); - return res; - }; - theRect = function (vml, x, y, w, h, r) { - var g = createNode("group"), - o = createNode("roundrect"), - arcsize = (+r || 0) / (mmin(w, h)); - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - g[appendChild](o); - o.arcsize = arcsize; - var res = new Element(o, g, vml); - res.type = "rect"; - setFillAndStroke(res, {stroke: "#000"}); - res.arcsize = arcsize; - res.setBox({x: x, y: y, width: w, height: h, r: r}); - vml.canvas[appendChild](g); - return res; - }; - theEllipse = function (vml, x, y, rx, ry) { - var g = createNode("group"), - o = createNode("oval"), - ol = o.style; - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - g[appendChild](o); - var res = new Element(o, g, vml); - res.type = "ellipse"; - setFillAndStroke(res, {stroke: "#000"}); - res.attrs.cx = x; - res.attrs.cy = y; - res.attrs.rx = rx; - res.attrs.ry = ry; - res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2}); - vml.canvas[appendChild](g); - return res; - }; - theImage = function (vml, src, x, y, w, h) { - var g = createNode("group"), - o = createNode("image"), - ol = o.style; - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - o.src = src; - g[appendChild](o); - var res = new Element(o, g, vml); - res.type = "image"; - res.attrs.src = src; - res.attrs.x = x; - res.attrs.y = y; - res.attrs.w = w; - res.attrs.h = h; - res.setBox({x: x, y: y, width: w, height: h}); - vml.canvas[appendChild](g); - return res; - }; - theText = function (vml, x, y, text) { - var g = createNode("group"), - el = createNode("shape"), - ol = el.style, - path = createNode("path"), - ps = path.style, - o = createNode("textpath"); - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - path.v = R.format("m{0},{1}l{2},{1}", round(x * 10), round(y * 10), round(x * 10) + 1); - path.textpathok = true; - ol.width = vml.width; - ol.height = vml.height; - o.string = text + E; - o.on = true; - el[appendChild](o); - el[appendChild](path); - g[appendChild](el); - var res = new Element(o, g, vml); - res.shape = el; - res.textpath = path; - res.type = "text"; - res.attrs.text = text; - res.attrs.x = x; - res.attrs.y = y; - res.attrs.w = 1; - res.attrs.h = 1; - setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000"}); - res.setBox(); - vml.canvas[appendChild](g); - return res; - }; - setSize = function (width, height) { - var cs = this.canvas.style; - width == +width && (width += "px"); - height == +height && (height += "px"); - cs.width = width; - cs.height = height; - cs.clip = "rect(0 " + width + " " + height + " 0)"; - return this; - }; - var createNode; - doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); - try { - !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); - createNode = function (tagName) { - return doc.createElement(''); - }; - } catch (e) { - createNode = function (tagName) { - return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); - }; - } - create = function () { - var con = getContainer[apply](0, arguments), - container = con.container, - height = con.height, - s, - width = con.width, - x = con.x, - y = con.y; - if (!container) { - throw new Error("VML container not found."); - } - var res = new Paper, - c = res.canvas = doc.createElement("div"), - cs = c.style; - width = width || 512; - height = height || 342; - width == +width && (width += "px"); - height == +height && (height += "px"); - res.width = 1e3; - res.height = 1e3; - res.coordsize = zoom * 1e3 + S + zoom * 1e3; - res.coordorigin = "0 0"; - res.span = doc.createElement("span"); - res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;"; - c[appendChild](res.span); - cs.cssText = R.format("width:{0};height:{1};position:absolute;clip:rect(0 {0} {1} 0);overflow:hidden", width, height); - if (container == 1) { - doc.body[appendChild](c); - cs.left = x + "px"; - cs.top = y + "px"; - } else { - container.style.width = width; - container.style.height = height; - if (container.firstChild) { - container.insertBefore(c, container.firstChild); - } else { - container[appendChild](c); - } - } - plugins.call(res, res, R.fn); - return res; - }; - Paper[proto].clear = function () { - this.canvas.innerHTML = E; - this.span = doc.createElement("span"); - this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;"; - this.canvas[appendChild](this.span); - this.bottom = this.top = null; - }; - Paper[proto].remove = function () { - this.canvas.parentNode.removeChild(this.canvas); - for (var i in this) { - this[i] = removed(i); - } - return true; - }; - } - - // rest - // Safari or Chrome (WebKit) rendering bug workaround method - if ((/^Apple|^Google/).test(win.navigator.vendor) && !(win.navigator.userAgent.indexOf("Version/4.0") + 1)) { - Paper[proto].safari = function () { - var rect = this.rect(-99, -99, this.width + 99, this.height + 99); - win.setTimeout(function () {rect.remove();}); - }; - } else { - Paper[proto].safari = function () {}; - } - - // Events - var addEvent = (function () { - if (doc.addEventListener) { - return function (obj, type, fn, element) { - var f = function (e) { - return fn.call(element, e); - }; - obj.addEventListener(type, f, false); - return function () { - obj.removeEventListener(type, f, false); - return true; - }; - }; - } else if (doc.attachEvent) { - return function (obj, type, fn, element) { - var f = function (e) { - return fn.call(element, e || win.event); - }; - obj.attachEvent("on" + type, f); - var detacher = function () { - obj.detachEvent("on" + type, f); - return true; - }; - return detacher; - }; - } - })(); - for (var i = events[length]; i--;) { - (function (eventName) { - Element[proto][eventName] = function (fn) { - if (R.is(fn, "function")) { - this.events = this.events || []; - this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node, eventName, fn, this)}); - } - return this; - }; - Element[proto]["un" + eventName] = function (fn) { - var events = this.events, - l = events[length]; - while (l--) if (events[l].name == eventName && events[l].f == fn) { - events[l].unbind(); - events.splice(l, 1); - !events.length && delete this.events; - return this; - } - return this; - }; - })(events[i]); - } - Element[proto].hover = function (f_in, f_out) { - return this.mouseover(f_in).mouseout(f_out); - }; - Element[proto].unhover = function (f_in, f_out) { - return this.unmouseover(f_in).unmouseout(f_out); - }; - Paper[proto].circle = function (x, y, r) { - return theCircle(this, x || 0, y || 0, r || 0); - }; - Paper[proto].rect = function (x, y, w, h, r) { - return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0); - }; - Paper[proto].ellipse = function (x, y, rx, ry) { - return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0); - }; - Paper[proto].path = function (pathString) { - pathString && !R.is(pathString, "string") && !R.is(pathString[0], "array") && (pathString += E); - return thePath(R.format[apply](R, arguments), this); - }; - Paper[proto].image = function (src, x, y, w, h) { - return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0); - }; - Paper[proto].text = function (x, y, text) { - return theText(this, x || 0, y || 0, text || E); - }; - Paper[proto].set = function (itemsArray) { - arguments[length] > 1 && (itemsArray = Array[proto].splice.call(arguments, 0, arguments[length])); - return new Set(itemsArray); - }; - Paper[proto].setSize = setSize; - Paper[proto].top = Paper[proto].bottom = null; - Paper[proto].raphael = R; - function x_y() { - return this.x + S + this.y; - } - Element[proto].scale = function (x, y, cx, cy) { - if (x == null && y == null) { - return { - x: this._.sx, - y: this._.sy, - toString: x_y - }; - } - y = y || x; - !+y && (y = x); - var dx, - dy, - dcx, - dcy, - a = this.attrs; - if (x != 0) { - var bb = this.getBBox(), - rcx = bb.x + bb.width / 2, - rcy = bb.y + bb.height / 2, - kx = x / this._.sx, - ky = y / this._.sy; - cx = (+cx || cx == 0) ? cx : rcx; - cy = (+cy || cy == 0) ? cy : rcy; - var dirx = ~~(x / math.abs(x)), - diry = ~~(y / math.abs(y)), - s = this.node.style, - ncx = cx + (rcx - cx) * kx, - ncy = cy + (rcy - cy) * ky; - switch (this.type) { - case "rect": - case "image": - var neww = a.width * dirx * kx, - newh = a.height * diry * ky; - this.attr({ - height: newh, - r: a.r * mmin(dirx * kx, diry * ky), - width: neww, - x: ncx - neww / 2, - y: ncy - newh / 2 - }); - break; - case "circle": - case "ellipse": - this.attr({ - rx: a.rx * dirx * kx, - ry: a.ry * diry * ky, - r: a.r * mmin(dirx * kx, diry * ky), - cx: ncx, - cy: ncy - }); - break; - case "path": - var path = pathToRelative(a.path), - skip = true; - for (var i = 0, ii = path[length]; i < ii; i++) { - var p = path[i], - P0 = upperCase.call(p[0]); - if (P0 == "M" && skip) { - continue; - } else { - skip = false; - } - if (P0 == "A") { - p[path[i][length] - 2] *= kx; - p[path[i][length] - 1] *= ky; - p[1] *= dirx * kx; - p[2] *= diry * ky; - p[5] = +!(dirx + diry ? !+p[5] : +p[5]); - } else if (P0 == "H") { - for (var j = 1, jj = p[length]; j < jj; j++) { - p[j] *= kx; - } - } else if (P0 == "V") { - for (j = 1, jj = p[length]; j < jj; j++) { - p[j] *= ky; - } - } else { - for (j = 1, jj = p[length]; j < jj; j++) { - p[j] *= (j % 2) ? kx : ky; - } - } - } - var dim2 = pathDimensions(path); - dx = ncx - dim2.x - dim2.width / 2; - dy = ncy - dim2.y - dim2.height / 2; - path[0][1] += dx; - path[0][2] += dy; - this.attr({path: path}); - break; - } - if (this.type in {text: 1, image:1} && (dirx != 1 || diry != 1)) { - if (this.transformations) { - this.transformations[2] = "scale("[concat](dirx, ",", diry, ")"); - this.node[setAttribute]("transform", this.transformations[join](S)); - dx = (dirx == -1) ? -a.x - (neww || 0) : a.x; - dy = (diry == -1) ? -a.y - (newh || 0) : a.y; - this.attr({x: dx, y: dy}); - a.fx = dirx - 1; - a.fy = diry - 1; - } else { - this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11="[concat](dirx, - ", M12=0, M21=0, M22=", diry, - ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')"); - s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E); - } - } else { - if (this.transformations) { - this.transformations[2] = E; - this.node[setAttribute]("transform", this.transformations[join](S)); - a.fx = 0; - a.fy = 0; - } else { - this.node.filterMatrix = E; - s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E); - } - } - a.scale = [x, y, cx, cy][join](S); - this._.sx = x; - this._.sy = y; - } - return this; - }; - Element[proto].clone = function () { - var attr = this.attr(); - delete attr.scale; - delete attr.translation; - return this.paper[this.type]().attr(attr); - }; - var getPointAtSegmentLength = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { - var len = 0, - old; - for (var i = 0; i < 1.001; i+=.001) { - var dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i); - i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5)); - if (len >= length) { - return dot; - } - old = dot; - } - }), - getLengthFactory = function (istotal, subpath) { - return function (path, length, onlystart) { - path = path2curve(path); - var x, y, p, l, sp = "", subpaths = {}, point, - len = 0; - for (var i = 0, ii = path.length; i < ii; i++) { - p = path[i]; - if (p[0] == "M") { - x = +p[1]; - y = +p[2]; - } else { - l = segmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); - if (len + l > length) { - if (subpath && !subpaths.start) { - point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); - sp += ["C", point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y]; - if (onlystart) {return sp;} - subpaths.start = sp; - sp = ["M", point.x, point.y + "C", point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]][join](); - len += l; - x = +p[5]; - y = +p[6]; - continue; - } - if (!istotal && !subpath) { - point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); - return {x: point.x, y: point.y, alpha: point.alpha}; - } - } - len += l; - x = +p[5]; - y = +p[6]; - } - sp += p; - } - subpaths.end = sp; - point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], 1); - point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha}); - return point; - }; - }, - segmentLength = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { - var old = {x: 0, y: 0}, - len = 0; - for (var i = 0; i < 1.01; i+=.01) { - var dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i); - i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5)); - old = dot; - } - return len; - }); - var getTotalLength = getLengthFactory(1), - getPointAtLength = getLengthFactory(), - getSubpathsAtLength = getLengthFactory(0, 1); - Element[proto].getTotalLength = function () { - if (this.type != "path") {return;} - return getTotalLength(this.attrs.path); - }; - Element[proto].getPointAtLength = function (length) { - if (this.type != "path") {return;} - return getPointAtLength(this.attrs.path, length); - }; - Element[proto].getSubpath = function (from, to) { - if (this.type != "path") {return;} - if (math.abs(this.getTotalLength() - to) < 1e-6) { - return getSubpathsAtLength(this.attrs.path, from).end; - } - var a = getSubpathsAtLength(this.attrs.path, to, 1); - return from ? getSubpathsAtLength(a, from).end : a; - }; - - // animation easing formulas - R.easing_formulas = { - linear: function (n) { - return n; - }, - "<": function (n) { - return pow(n, 3); - }, - ">": function (n) { - return pow(n - 1, 3) + 1; - }, - "<>": function (n) { - n = n * 2; - if (n < 1) { - return pow(n, 3) / 2; - } - n -= 2; - return (pow(n, 3) + 2) / 2; - }, - backIn: function (n) { - var s = 1.70158; - return n * n * ((s + 1) * n - s); - }, - backOut: function (n) { - n = n - 1; - var s = 1.70158; - return n * n * ((s + 1) * n + s) + 1; - }, - elastic: function (n) { - if (n == 0 || n == 1) { - return n; - } - var p = .3, - s = p / 4; - return pow(2, -10 * n) * math.sin((n - s) * (2 * math.PI) / p) + 1; - }, - bounce: function (n) { - var s = 7.5625, - p = 2.75, - l; - if (n < (1 / p)) { - l = s * n * n; - } else { - if (n < (2 / p)) { - n -= (1.5 / p); - l = s * n * n + .75; - } else { - if (n < (2.5 / p)) { - n -= (2.25 / p); - l = s * n * n + .9375; - } else { - n -= (2.625 / p); - l = s * n * n + .984375; - } - } - } - return l; - } - }; - - var animationElements = {length : 0}, - animation = function () { - var Now = +new Date; - for (var l in animationElements) if (l != "length" && animationElements[has](l)) { - var e = animationElements[l]; - if (e.stop || e.el.removed) { - delete animationElements[l]; - animationElements[length]--; - continue; - } - var time = Now - e.start, - ms = e.ms, - easing = e.easing, - from = e.from, - diff = e.diff, - to = e.to, - t = e.t, - prev = e.prev || 0, - that = e.el, - callback = e.callback, - set = {}, - now; - if (time < ms) { - var pos = R.easing_formulas[easing] ? R.easing_formulas[easing](time / ms) : time / ms; - for (var attr in from) if (from[has](attr)) { - switch (availableAnimAttrs[attr]) { - case "along": - now = pos * ms * diff[attr]; - to.back && (now = to.len - now); - var point = getPointAtLength(to[attr], now); - that.translate(diff.sx - diff.x || 0, diff.sy - diff.y || 0); - diff.x = point.x; - diff.y = point.y; - that.translate(point.x - diff.sx, point.y - diff.sy); - to.rot && that.rotate(diff.r + point.alpha, point.x, point.y); - break; - case "number": - now = +from[attr] + pos * ms * diff[attr]; - break; - case "colour": - now = "rgb(" + [ - upto255(round(from[attr].r + pos * ms * diff[attr].r)), - upto255(round(from[attr].g + pos * ms * diff[attr].g)), - upto255(round(from[attr].b + pos * ms * diff[attr].b)) - ][join](",") + ")"; - break; - case "path": - now = []; - for (var i = 0, ii = from[attr][length]; i < ii; i++) { - now[i] = [from[attr][i][0]]; - for (var j = 1, jj = from[attr][i][length]; j < jj; j++) { - now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j]; - } - now[i] = now[i][join](S); - } - now = now[join](S); - break; - case "csv": - switch (attr) { - case "translation": - var x = diff[attr][0] * (time - prev), - y = diff[attr][1] * (time - prev); - t.x += x; - t.y += y; - now = x + S + y; - break; - case "rotation": - now = +from[attr][0] + pos * ms * diff[attr][0]; - from[attr][1] && (now += "," + from[attr][1] + "," + from[attr][2]); - break; - case "scale": - now = [+from[attr][0] + pos * ms * diff[attr][0], +from[attr][1] + pos * ms * diff[attr][1], (2 in to[attr] ? to[attr][2] : E), (3 in to[attr] ? to[attr][3] : E)][join](S); - break; - case "clip-rect": - now = []; - i = 4; - while (i--) { - now[i] = +from[attr][i] + pos * ms * diff[attr][i]; - } - break; - } - break; - } - set[attr] = now; - } - that.attr(set); - that._run && that._run.call(that); - } else { - if (to.along) { - point = getPointAtLength(to.along, to.len * !to.back); - that.translate(diff.sx - (diff.x || 0) + point.x - diff.sx, diff.sy - (diff.y || 0) + point.y - diff.sy); - to.rot && that.rotate(diff.r + point.alpha, point.x, point.y); - } - (t.x || t.y) && that.translate(-t.x, -t.y); - to.scale && (to.scale = to.scale + E); - that.attr(to); - delete animationElements[l]; - animationElements[length]--; - that.in_animation = null; - R.is(callback, "function") && callback.call(that); - } - e.prev = time; - } - R.svg && that && that.paper.safari(); - animationElements[length] && win.setTimeout(animation); - }, - upto255 = function (color) { - return color > 255 ? 255 : (color < 0 ? 0 : color); - }, - translate = function (x, y) { - if (x == null) { - return {x: this._.tx, y: this._.ty, toString: x_y}; - } - this._.tx += +x; - this._.ty += +y; - switch (this.type) { - case "circle": - case "ellipse": - this.attr({cx: +x + this.attrs.cx, cy: +y + this.attrs.cy}); - break; - case "rect": - case "image": - case "text": - this.attr({x: +x + this.attrs.x, y: +y + this.attrs.y}); - break; - case "path": - var path = pathToRelative(this.attrs.path); - path[0][1] += +x; - path[0][2] += +y; - this.attr({path: path}); - break; - } - return this; - }; - Element[proto].animateWith = function (element, params, ms, easing, callback) { - animationElements[element.id] && (params.start = animationElements[element.id].start); - return this.animate(params, ms, easing, callback); - }; - Element[proto].animateAlong = along(); - Element[proto].animateAlongBack = along(1); - function along(isBack) { - return function (path, ms, rotate, callback) { - var params = {back: isBack}; - R.is(rotate, "function") ? (callback = rotate) : (params.rot = rotate); - path && path.constructor == Element && (path = path.attrs.path); - path && (params.along = path); - return this.animate(params, ms, callback); - }; - } - Element[proto].onAnimation = function (f) { - this._run = f || 0; - return this; - }; - Element[proto].animate = function (params, ms, easing, callback) { - if (R.is(easing, "function") || !easing) { - callback = easing || null; - } - var from = {}, - to = {}, - diff = {}; - for (var attr in params) if (params[has](attr)) { - if (availableAnimAttrs[has](attr)) { - from[attr] = this.attr(attr); - (from[attr] == null) && (from[attr] = availableAttrs[attr]); - to[attr] = params[attr]; - switch (availableAnimAttrs[attr]) { - case "along": - var len = getTotalLength(params[attr]), - point = getPointAtLength(params[attr], len * !!params.back), - bb = this.getBBox(); - diff[attr] = len / ms; - diff.tx = bb.x; - diff.ty = bb.y; - diff.sx = point.x; - diff.sy = point.y; - to.rot = params.rot; - to.back = params.back; - to.len = len; - params.rot && (diff.r = toFloat(this.rotate()) || 0); - break; - case "number": - diff[attr] = (to[attr] - from[attr]) / ms; - break; - case "colour": - from[attr] = R.getRGB(from[attr]); - var toColour = R.getRGB(to[attr]); - diff[attr] = { - r: (toColour.r - from[attr].r) / ms, - g: (toColour.g - from[attr].g) / ms, - b: (toColour.b - from[attr].b) / ms - }; - break; - case "path": - var pathes = path2curve(from[attr], to[attr]); - from[attr] = pathes[0]; - var toPath = pathes[1]; - diff[attr] = []; - for (var i = 0, ii = from[attr][length]; i < ii; i++) { - diff[attr][i] = [0]; - for (var j = 1, jj = from[attr][i][length]; j < jj; j++) { - diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms; - } - } - break; - case "csv": - var values = (params[attr] + E)[split](separator), - from2 = (from[attr] + E)[split](separator); - switch (attr) { - case "translation": - from[attr] = [0, 0]; - diff[attr] = [values[0] / ms, values[1] / ms]; - break; - case "rotation": - from[attr] = (from2[1] == values[1] && from2[2] == values[2]) ? from2 : [0, values[1], values[2]]; - diff[attr] = [(values[0] - from[attr][0]) / ms, 0, 0]; - break; - case "scale": - params[attr] = values; - from[attr] = (from[attr] + E)[split](separator); - diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][1]) / ms, 0, 0]; - break; - case "clip-rect": - from[attr] = (from[attr] + E)[split](separator); - diff[attr] = []; - i = 4; - while (i--) { - diff[attr][i] = (values[i] - from[attr][i]) / ms; - } - break; - } - to[attr] = values; - } - } - } - this.stop(); - this.in_animation = 1; - animationElements[this.id] = { - start: params.start || +new Date, - ms: ms, - easing: easing, - from: from, - diff: diff, - to: to, - el: this, - callback: callback, - t: {x: 0, y: 0} - }; - ++animationElements[length] == 1 && animation(); - return this; - }; - Element[proto].stop = function () { - animationElements[this.id] && animationElements[length]--; - delete animationElements[this.id]; - return this; - }; - Element[proto].translate = function (x, y) { - return this.attr({translation: x + " " + y}); - }; - Element[proto][toString] = function () { - return "Rapha\xebl\u2019s object"; - }; - R.ae = animationElements; - - // Set - var Set = function (items) { - this.items = []; - this[length] = 0; - if (items) { - for (var i = 0, ii = items[length]; i < ii; i++) { - if (items[i] && (items[i].constructor == Element || items[i].constructor == Set)) { - this[this.items[length]] = this.items[this.items[length]] = items[i]; - this[length]++; - } - } - } - }; - Set[proto][push] = function () { - var item, - len; - for (var i = 0, ii = arguments[length]; i < ii; i++) { - item = arguments[i]; - if (item && (item.constructor == Element || item.constructor == Set)) { - len = this.items[length]; - this[len] = this.items[len] = item; - this[length]++; - } - } - return this; - }; - Set[proto].pop = function () { - delete this[this[length]--]; - return this.items.pop(); - }; - for (var method in Element[proto]) if (Element[proto][has](method)) { - Set[proto][method] = (function (methodname) { - return function () { - for (var i = 0, ii = this.items[length]; i < ii; i++) { - this.items[i][methodname][apply](this.items[i], arguments); - } - return this; - }; - })(method); - } - Set[proto].attr = function (name, value) { - if (name && R.is(name, "array") && R.is(name[0], "object")) { - for (var j = 0, jj = name[length]; j < jj; j++) { - this.items[j].attr(name[j]); - } - } else { - for (var i = 0, ii = this.items[length]; i < ii; i++) { - this.items[i].attr(name, value); - } - } - return this; - }; - Set[proto].animate = function (params, ms, easing, callback) { - (R.is(easing, "function") || !easing) && (callback = easing || null); - var len = this.items[length], - i = len, - set = this, - collector; - callback && (collector = function () { - !--len && callback.call(set); - }); - this.items[--i].animate(params, ms, easing || collector, collector); - while (i--) { - this.items[i].animateWith(this.items[len - 1], params, ms, easing || collector, collector); - } - return this; - }; - Set[proto].insertAfter = function (el) { - var i = this.items[length]; - while (i--) { - this.items[i].insertAfter(el); - } - return this; - }; - Set[proto].getBBox = function () { - var x = [], - y = [], - w = [], - h = []; - for (var i = this.items[length]; i--;) { - var box = this.items[i].getBBox(); - x[push](box.x); - y[push](box.y); - w[push](box.x + box.width); - h[push](box.y + box.height); - } - x = mmin[apply](0, x); - y = mmin[apply](0, y); - return { - x: x, - y: y, - width: mmax[apply](0, w) - x, - height: mmax[apply](0, h) - y - }; - }; - Set[proto].clone = function (s) { - s = new Set; - for (var i = 0, ii = this.items[length]; i < ii; i++) { - s[push](this.items[i].clone()); - } - return s; - }; - - R.registerFont = function (font) { - if (!font.face) { - return font; - } - this.fonts = this.fonts || {}; - var fontcopy = { - w: font.w, - face: {}, - glyphs: {} - }, - family = font.face["font-family"]; - for (var prop in font.face) if (font.face[has](prop)) { - fontcopy.face[prop] = font.face[prop]; - } - if (this.fonts[family]) { - this.fonts[family][push](fontcopy); - } else { - this.fonts[family] = [fontcopy]; - } - if (!font.svg) { - fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10); - for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) { - var path = font.glyphs[glyph]; - fontcopy.glyphs[glyph] = { - w: path.w, - k: {}, - d: path.d && "M" + path.d[rp](/[mlcxtrv]/g, function (command) { - return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M"; - }) + "z" - }; - if (path.k) { - for (var k in path.k) if (path[has](k)) { - fontcopy.glyphs[glyph].k[k] = path.k[k]; - } - } - } - } - return font; - }; - Paper[proto].getFont = function (family, weight, style, stretch) { - stretch = stretch || "normal"; - style = style || "normal"; - weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400; - var font = R.fonts[family]; - if (!font) { - var name = new RegExp("(^|\\s)" + family[rp](/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i"); - for (var fontName in R.fonts) if (R.fonts[has](fontName)) { - if (name.test(fontName)) { - font = R.fonts[fontName]; - break; - } - } - } - var thefont; - if (font) { - for (var i = 0, ii = font[length]; i < ii; i++) { - thefont = font[i]; - if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) { - break; - } - } - } - return thefont; - }; - Paper[proto].print = function (x, y, string, font, size, origin) { - origin = origin || "middle"; // baseline|middle - var out = this.set(), - letters = (string + E)[split](E), - shift = 0, - path = E, - scale; - R.is(font, "string") && (font = this.getFont(font)); - if (font) { - scale = (size || 16) / font.face["units-per-em"]; - var bb = font.face.bbox.split(separator), - top = +bb[0], - height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2); - for (var i = 0, ii = letters[length]; i < ii; i++) { - var prev = i && font.glyphs[letters[i - 1]] || {}, - curr = font.glyphs[letters[i]]; - shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) : 0; - curr && curr.d && out[push](this.path(curr.d).attr({fill: "#000", stroke: "none", translation: [shift, 0]})); - } - out.scale(scale, scale, top, height).translate(x - top, y - height); - } - return out; - }; - - var formatrg = /\{(\d+)\}/g; - R.format = function (token, array) { - var args = R.is(array, "array") ? [0][concat](array) : arguments; - token && R.is(token, "string") && args[length] - 1 && (token = token[rp](formatrg, function (str, i) { - return args[++i] == null ? E : args[i]; - })); - return token || E; - }; - R.ninja = function () { - oldRaphael.was ? (Raphael = oldRaphael.is) : delete Raphael; - return R; - }; - R.el = Element[proto]; - return R; -})(); \ No newline at end of file diff --git a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/yui-min.js b/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/yui-min.js deleted file mode 100644 index 55f017d9b7f..00000000000 --- a/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/yui-min.js +++ /dev/null @@ -1,12 +0,0 @@ -/* -Copyright (c) 2010, Yahoo! Inc. All rights reserved. -Code licensed under the BSD License: -http://developer.yahoo.com/yui/license.html -version: 3.1.0 -build: 2026 -*/ -if(typeof YUI==="undefined"){var YUI=function(F,E,D,C,A){var B=this,J=arguments,I,G=J.length,H=(typeof YUI_config!=="undefined")&&YUI_config;if(!(B instanceof YUI)){return new YUI(F,E,D,C,A);}else{B._init();if(H){B._config(H);}for(I=0;I-1){M="3.0.0";}YUI.prototype={_config:function(Y){Y=Y||{};var T,V,W,U=this.config,X=U.modules,S=U.groups;for(V in Y){T=Y[V];if(X&&V=="modules"){for(W in T){X[W]=T[W];}}else{if(S&&V=="groups"){for(W in T){S[W]=T[W];}}else{if(V=="win"){U[V]=T.contentWindow||T;U.doc=U[V].document;}else{U[V]=T;}}}}},_init:function(){var U,V=this,S=YUI.Env,T=V.Env;V.version=M;if(!T){V.Env={mods:{},base:K,cdn:K+M+"/build/",bootstrapped:false,_idx:0,_used:{},_attached:{},_yidx:0,_uidx:0,_guidp:"y",_loaded:{},getBase:function(c,a){var W,X,Z,d,Y;X=(R&&R.getElementsByTagName("script"))||[];for(Z=0;ZJ)?H[J]:true;}}return L;};F.indexOf=(D.indexOf)?function(G,H){return D.indexOf.call(G,H);}:function(G,I){for(var H=0;H-1);};E.owns=F;E.each=function(K,J,L,I){var H=L||B,G;for(G in K){if(I||F(K,G)){J.call(H,K[G],G,K);}}return B;};E.some=function(K,J,L,I){var H=L||B,G;for(G in K){if(I||F(K,G)){if(J.call(H,K[G],G,K)){return true;}}}return false;};E.getValue=function(K,J){if(!B.Lang.isObject(K)){return D;}var H,I=B.Array(J),G=I.length;for(H=0;K!==D&&H=0){for(G=0;H!==D&&G0){C=D(I);if(C){return C;}else{E=I.lastIndexOf("-");if(E>=0){I=I.substring(0,E);if(E>=2&&I.charAt(E-2)==="-"){I=I.substring(0,E-2);}}else{break;}}}}return"";}});},"3.1.0",{requires:["yui-base"]});YUI.add("yui-log",function(A){(function(){var E,D=A,F="yui:log",B="undefined",C={debug:1,info:1,warn:1,error:1};D.log=function(I,Q,G,O){var K,N,L,J,M,H=D,P=H.config;if(P.debug){if(G){N=P.logExclude;L=P.logInclude;if(L&&!(G in L)){K=1;}else{if(N&&(G in N)){K=1;}}}if(!K){if(P.useBrowserConsole){J=(G)?G+": "+I:I;if(H.Lang.isFunction(P.logFn)){P.logFn(I,Q,G);}else{if(typeof console!=B&&console.log){M=(Q&&console[Q]&&(Q in C))?Q:"log";console[M](J);}else{if(typeof opera!=B){opera.postError(J);}}}}if(H.fire&&!O){if(!E){H.publish(F,{broadcast:2});E=1;}H.fire(F,{msg:I,cat:Q,src:G});}}}return H;};D.message=function(){return D.log.apply(D,arguments);};})();},"3.1.0",{requires:["yui-base"]});YUI.add("yui-later",function(A){(function(){var B=A.Lang,C=function(K,E,L,G,H){K=K||0;E=E||{};var F=L,J=A.Array(G),I,D;if(B.isString(L)){F=E[L];}if(!F){}I=function(){F.apply(E,J);};D=(H)?setInterval(I,K):setTimeout(I,K);return{id:D,interval:H,cancel:function(){if(this.interval){clearInterval(D);}else{clearTimeout(D);}}};};A.later=C;B.later=C;})();},"3.1.0",{requires:["yui-base"]});YUI.add("yui-throttle",function(Y){ -/* Based on work by Simon Willison: http://gist.github.com/292562 */ -var throttle=function(fn,ms){ms=(ms)?ms:(Y.config.throttleTime||150);if(ms===-1){return(function(){fn.apply(null,arguments);});}var last=(new Date()).getTime();return(function(){var now=(new Date()).getTime();if(now-last>ms){last=now;fn.apply(null,arguments);}});};Y.throttle=throttle;},"3.1.0",{requires:["yui-base"]});YUI.add("yui",function(A){},"3.1.0",{use:["yui-base","get","intl-base","yui-log","yui-later","yui-throttle"]}); \ No newline at end of file diff --git a/src/contrib/monitoring/JMX-RESOURCES b/src/contrib/monitoring/JMX-RESOURCES deleted file mode 100644 index 1d1aa98fe7e..00000000000 --- a/src/contrib/monitoring/JMX-RESOURCES +++ /dev/null @@ -1,38 +0,0 @@ - -Resources for monitoring ZooKeeper using JMX --------------------------------------------- - -JMX/REST Bridge ---------------- - -http://code.google.com/p/polarrose-jmx-rest-bridge/ - -"Simple Java Web Application that exposes JMX servers through HTTP. This was written so that external tools can easily query JMX attributes of Java applications. More specifically, this was written to allow Cacti to generate fancy graphs of ActiveMQ instances." - -JMXetric --------- - -http://code.google.com/p/jmxetric/ - -"JMXetric is a 100% java, configurable JVM agent that periodically polls MBean attributes and reports their values to Ganglia." - -jmxquery --------- - -http://code.google.com/p/jmxquery/ - -"a plugin for nagios to check jmx" - -check_jmx ---------- - -http://exchange.nagios.org/directory/Plugins/Java-Applications-and-Servers/check_jmx/details - - -jmx2snmp --------- - -http://github.com/tcurdt/jmx2snmp - -Expose application JMX properties via SNMP - diff --git a/src/contrib/monitoring/README b/src/contrib/monitoring/README deleted file mode 100644 index c88a9d86dbc..00000000000 --- a/src/contrib/monitoring/README +++ /dev/null @@ -1,84 +0,0 @@ - -Tools and Recipes for ZooKeeper Monitoring ------------------------------------------- - -How To Monitor --------------- - -A ZooKeeper cluster can be monitored in two ways: - 1. by using the 'mntr' 4letterword command - 2. by using JMX to query the MBeans - -This repo contains tools and recipes for monitoring ZooKeeper using the first method. - -Check the file JMX-RESOURCE for some links to resources that could help you monitor a ZooKeeper cluster using the JMX interface. - -Requirements ------------- - -ZooKeeper 3.4.0 or later or you can apply ZOOKEEPER-744 patch over the latest 3.3.x release. -The server should understand the 'mntr' 4letterword command. - -$ echo 'mntr' | nc localhost 2181 -zk_version 3.4.0--1, built on 06/19/2010 15:07 GMT -zk_avg_latency 141 -zk_max_latency 1788 -zk_min_latency 0 -zk_packets_received 385466 -zk_packets_sent 435364 -zk_outstanding_requests 0 -zk_server_state follower -zk_znode_count 5 -zk_watch_count 0 -zk_ephemerals_count 0 -zk_approximate_data_size 41 -zk_open_file_descriptor_count 20 -zk_max_file_descriptor_count 1024 - -Python 2.6 (maybe it works on previous version but it's not tested yet). - -In a nutshell -------------- - -All you need is check_zookeeper.py It has no external dependencies. - - -*** On Nagios call the script like this: - -./check_zookeeper.py -o nagios -s "" -k -w -c - - -*** On Cacti define a custom data input method using the script like this: - -./check_zookeeper.py -o cacti -s "" -k --leader - --- outputs a single value for the given key fetched from the cluster leader - -OR - -./check_zookeeper.py -o cacti -s "" -k - --- outputs multiple values on for each cluster node -ex: localhost_2182:0 localhost_2183:0 localhost_2181:0 localhost_2184:0 localhost_2185:0 - -*** On Ganglia: - -install the plugin found in the ganglia/ subfolder OR - -./check_zookeeper.py -o ganglia -s "" - -it will use gmetric to send zookeeper node status data. - - -Check the subfolders for configuration details and samples for each platform. - -License -------- - -Apache License 2.0 or later. - -ZooKeeper 4letterwords Commands -------------------------------- - -http://hadoop.apache.org/zookeeper/docs/current/zookeeperAdmin.html#sc_zkCommands - diff --git a/src/contrib/monitoring/cacti/README b/src/contrib/monitoring/cacti/README deleted file mode 100644 index 8188723e5e2..00000000000 --- a/src/contrib/monitoring/cacti/README +++ /dev/null @@ -1,56 +0,0 @@ -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Recipes for ZooKeeper monitoring using Cacti --------------------------------------------- - -Cacti install guide: https://help.ubuntu.com/community/Cacti - -Cacti Manual: http://www.cacti.net/downloads/docs/html/ -PDF version: http://www.cacti.net/downloads/docs/pdf/manual.pdf - -Check Chapter 16: Simplest Method of Going from Script to Graph - http://www.cacti.net/downloads/docs/html/how_to.html#SCRIPT_TO_GRAPH - -WARNING: I have wrote these instructions while installing and configuring the plugin on my desktop computer running Ubuntu 9.10. I've installed Cacti using apt-get. - -WARNING: I'm going to make the assumption that you know how to work with Cacti and how to setup Data Input Methods for custom scripts. I'm also going to assume that you have already installed Cacti and everything works as expected. - -You can extend the Cacti's data gathering functionality through external scripts. Cacti comes with a number of scripts out of the box wich are localted in the scripts/ directory. - - -The check_zookeeper.py script can be used a custom data input method for Cacti. - -Single value (check cluster status by sending queries to the leader): ---------------------------------------------------------------------- - -python scripts/check_zookeeper.py -s "localhost:2181,localhost:2182,localhost:2183,localhost:2184,localhost:2185" -k -o cacti --leader - -When you will call the script this way it will about a single value representing the value attached to this . - - -Multiple values (one for each cluster node): --------------------------------------------- - -python scripts/check_zookeeper.py -s "localhost:2181,localhost:2182,localhost:2183,localhost:2184,localhost:2185" -k -o cacti - -Output: -localhost_2182:0 localhost_2183:0 localhost_2181:0 localhost_2184:0 localhost_2185:0 - - -TBD: Step by step guide - - diff --git a/src/contrib/monitoring/check_zookeeper.py b/src/contrib/monitoring/check_zookeeper.py deleted file mode 100755 index 348ac393b93..00000000000 --- a/src/contrib/monitoring/check_zookeeper.py +++ /dev/null @@ -1,353 +0,0 @@ -#! /usr/bin/env python -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" Check Zookeeper Cluster - -Generic monitoring script that could be used with multiple platforms (Ganglia, Nagios, Cacti). - -It requires ZooKeeper 3.4.0 or greater. The script needs the 'mntr' 4letter word -command (patch ZOOKEEPER-744) that was now commited to the trunk. -The script also works with ZooKeeper 3.3.x but in a limited way. -""" - -import sys -import socket -import logging -import re -import subprocess - -from StringIO import StringIO -from optparse import OptionParser, OptionGroup - -__version__ = (0, 1, 0) - -log = logging.getLogger() -logging.basicConfig(level=logging.ERROR) - -class NagiosHandler(object): - - @classmethod - def register_options(cls, parser): - group = OptionGroup(parser, 'Nagios specific options') - - group.add_option('-w', '--warning', dest='warning') - group.add_option('-c', '--critical', dest='critical') - - parser.add_option_group(group) - - def analyze(self, opts, cluster_stats): - try: - warning = int(opts.warning) - critical = int(opts.critical) - - except (TypeError, ValueError): - print >>sys.stderr, 'Invalid values for "warning" and "critical".' - return 2 - - if opts.key is None: - print >>sys.stderr, 'You should specify a key name.' - return 2 - - warning_state, critical_state, values = [], [], [] - for host, stats in cluster_stats.items(): - if opts.key in stats: - - value = stats[opts.key] - values.append('%s=%s;%s;%s' % (host, value, warning, critical)) - - if warning >= value > critical or warning <= value < critical: - warning_state.append(host) - - elif (warning < critical and critical <= value) or (warning > critical and critical >= value): - critical_state.append(host) - - values = ' '.join(values) - if critical_state: - print 'Critical "%s" %s!|%s' % (opts.key, ', '.join(critical_state), values) - return 2 - - elif warning_state: - print 'Warning "%s" %s!|%s' % (opts.key, ', '.join(warning_state), values) - return 1 - - else: - print 'Ok "%s"!|%s' % (opts.key, values) - return 0 - -class CactiHandler(object): - - @classmethod - def register_options(cls, parser): - group = OptionGroup(parser, 'Cacti specific options') - - group.add_option('-l', '--leader', dest='leader', - action="store_true", help="only query the cluster leader") - - parser.add_option_group(group) - - def analyze(self, opts, cluster_stats): - if opts.key is None: - print >>sys.stderr, 'The key name is mandatory.' - return 1 - - if opts.leader is True: - try: - leader = [x for x in cluster_stats.values() \ - if x.get('zk_server_state', '') == 'leader'][0] - - except IndexError: - print >>sys.stderr, 'No leader found.' - return 3 - - if opts.key in leader: - print leader[opts.key] - return 0 - - else: - print >>sys.stderr, 'Unknown key: "%s"' % opts.key - return 2 - else: - for host, stats in cluster_stats.items(): - if opts.key not in stats: - continue - - host = host.replace(':', '_') - print '%s:%s' % (host, stats[opts.key]), - - -class GangliaHandler(object): - - @classmethod - def register_options(cls, parser): - group = OptionGroup(parser, 'Ganglia specific options') - - group.add_option('-g', '--gmetric', dest='gmetric', - default='/usr/bin/gmetric', help='ganglia gmetric binary '\ - 'location: /usr/bin/gmetric') - - parser.add_option_group(group) - - def call(self, *args, **kwargs): - subprocess.call(*args, **kwargs) - - def analyze(self, opts, cluster_stats): - if len(cluster_stats) != 1: - print >>sys.stderr, 'Only allowed to monitor a single node.' - return 1 - - for host, stats in cluster_stats.items(): - for k, v in stats.items(): - try: - self.call([opts.gmetric, '-n', k, '-v', str(int(v)), '-t', 'uint32']) - except (TypeError, ValueError): - pass - -class ZooKeeperServer(object): - - def __init__(self, host='localhost', port='2181', timeout=1): - self._address = (host, int(port)) - self._timeout = timeout - - def get_stats(self): - """ Get ZooKeeper server stats as a map """ - data = self._send_cmd('mntr') - if data: - return self._parse(data) - else: - data = self._send_cmd('stat') - return self._parse_stat(data) - - def _create_socket(self): - return socket.socket() - - def _send_cmd(self, cmd): - """ Send a 4letter word command to the server """ - s = self._create_socket() - s.settimeout(self._timeout) - - s.connect(self._address) - s.send(cmd) - - data = s.recv(2048) - s.close() - - return data - - def _parse(self, data): - """ Parse the output from the 'mntr' 4letter word command """ - h = StringIO(data) - - result = {} - for line in h.readlines(): - try: - key, value = self._parse_line(line) - result[key] = value - except ValueError: - pass # ignore broken lines - - return result - - def _parse_stat(self, data): - """ Parse the output from the 'stat' 4letter word command """ - h = StringIO(data) - - result = {} - - version = h.readline() - if version: - result['zk_version'] = version[version.index(':')+1:].strip() - - # skip all lines until we find the empty one - while h.readline().strip(): pass - - for line in h.readlines(): - m = re.match('Latency min/avg/max: (\d+)/(\d+)/(\d+)', line) - if m is not None: - result['zk_min_latency'] = int(m.group(1)) - result['zk_avg_latency'] = int(m.group(2)) - result['zk_max_latency'] = int(m.group(3)) - continue - - m = re.match('Received: (\d+)', line) - if m is not None: - result['zk_packets_received'] = int(m.group(1)) - continue - - m = re.match('Sent: (\d+)', line) - if m is not None: - result['zk_packets_sent'] = int(m.group(1)) - continue - - m = re.match('Outstanding: (\d+)', line) - if m is not None: - result['zk_outstanding_requests'] = int(m.group(1)) - continue - - m = re.match('Mode: (.*)', line) - if m is not None: - result['zk_server_state'] = m.group(1) - continue - - m = re.match('Node count: (\d+)', line) - if m is not None: - result['zk_znode_count'] = int(m.group(1)) - continue - - return result - - def _parse_line(self, line): - try: - key, value = map(str.strip, line.split('\t')) - except ValueError: - raise ValueError('Found invalid line: %s' % line) - - if not key: - raise ValueError('The key is mandatory and should not be empty') - - try: - value = int(value) - except (TypeError, ValueError): - pass - - return key, value - -def main(): - opts, args = parse_cli() - - cluster_stats = get_cluster_stats(opts.servers) - if opts.output is None: - dump_stats(cluster_stats) - return 0 - - handler = create_handler(opts.output) - if handler is None: - log.error('undefined handler: %s' % opts.output) - sys.exit(1) - - return handler.analyze(opts, cluster_stats) - -def create_handler(name): - """ Return an instance of a platform specific analyzer """ - try: - return globals()['%sHandler' % name.capitalize()]() - except KeyError: - return None - -def get_all_handlers(): - """ Get a list containing all the platform specific analyzers """ - return [NagiosHandler, CactiHandler, GangliaHandler] - -def dump_stats(cluster_stats): - """ Dump cluster statistics in an user friendly format """ - for server, stats in cluster_stats.items(): - print 'Server:', server - - for key, value in stats.items(): - print "%30s" % key, ' ', value - print - -def get_cluster_stats(servers): - """ Get stats for all the servers in the cluster """ - stats = {} - for host, port in servers: - try: - zk = ZooKeeperServer(host, port) - stats["%s:%s" % (host, port)] = zk.get_stats() - - except socket.error, e: - # ignore because the cluster can still work even - # if some servers fail completely - - # this error should be also visible in a variable - # exposed by the server in the statistics - - logging.info('unable to connect to server '\ - '"%s" on port "%s"' % (host, port)) - - return stats - - -def get_version(): - return '.'.join(map(str, __version__)) - - -def parse_cli(): - parser = OptionParser(usage='./check_zookeeper.py ', version=get_version()) - - parser.add_option('-s', '--servers', dest='servers', - help='a list of SERVERS', metavar='SERVERS') - - parser.add_option('-o', '--output', dest='output', - help='output HANDLER: nagios, ganglia, cacti', metavar='HANDLER') - - parser.add_option('-k', '--key', dest='key') - - for handler in get_all_handlers(): - handler.register_options(parser) - - opts, args = parser.parse_args() - - if opts.servers is None: - parser.error('The list of servers is mandatory') - - opts.servers = [s.split(':') for s in opts.servers.split(',')] - - return (opts, args) - - -if __name__ == '__main__': - sys.exit(main()) - diff --git a/src/contrib/monitoring/ganglia/README b/src/contrib/monitoring/ganglia/README deleted file mode 100644 index 578adfee241..00000000000 --- a/src/contrib/monitoring/ganglia/README +++ /dev/null @@ -1,48 +0,0 @@ -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Recipes for ZooKeeper monitoring using Ganglia ----------------------------------------------- - -Ganglia Install guide: http://sourceforge.net/apps/trac/ganglia/wiki/Ganglia%203.1.x%20Installation%20and%20Configuration - -Gmond configuration: http://sourceforge.net/apps/trac/ganglia/wiki/Gmond%203.1.x%20General%20Configuration - -WARNING: I have wrote these instructions while installing and configuring the plugin on my desktop computer running Ubuntu 9.10. I've installed Ganglia using apt-get. - -WARNING: I'm going to make the assumption that you know how to work with Ganglia. I'm also going to assume that you have already installed Gangli and everything works as expected. - -You can monitoring ZooKeeper using Ganglia in two ways: - -1. Using a python module: - - WARNING! The python module only works with Ganglia 3.1.x - - a. enable python modules: you can find instructions in modpython.confg - b. copy zookeeper.pyconf in /etc/ganglia/conf.d/ - c. copy zookeeper_ganglia.py in /usr/lib/ganglia/python_plugins - d. restart the ganglia-monitor - - This is the recommended way! - -2. OR Using check_zookeeper.py and gmetric: - - Monitoring ZooKeeper using Ganglia is a simple as calling: - - ./check_zookeeper.py -o ganglia -s localhost:2181 - - on each of the ZooKeeper cluster nodes. I'm making the assumption that you have already configured gmond and installed gmetric on each node. - diff --git a/src/contrib/monitoring/ganglia/Screenshot.png b/src/contrib/monitoring/ganglia/Screenshot.png deleted file mode 100644 index bc0e41d5f644bdebd680aa8e97e3d3f08039fcdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111055 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\o(&ieLfwK!DlYCd)QI1| zehtLwq~fcNaNRfyT|e+N{1-d(>ZEauar#krK@ffr3`{EcSYYa?i5NM#xuFiR#<*{X)0$(Zf_ryv2Yt()QGo! z9Gv5ky3g`=%dihT-_#a3w<|~0e;OHyi7hTIm4TE4Jq@P0!Ds(p>kncK(+X$c&W^ba zN~;V1qFa!4^cE27&4h;lW?$>quJxOEx5wf2mi@eu*kS{R*_yXg>F9@?4!(?!T)F)5 zg?0A!`X5RkH8hO&ZXcT3wp#J-o$6Oz?FO@c47|Kn)89Tuuisk+B-38dVoTh_d;Htu z(D2QrFU%k6zI*%Bkzc+)udQdzQ*AiCh!nYz4mVnWyj6ex3mQ^66W5^HS5HjS@POUm@5!klP!1_MnAU8>0 zc=Kk-ojaL>eIidkIj9BQPFh9Th?r1kn5~y~-2S){zxcDM)_@(-JHi%khlTDKby(+x zttud-Pftz7UH*XEM2yiu7s3D3ERiH?7YZo#UE5OZK&2k9XnJeX;hU3L20Un1rJsK4 zeH7kDfc>c@#WPb;JH4MpH7DG;Cv3h%YvoiZwbM*IR*|TLU3@!bWqX>MK4IjC4s1|O zC{E zN^;{Sh{y??YXoRL=?gV?`-W|q2I#| zLCLa2t~j(sp)RB2?uA07$6L0wxDgB}#pPAqnRKu9r$)7TwI8BnnzkN!8<#uz*@KEB>PP7j|KP&NoAQLmxR5Z(qq zfwvGyptFgIy(n%k=-=Alk?2?gm=2VuyHQarsV#t@K-mr3PE^W7Oe6nI*imBxl&y0g zwjTzU_QP(N*dYmUzuoo-1>DD#$OjJ(;4nJE0n-6po7^75l0o@O)g5T?z^esIoUgTX zWuzbfWKJOXgqUGt&3f!vM`qP^ zh?t3J-=Wr1>)|OgM~lm1pZ(Os6RkV!Lak1Z$;7S>$`808e$3M=7~}jMsXGIH`*BBg z!;(QPx$e8oe_9@tZi7!XmzMTbdKE-P>Dt;3x=pp~kVC%koFLb=cF>a-6VjH&Cw(Fh z?+J7ps8LWVf9bE9nf_?>ErkY8KPWl*0KPBYB0Or4_2-FA$URsj*V=l~k2kG$*8ZL; zJX=~D8a9?y#@+jN;Pm3Mi3fP7EDO#$gNm_1UIUPp1 z2=QCH4~Um}$R&7U(+=|C;EoS~gHHSS}(*x%!<8TXMU@Kedx|gN-AV2%vM6-kM9q? zBk(DNt9g`_Tv}&$R#Ik#iY0tABR43aYIJs3r8OdIyY!CrlNJ*rMz#A_8~$2v=}E}e z)TuFkDY3e}VZ0I|P;?7NY|P9PI2g8@&uJcXtNU9+8kB7Otv^ zMrQLRiQ|n=%}=EdA=JvUod;P6U;2SchbxV_o3QBNT-bq~r+?^INwAR|;!MB$``;dZ zcIrfh`*-)o(5B#_rrOVm-L%IR1%C8I%oP$MBpSAt#V0eg+$16$G=h_?d?UjS+*`|W zy&sTZiFSdvkx{r;;i$Fo$f8m%8lGx9HgDD(%+M-#jdTbhf@6s}-(Ne{%rKQ;2-M)> zycivGjX#O4AU+0s1rqENSVT$3W5NdYGzLewb-Q=V%t$*_Y9@)Sd!?E`FJb@CjPcFB zzD>N;nHURArNO_$;^55&v|89T+=SDQsR5dD0T}CrAZM@(bnYeBec?Iv%rM#%9r+ev zVQb0NplA}PeQD_)O*5$tS~lj+w94rO91QJP^cvZ5iGW#Myj-_bPTWTnr)ya?(<)`< z@NmBFPHOgLyoa*K-%N%9}t^tlf>WmP~gBpM)Q&q7NLHZ(Dh|7(Nq~zr2-}sh z{rE+r+Kfj^6SxB+wtx1=CANJ0`lwScL#2n=2*L;04M6UM?35L9xqwZ7* z$insfFfo7>-*2i#wm9w#Ch1uIaI1O}!J`qk4j^ilpFHrH+S*z;qd=Puq&~F&Fg`pC zp3&Dy8BY2rf%62JAFhj&OVjzBx!MxeO+bJl!M78-Q-UG!h|1);R!BYo`4z~d8WW2gXdw4==dNs(1_NL+uLIw4S==MU?1!0 zuoUV&Y^cVpNWdQgmGv#o7FEF;u*kTOE=jFl<8#r>v~ZF9=DIuoLhNbJGx zEo26a4?&#_I~^XA)Z5Wfj8jUiBv&fOt}6goK>M*iK45#6;4gRvfgg@V(s8vzocHxI z6sfs`Mw;_Kr6{B#C`D)Y6V947H8tt!hYSrbN&TJR*v`NESZvBSb6&{~Td>}Z-1C{( zCG!SE2+QD&uclYg@6edubr@uy;-;a z&^_O80C3=MQ-f7*n90PhxnIBkVpP<2S=r62zA)MGjYT0PCrTW`>#ngodK6?X9nsBd zY(S3&iyy$Ql9=OEJHlxLM{VJ!_a~wsV!D6t{{6Iv)(!h0Wrx1f#m1%so8Slz)b(?W z_?7~{`SDL1I&l)v-qa^-dZVi+!hbMUw794Ugc!J9LW$YlejDDG-#_v#L_m+|`l|eP zb+wDImPgli|abws-O*g+`S391Evb>dt$yc>!aj;L#<+wvB> zwjf4<2kf7u$zMpHI|LX9{URXsepKclKW6RJFb_fNiG>x%F}ESq>sOJ0XEgq#dIfvI zcFbv<)7ndckAgt_2GsvAs7Znut|*;-g-!z752UwmKO(u>LVN~3A07L!$7LFLFn_hN z-s4rE1GZdM?12bbQSl`>0UGj5!EHS~>DVfQRRaO@6PkGoyUv_>sCr*(`@XE+P5jvw zGv{{YuxJ>9v;r;|V=!BZBA4pmxiCdN{!9L-@o{P4F$v+_DnCygD;WEO-z|gz%GRxV zLAx%I^}u{rOXFx3+09n6%8Q#CZ3DxKaVUYy!IB7NL)_GH4q5hyQ6Y0|H9)wE zSVl(rD7WeS_QTfFiPIVuq7!%UtNu?TVd3FWEMuq!_b>`l=HsSyu6sJFC!0jyp@^PHmvkV)6%mEY zP2Ul>ko2-WL5`KkAefn95P>?+{NTan7kYvd(@9S+U{_;9os46L5n3-c@tqQYk(aXa>-zdVwnqXt4i#Efg%u1VCt-4prW+b@ zkso{x#KKl-Y2)%i5L6L)A-KWtj8FEjsQNib8-TqF%RL)%hxZKUX^ZeWcb%1VBaGC0 zgJNX0{799iS)ZnlW4)H7oZJtHpO3$;o(0?zU-EXsl{ST5@U$mEq@O z5u>!6F;iS4VWuH@nAS9N2@TLB)SWb{ERrE~fs+ghqt8I?Uxq5XSg;e8ue^w~9d^{lMvTD#5S0wP z(Lr9}=i>`0C~MeVICS$X#}wx?V%wwTZ5w(uHp0RWSY9*^Uw=)&8`$Z{%O^j5*xW42 z&;OUo3s9J|CZLSFH{GFI8vq+ZqGm#eMTkK}M4GBhp_f8xe17ZDArlk921gKl5R`AB zT*}WU-93H)wvTv&&Yv2!q*u=FPa0V>c`#WW`j~&!RIv<#*t#Vp%u!0KM)|jpI+XE+7}x;P%~VWJ7MGM zdh6{?c;!KIhivIEGXzrZHef5LLg6==lALVdeyh1*3Qq?fRGk<>>#(UqyG|LX+(=ID zJ%ml1kWostfNf2v(aITcjv`+Hf>ckf$^0^<*4!8~Zqd&l;A)8iUr@3qvg3Ar{`G?ugYS)(i6T zl5h(+IEq044R`EUYY2q#-=iv)Oc|z#E zvXDM-$F(f#hjCo%?(yD9@b;R7bFPe)0T0T}%|*2i5!{Iii`f0AlwKok+Ge3KFhM;B z1ljG7?mOw?Q3;Nqht|esW)kK_)>t{UTLD5h!jwEl=o5LWm{Te`9UF47-7HZ7IWn}_7^I9k6+1H<}Mxp4V{AX&!=?^9 zf3$*!4`0Kjfb-sc=ud!z25*F&24H+F=9q|L3ADMQd?saM#6^|g=07b&#l@dt0fgn5YccJ}EJ^491a}6a1Uvt>vF1gAaZ%cC@+F+6d6tEZ#sdI@^Z}Y$46YHQSBmFHZ0xwN^-kqFT+WmT zlds#i4J>#jaSNo1xhnW3-fZm6iGr!vq5H&Uc=W|6ARltwgQp#y0YVCj%)W!J_x9`? zZdmwBVAwTVT1~*}#yNw%f2lrpme{Z3kRv+JB2H9>W6ABr`tJ1d%}y6EUCmIDczt_j zX2#XtUTg1OP?VJ1-l{@KhRT&K|Kn#YV1x146iR~HrRY(LkSVan(I9pqBufcEC9$FT zyE_iQv_l6757=0R#qWTLzF;rndRGUB+js9izNJhAt{PDyW_`_<1TT1W6zrM?x|)UG ziag`{w0ny0-i6CZc5*V$*@ti4D`vjLiru;VhYRp#Gb5jbge#W2;&I)$aRcQm=L>#B z@-fwv{3yp?bvy$&lSBv@JG$qXE|DS+8R3Y zXH0@R=~Y$&4Mo|Bov625)`|0(Nxu@=7CQ}l#Qy429{B8F_V+du3_5}|g20X=h*QqY zX_)p#r#CqFc2YqxL*RVPHx45mMa#34B(4a7A>>4x;s{nfA~Ximu+A8Crw922>IPIw z8tUqx=%6muKtS&MNMu#$^aBC{fEl{G!2nKAPtPsg zKtQ5^HIUyRyYQIQa&Y(vkbc601vB5ZvHGHePVfpv5Xbid25MvT4!zK=Y9~|^D9Qli zexIJkiGI-Ee{~}xh`5v1;L#ztUAb}vqBHxIk7QP|3(QfnJ<9l^q9VZG>lHk;Bu#{? zh;+zK7>v05Q-&eDc(G{bIB+%o(DO3Xd$ae^D4KE+^h zC)Uc;#p7WnUd>UeQIjevF5a?rE2r`#fYTkR5OLHTdBXZo4Gul%oMFY_4t@?ChEm=3 z@Bh(yc+>4G9dzxn41wogS`8kO*p^+=DKoOl;oW5Y@Wa8Z6#(9;wOZpLTN zgY^VeRaN2P;TTv|IO%Hl^uYt)!~|4}mmy3m3-P|DM+|~DUf7`W8x|u-fAR_n+?5~%=&jdblMZg z2m?;~*nRf8fEJM8%uw<(Po2MTLD_%4{DHy}nn!!Y77J~#`=u&Z`aFNec%MWth)^Df zP#j^X3*Wg)@-=#U8v6-}@=Y!>)aqMX+IWjJ|KmQkh?o-p@9$p*GV5D*4IxGEa-pGd{x~{{iQL&g-;EbSJ4xcTF`6)= z$Oq16T9J6|+%1fp*cXn`(DcVlywE245->n$_=idSbbnA_c#3iT0d~>H{QV*_M8gQ* zGZw(*+JjndZj!c3P}<>;KKq7q_8lqnpCMP3z&dEskm%6=AV}l( zZCruT7;5i&5EN=_ZO~@~PgJ1x2LT+s5p>Xp;pyaVb?fVzOF*-U?Kf!l@kO-L=n8Cn zb)MQG3ZYft-1i+x0z`)nrtD`;qhU0ep&LV4WTT*<3Bu=YFhD@q{`pt&@mZn z`Z>EIyyRrk`R~gtSlV8dwzYSYDnnnulM3Fmp((?9{Vx9t@CF!dqT^~3Nm%jTbOG9m;hJUx3ZrKbmg^^KH^f}r6!k3K#8uO`Ag zm3U-O2jb5FcHe-9q_D6(Fer|zn%2r4Yxk$@*4a%`W|m8>`;uVlkMok!u`e$6VjW4M zWoK4tm2H{bTTogd$EWdzj@IMS$mL#iOd~&C#Xd`2A>o8$1eD5BTF-D)rsf3ZQKme>pVOENX z-lg}dIGQ(eb2l>9Ha335r-W*mV)z8(`hrQHu9W|~Zvbxr$vKDuP*diM_= zkn{%p`g{mb6ycsKEsc#tBhWX1*9WX5%W_rnDHINX1qR!B^tBMKFza=kDk1=56q%2qB+`bpe}n2wl4RI@8ZBH!-seG}F10;iaYF=?Jzt()IEUhO7T z>1v#2D5OV2%1$5cAhuaVByuBT(C66EH#KkS>IPL7W-ltqU%{9eq+Ed8*h`89Yz>%- zUfjO=Jcf#Re2{9vCJ6S@4@I&>+3B`9cI85o7xxA14WYd$0%-v#(4e@g_)&)q>)F|% zO0QqNLf^gj{;Fz&Kx_#b@S~eGx^xi$1Q>Tq85rPBmKGNWXvSyZ_ZcbP#FEc^>>r1k z+IaNq!J+|CZuPRqa7l#GVB;GS&6AZH8qx3~Uq^M!5(k_0(LMQA=7 z%f;zE8R7$r#NDblt=q2=jVBFkX9{*5Ew)mpeSL8N)t{Zg*7dZs*D)7lEwYCATY3S$ z!=AYDXEnN#-x3@mDhU019da5Q?99jb9L5R#4G51<9nYDN6$lt($QmL^C=J`n6wJICx(XZIeGH# zSJRCSi;p7Kipt`g`TiY<^zgthQV>aN74WuN5Th_MYH1nEPO`#Ur_`@G0JZ0UQ@I>F zCUpHGFJ4yI|g5@r}0ET^9u@QbPH=J@+?q=+ZM|JXD>0-dukCo$p-^R+s0rC0}02V zQRs-pKAMJ9VkJSa3YI>F3>(%?*uN8S+ks?&I=p9>=Bo3I=Xl~ijV)tfrfE~&9CU|L z)KMzJbAf@`)B|X`1zDC)dHg4%81%{1C<+5nUO|G z1p5)z9SCUcJ*y09G7i9k6zqC5XKZv2ttAoK>W6{rp&@&s{#nlu`s9LF_w$q8&$-tt zGQiA3Zu&RzDhQ?t4Ncz|>2iC*f17C4_3;*?zC|MmMqEGa#5OWAoJC+-LiygtE8-gC zzjKL8q;rH(yayF?@-wbVWMDi`SJm?p$m zr>5lVX+z(eS1HVV$JdmkL+Fm}_h{#l{>?$2N4}UlNEB%Qq)0I?3f^Rjfj40bBdVF( z1qHf&@i&)gURt`y?!`-D{0{>?F;?hbDzaCxqIzhmmf!L9ow{)ndY}L4_Vn=!>3^~r7LF(Y=EJ~N_I8k*@Tw%!To&Is+<>apFR||_FAOrV?Tdy z5^3S=Qs&EnVPRel{}_j}6LWJir*OC1PB#x1aSL4tn+b3R5(el9 zdS1!JMX+<#L}w3$UWIkSHI}J=)L4KSjeD}eid#3N^^mS zpwVEbOP=3hAEh*ca@Mnl>Y#LP{XxxEmM$4GA0Hp*Ge-b6$O?Q%W~JND`6<72gKsOG zlvXOeBz!1*J~zN3fhqV0+`=4&NVYtvjUZ?QTz&J_tre74NMFob$g^+V+E;ELmRabA z{0|2fW;qTuw*Ny&Or$`2!R3c#SGrtwa5?Ct-hM5&8Q3swc*5N>TZA8A!ax)heZFR- zr@v|73d`r+Ovx(7P2G`Jm{_dWf-FYJ) zNWjp8XF6^;4SHt%(_X2&wwAV#aJ6WA&dcQaOTLot@LvSW@{exe8!GPDpH}NHYCgK@ zn-${KokINR?YM(tou4kPo|er;8}s`9nbW8WY|nQhQ4?XMO^5!#UM|uCeGf+wbr&N( zW^t@&$Ida+8|&%^+~6kwT+i`YCE`x8V=uJ|QTLZADv zD)-FxN;)+pWv9Xegu=E>=O~tNY9zT7%D^J}M47>ZSh^f_Fak9-9}$G^1RLaSMpKDQ zvLL%Y%4Nh`$0_f15wcIE9D;}E3w zU>)%+y^V$$3k-TbXk;Xm$g}m{O4!OBRKZ?S+U6e!sq7;)O|zYQAH!5C-(MYe2`0-g zEFEs`gi2nAhaC2@dSx~C&S~^buKwm2DwH|@e;6(N|FHLE3HE;YH^-hnox^{V_N{gP z5E|iaLSee+Cw9_g(8eFA+*B;GY5@GYc z1o@;*XnR)UddqwZ7H15_I$G?@HNDIKNH3~4klN>IOWiMyqZkBV1?(B@c>?D$ek-W7 z&+&)YJqUR2Wx0b_~qI!8c?T?m6pbF%^-)O&QibcKa zD9?+RF9A^~!b1)wASeR?g;!c%BR)*;l7D=df!D@=^b>7Au{q@pm{Bk$7r4>b+^nmu zy=}*i5AGEZee#>FRzH50xQk*@m3m@SDad8)$O12D+*}w^&5m_sxS1rAq(zHq9Qp7E zmsSoiy*A=(+_SRhI$eKXX0xpn24jTMZk?jgHp1%rj|70@K$P77qvJ<*k!dFb1E>Gh z?Y<=q>?!t(?jMK9Rm#w;!*~?qcxzi*bvAWW+TuSpL-Uj{Zl6uwUI|rk|74ra&+Foy zV#PG%wu)S07m~{S*GJg!@eR9gq5>TMPvZV1nx7hh3_&|9C(rh~&^%bC`D}vi07|R} zT2;wmmCqxt|K_n2iqrnC0yziDd(0*@MxwBj+0fF`*_!y;=p11~O$sO>fVg6N4!?Kz z>M!BMvm9+tcnAXHjeS?=7G5Z}mJ8jgiB8w6Wu)Wy{yO5_O_o*XQcVwj9(mKhrCX;z zbKJzPz;HwTPNzKwUp+TiWxwZyDLW@y{JlraY?opNPqFU_2|Q1;$>PnbS=I6{aUO-Q zhTqLU>w+1&s}1i8d?1eUB!|}#g`}~qZT4xA6f=RAo>FOnHj_5SP6y1&WIRXoaUL;s zC76wQFTqaPBp^`t>eW0japW|o@<|&px)Q@FTE}_ben^?0&sxEM8>N#>pXX(T%s%Zv zPUg^Ddr~|+Uh;3m93>sdJ6xENQ0ZXu@_i^rxdqn;9g{ek&+kd+uF_+5z%Os^7nNR4 zGO>=}mBFbwsTtdcv82s~v_k#o5>n|}a+il)&|G<nO$w%(q>x=G5g{h$iUeRN&3vSvUKWy9HFmlTs5C9}l_H)2ab- z#OzW-0~`qW4&*r-mdZT(F4fy%lq20(YP<*YRAQo;kYLjNnhraFAwDAJsDOj87m4#{ zpsDKZ?G#En`BRLsiOX|8%K|lrIm@Kx8=W^TtL_f1?FvfUZlC>uzN?zulIx`C_mTGZ zu$>Eu0oH{rE;FBdR#1|*w1|id&l-oxhcV0%>U_9#I0J+gLB5ltzb{(R?}SDJe+Hh~ zVhJikw^zmMKxn5!3cESW5^`5{U`m;W_tz&~o7X_TsOa$lc>D#dufj+O3^Gd1s1tX| zQ6etSal4Al39*FHtwk^<0`vqDrLo6enH;nD%?b*>&~`#CeUokIT?k9?O7V>D9IJ%1 zq;@MIqwtmD#xU!-RzFs0vA<~LMtcqGo*_s5aP2_!PhGvsOzeI4j+5}w|AKrpY{iDc z%iHH08z1Swo-8Ke{=5(cdyCy!OS%WqS^U|`aDn+zA3FXPWGGRbI;f5bBI!K z$UK4?5GceSCCCfavI=|zzjSX0ICymB-#s0jcn|hp#{P%amE=$T&PFSaE$#3#BZeO< zd3p15o4xp9$lKp<2~6NuS|Zzs4gD(On&dS)%5qhjJ{sGqU)CvlJ~|q%Cfs5qY1B{G z^cQ;ua#+U49Pt8Mg}s?US7}ncmfZgA-qK!enFMYA$AJM1%Q-dXQT)R0Z}PTi-h_ZE z7>1!v?e@~&R_GJFBzu!^%cOLJye-K~bA<{M($bVcp2EJcEeU&iH-qA`mT1hi$GHq_y_Uqe+pEeBHtY`jkO|{^I|AoSQ z3+%NW1rCFCIT&mL_ca?9Uga!SQkr41PCwnl)8xhTju6$dqwBkZO4-_UT3B*VIeB6l zwr1r@58!n`9q)lVgb&zjpCjg@aH0mM)p30LGKNPO7qnmZa&yZaF0h%~|Em+g9voqf zv=u_w;s$MHoKTF;Q~oB5TXI2VEndsgw#UceXwc4U+(5$eKfOA2 zYT`QY2WFDYDsuj(w9j<|xsqmH<7q~+J&x2{znmtT=A>nzyi78kc?;=PV$oaLz{F!= zpYDE!z>=n|8vifSNk({DXrY#~(}C1?|mja>i%alTu z>!D-WV|Rjz10eyH2jMFKQ63NJ&td#=*EEj4ax~u|V?S1C*#e(I_Qr`mm2+;-Spc$N zsbcg7Q)qelih*+%y>kA11bN2!sv9%(g)w>rb9mna@ zj;Zk}SG4kW^}wBjiIGwNN^liS;lX9WA-*oOs~|Bm;X{S#6ssrw7qEt&0gu8Q5nmwC z-nV`j%rIAnF*rQ~1G>52=#fxX82h%s`2u6sycdkVz}%Oc`AtJ$kIVTXdIS>>ycn?6 zKcTIH)4cbI69x;WDYXIxHNb#jJG*7S&fdowh#24&ft+>{_5z-s)gX6bP)ypD5k<*1 zV%6mCX(!K_(O!@WYHg*MHi2di@_<#Tqx7L1sXtr*lihx80OC1Srh$iLWT^7RqK#$l zK_s<9657h$+}e5zuYS3i0ar)cE*5MnFq>c01tQ^21L#saY_@_UKWV zaS4^^6VpXfo{~sCuVupMu$`@K30edpZ!zGfh=4KN3`Bx7AU=A{(86qv6#r-!{{1-v zQ?np$q$;2B9THAry9D0-`w0ftwGBh=upVq$&`yctm-Ez0Og*@oV;5uIFsz6(bvibM zTow|{6S!I%!*=iWy>3afw)|%#uNa-r^Ny(rCIjna+VkUESFKeL@SiQd>s3?Bz?3kf zn=6lXle3_1LpY7^4bs!V`}dt8+{{F?T9EHqAmB{>1_r(Nw$ z%HSb3IPL1?LB644B@Cz#9UWKnb;8d#ATs&zg`OW|)|0clZ8fQvSKvM!l@jQWQA`F9>r#z7V*Z z63ghp@^oA?UQ2X?`CIafOR4!c<;X!Im*zD`@3{H_ksWeHf}#TAYeD4=vs+fUKj7z2 zV9NLtvkSN6ha^TK(cV;Yt$2FSY0X6b%fe=pQzu$5RaQagol?R`39;FeS;pmkg_;+Je>OT*oj*G-o)mi87t~@*u6Qsw%g^~gdy?> z8BMS2g5}>MTAV>h4YymmLxyglz``q7Gi@y?Yabj}v9IO+fqsO3H!xZsg@a*;B zWu&}QOJrQt%|fO7jC8wTW^PdSGA)P+{N;!R6*%MT5gVM@K7{kyE}EHZyvPUcbCA zQ_jjBqEj*3e|#^UydwL=mSd++oocxve@9T72P(Uq{DA93QgC#uuJ$ol?g9TY&4PU1 z(C(U(@7zB5xrUly;t}m2wS$(Dg~Fb^XWs=Q#ZR;Oa^_jTKg!9;Vck%kz`9)#+U~`R zIR$wr;eOxb?3SkqQBhHZsYJyokFt2&mRuCESY3ye(=~#}Ppwep*H3>Rnt$!t@UpzE z?uDI+Gu=i7XBF~QzAQx-cI>qEI;A@}(HJ#?fCt9%QSyBu>D&8B!WJxn3NKO|Q|+QZ zke2IoUL3jm^rQ3$D*Wh7lQSBd^5>j79Yb%&-k|jm%`jr=!yIOt*~>+2#6dVdHATxc-~{?3Q+|^Xtl||0p6jZ2l zqf=V3X!B-DFEUuz96!^weGSy_iYG@eW233AE@9~pw?e9?SKKATbju`@ArkBh%&Sh4 z;pO$KYWRoWZ_G$<>`#AYExBbu2B_|n_siGH#X$8AK5K#&_Bml}kIfHqP{~hz(e-er z3ukR~_h8w*i*eI8KUh~96BSQqmOw(5(Q1zCJym6tBgaox(8^9YL{xT9O+?ZqkRawK zkFKnwb}?I$_jTImI#OGi-+~2_d9{k(#@!r-t+aQg$)C16LpYk7|NVgT@Z(&Ig37%) zu+JmpnW_7*7R~1Paf|Ogq`kBdISfwCqNhhq%NL6ZcIUQj?9^u@mZxyq`gF3M$?d|} z*mkhLQeKrBzeohzy*7fK9|NyFCPJ~GIe`j=%YaK;<@xctr|))b-u?PjYybWQaLT|} zq*kaN{|?aCWKZ$yZM*sy2{`o1o;;dgzo>_RzY>cahWhU+W6WWgXG|Sh!^}|Tcz5e!~2uI1f+?7Y6P`a$4G_9pwgY@-XCUXs3L5#7-*tAO-ZVr+^_f{)Rm6t%t& z1j)2I@fV5mN#SF%5HG7xVnExp;yCG3_2*({%cz!PU{|kLE%_Gx=CyWvNB`Aw)F`4b zN-gwreQcVHruu19i|%9%T-w#jQ?h0El!m8?7Feu!&1*S0I_%elege`$ZR_PLL*RxV z6A=*s>A;P&(-PF1cdRN==xSz^>I{E=FYyvNM(JL>Dlx2-IJEqb^PS@SS*MkbCh1pc zOo)*w_P?M683z6iLZl!Vje;-uP2JRpT5@i*^5!lSuvm9Xh*yxAOTY1SV;k4;jEqZ> z6KFp3Qwq}^j%J5d1e><(Dahy65pb)((B0Ie@Kg2MUHuQbhA#dXbP0OFRCHh!Ta|cE zzIN8(Dsi(6Tu@q~$!~fj+t-%Ed`}UfPvU4pl9?5${}K;s0f*a*W$Npop86k~;l+hUV-( zpovH4iQ{!1R%W_I`qXb`7$;NdSb9o?0xFcU^%xf1<)-aV;rns9Sho2P*mEn_uDuc$ z=YfSwn4E?+FQevDeSDrv1N6=m8(3KQL34qU7tCW{@K``DPWMe&Z(4P@bWKVA%iF3< z7UBl&6T84B^s*Yev8%^1IRnt6v|fQ@;*ne=@yZI*sDqgY_ZPWj;psYgI8Cyhl<~*gbkT#bG zCntpw#eM0XojoHq`l|@<6*BKbN)YBK;xYMWb5t-}YFhF@7=MRUDopOOi$(`pP?6C` zzY4t-|CW^18!&Rp|2QWhVP*WtLapG#brTa7L4^}?2$EM&cV9scA#rr+(!OT!y9 zr)OEnkf3)y&+PQQeVqFnr(Bk^g{hs?QK~(aA8egTk?zZu^l=5<(AK9^-$hJfy zu%=yb!AYVU@-I731Q2NWWkInG=*Vx%aO>AM{Df`y{658J?%RA1ZO$ci(JncMdAV0HY5z zunYAA#EH4F#Ay>zXz8Os*pe6p_;$_e)zi?$3F^^;Wr7wOX7X(B-oDjuHLIUapUZjw z_AMKp>LpLcO+t;0j+bGpsZ4@r%kzQm#MWC=_qeR!dthN<0bmj#ZGaU0YG?S>kZW8g zEnNmJle$|f^w${ zF#*9IRwZ(yw`Jj3G~BD(op~j7m?&tJ?Df-MUhf19hYB4WqPs5r3VeLD8X=VPqhkfNoP-!ViKLgpU14$2j=;_g35A?BK z_ir>R>hg+IzVw*{jtMJMiesmp&8toX4HUZF@6W#ShI5%irIfz5l~F$#F=F z`kGb$yk@N5dIFz^dK=(Tp&=n%v%FrPYt?Xb93!$)>I<8*1qEklX)pNA4yC>Jr!EU zyc-tyHrtK=n1Gto!PobU6*f#>P&Zugv_EzXn<5zY?fbT0GbK%Y6|+cR-={=TdlNhI z*5s#Q4c*w-l%g+s#_4m%51*9CNVNm^5}LJ-IOkL6b}d>33a2w1OH))lZX7E&y}B+> zh%y5F6nCrJCuLVb-`zIp`{eFmT1(dON5fOtDwLR<+&JLo=pP*wMQpR@0*E%%3J@hX z;20c#!OJo+6bCZv{$+i0r%}HCx&+h|Fo9@xIO#7)er<>Y-1x82j1tc6Q z+g#;g(i1<7spxa-NPDIAOWJgHml~Rr&9@T!u>m}bp5BH93;2hDPBJC@-E`&Lfv|n0 ztK1t15LBHeBbkqr8J&!SH0HbOuf8^XwF0CK0^Fgo4=UVKKhw^U>ldi_b7%J}FOX5; z%nT6k+i33;w7KdntifiYWKNI)N=J(m^Gt^9{=e4VGAynw4Hry81b0t>;10oq2X_eW z5InfMog{@@@Zb4s9$O6bCpL5qW!050b|81@IyTn@M+jI~5o$0N4h!9HHlo zyX4Bhr`Q1?B_JRGG@;;M$K@gvi&H3rz81)Sc|gWP$F(|i47-v6-hU7O=%_c#kyHX+ z2?gXYGX4Ft0_X|QdBAstj(UY6KZEVUGIt-ivn1*cJlvB_1K~JP07M6Nsz5j&xK;V4 z7|gDB*4Md#{%1BfJ6b2+-<-i({jwzCZ*LYb*!lBB5#xZy_hUK2{H2dyF}j2Q*=|x| zA|O)uf|f!v!-dnJ5HuvFK&SCB0NI} zFhHOz^dD!r7X&=%8hrJzP0~^KGq-Zml>6Jj<*$$HmrexIB>21l6^m9O`Q+x(x#Qs; z(AeUgRjFP`tUr>SPUOdbS$%+CfLH(buMz(hDE_aL|?f$*^_L5bJzgqF+dk>Dk;bQzE(<}{Po?V1sx7qqN)OYis<)% zsRh(h;HWSkzz*xEDozDV(DS1sKz=KcnTYQ(1x0nqT{)0?0`xJXZ%s#6_g>#YPM%fZ zQ_$A(0vhDPBlY-^$Zg-K)7>FRDO~{RGg-pW^$FFjc=d(7^>uI;asA_5Shczd{CV+w zw9fxzJ<;gLML9M4`AyAT)efi?ffgDKc;{na08>D)DOeZVfp+8uq$Ys8+8B^yA3J*u z?tJ#7{_Rqr`W^zz&U*<5B_+Sh<|{zNwK}4wd-gE1>@}zo^YSdj{AC9U^F|p9>lZ-M z5un1|U$0-Cws|4aZ1!Bg-6u!sP;EbQDP40X93KM+Ca>Y$*&JoLJCzC!U_r!4`_PKQXkKSAfOO78oRe_XKY3`_lF8 z*)fnJ0o5mPF#~06A*0rDAy zzt-2)(XW?`rvfrAn81Tr8t}3K>&$BV-Z~jDMFG=Y5iCQ=c`ZP_1lXem$v!~g3yd$T zttPC1u?E=I)&KN?2J$;-ZZ7D9IzX@6Y6yC7KA>zIhzI3A8MloVShy~)N-L-Gt}qJz z081{=Oo2|07hESweDA;ypwkxeG+V>I2Q;R?8-W1~Xd^-2S**KpFVnhr3WDExcck2p zpTWQmjE#H6zukzPg3TZJ7G7Ro1BF!i;3bnGI9Xg=695vZ4p?@o1E*Zb`^;vB8NfsU z-U9>EVvAEXFEjNT5Tyi&djHE)g%2IU#{rp;;h4){DAu+|1}zi^^y>o8D z&jmWn?NUxQHb7zrR8<1R$Kdu~tI!eoTp%R9MG&2wTu{3JvPVEsSv^S zYHW;6K+t7g0bE|et^shz!j`61f2k-?@m#=LEPJDk0^d6w*2GUpXN%;Z# zG4Q?tsWpor=K-3bGGJmD3+`ooCVX8T9dk3YiRy9Sf&=*soIvpZv3XdXLjT$gYZGRx ztKueEtDK6TO2kUJ0J<88l*SbXDm4(ElCZCPWhw!ZDpP?qUQpWzSRIa!!-9jqZJ&W~ zFmQ**XSgU2JnLw0KY`69D&`qBxB=d8X0i{q=r^?jz_|vh3$R)0u4zg`6I?^oU$rCS z)h07qpA?O6ot3A9K|DpgEE4&eh2D4qGIfD1k3zr>NI@*KnkWEFGC)Z!7fu6~;vnP~ z+{>V3AO?;VB<%SI*2EDS3Av*_8kEvFe5K1hr2@}#X##*271*RMRaqy>)1V+Cg5Zmj zqoV@b)1u#CbxjBHE|!+}rtOMu<9)pFw_YfSj&i$~ae6Mi(7>s-C zjM@T0G)~<95xu_%Ah-Y;DX?2S>A|LN0(>23Ffsu93BWR3YxOM?5XphagdnT~eBGZc zPFW@aP9xwRCLkaHctGI$)a*6rR`M z(gBtsVDlEwdb|BrkkRK+gK$~(>3`5gaGie!MF?fy#Us>FhFB&k9lopD?t|pq733; zZ{Dh@<#1+%wg6tN`_?GX*zJHxuu zD`%Zd9I*$aELK(u01XmYrf~O*06+&un|5|~z*cK3UbosNzc?L``$0j&#=*f4$l^24 z)I078?`Y38gf>9I^@77(9!#15qmDrURgc9T*uem3kHs4}#J;S5)^8CH zt^ZlU{eONv|FHsma9>sab^N!-|9JCHmE;9(9N4r)|9OIvaW*9-Bt-uG=iW7^Z-4*i zHzq%b|E=b{cWwFe_;1hn-yZ+BSM(+moZP>Tf4!qWAAi$CKM>LAbzIg-s zmvcQ0Ast6od5nF{b)EKO2p<|bZ;y~$n1>)4jbbNPn|e!liKB8$W=yB8?1oFw4b{b4 z7KN6^R`D4vTR?`UYq-1KyyM74)*XMLt(d*9RO ztNiupm4P*bH(oc2q2Ua;9nXQxq?rTX_HM1Qm7oSOv0^^2sJ+oL7w6@R`y z*7=(*Uuybpv%@Ks+z3Q>M}On}yByf(DQlKEx^y8+$+Pc@%6Sz#Uv8K44)w38g&6a5 z{}{baF|2a)=Uawo=@jP3g-)IM@-2T9Kl{yWc~A+TWx>RqDl!+pAs)#u2m_CzBO0El zJvF(%7*R;DfwEMUasYkd_&ECYoJh7`>MyHY$^kiK458Dm&euOfCB&Er3ocQUMH;Yh zY;1(mk`v6NEQ*H@YFo3+Um!=XH+m#X1mA4sZ_PCP%>_WY+epaeq+s>Arik5o-78y2 z>Y0^{rrY=N!Efrksjcl+ERBuzsP0AX?w4 zN?&M9NSaS_cCUaX<-t+v2YOaIOO$7!neLbx1j5hclRog`SV|c)XKuBIqdrx6`>Ecdi{- zaCXN*M(VQdjcPhJoC)cqDf&X?(QA+* z!}G?eS7)cf8QO#MoC)&+Zp&B4F61L+o3B0QAtEe%PvS!bs&tQqf>A>C&7gY6iC?f&X-zvvYmhL{b;ojwdWbN+QBZ#PGCCI8*R0{vGA!g zdcG&kL1ct2YHH6)#d@?MJop)lIC~*6q2)Hm+q!e7jUH-hWxD58(zde)hZfd8wc6Sm_N>0<&U6_3;m!y{W?L z{iBQpu5(GP>QSP0b?OUp)Ct#JQ+m(m=`=J)A2**7H3Dqconz4BfWc6biimYlnu(9M zj9%P;@U8)#j~bQ#-2JId(wvkV;46QG8vQ;622U%(?o*mqWcq!Uy9MedXAS+=*G!kNy3Ts0;5XN`H@Je1arV3~vO|HN z9-GEgF(u9R)dH*~55_uVW?*o|O}Do2!m@4@(3V6^B9R=DjKs()D^B+*lc?f_kGoKN z=<4bQ)O(#XRQlF1^Q=9-V zPY7|Kl8h~{FaSMZ(WnS#qxM%dKim@Iv1IZ$tDjU00qI?kkDu5V0XT?((>4ZT1c)UK(N*Xw0!u^673W2OE^y zOA6|FF)+F2WZz0TyF=;ynbYnxwdvLcEMbq7L=ii>op%wHp6d+4DD6GOG|rtlGqINW z4pN%ocJ(Cst9o_a#@d&!v@QK+RT&r5zu;bd|2_flJo#>05+3H_)f; zL&4-(ncehyT`}Bk$`hT*^S6oFRt<9>w-}+ws)~_;V)}6Z*W22O7DP)gwIe^?A|}>l zw*eG&a#JXw%eCdcML^Fv`~YL#cffM%?Qt^vIQJD=t-NTkUL27htn>#XQZX&d86A(g zeEa1x1qpsla{eZ2v5d2)oei>4MNnm~P{7OQ^7Y$FR*|9I7eCxJjqT0u3!WWexr)M-)9@{b3!DCF z%WkwkvJ*chZ@}fLQ(x(V^$ge0sA2h!?upNsVvE}nyuLRTtmYeQE;R<1@sU^o4lUnx z{K(yr$kouh;2(v$VzR_c>rjbPSa5tA19_EXj8NDude^YEB@4vgKE<@f5BoVl*;)s8+5(0!3ifb~0gE!52AgiH~b{Tn=*{4tMBL1C?IsnNxOD ziHfu^{_mwhO@mZJ95Z{i;EhU$l^;pW@c8LFY%JtSOSIR%p`+xi4j5^u>%}F(kt4xs z`4;y%>XCjy&v0FZSMzgLDz&F6V$`0HVKgSxO-LNR8NtZw^{ahksSPcsZfCz}`BIf= z;ceEp>16bIqx$8oyV&}2EAHEGwFI2Gi+`u=&bBdbo?N6Cb9u7+d!@$N_Buj~4;mJKCdI>XLR3WIsDVUYgkdkMa-<0P+?>se z55eSHnEgYACFrP;O2*NtlgGCl7aE(`OEo0E=^a-i7Qdyx`Pun_bCvk=e1DP0M?!r4WRy!yV z$3qpxXT|P?Kk3yNpYaI>VNyi!Wi;LzD|2T#F%wc?l@)wuqz+()x>N~PzI5$6n_9S) zjhIK1RIEtm{Uh)#=r{gstuFqkwsj|P+U)&QG!$W?XKn;i;T4-opy?MD>`@lrjA5^pNh*3MbiB8rjy6WUB60*89CzJWQNa z@iSh*^q(+|C(DY@0Nl6MgK9zMOhXPS7#Nu-->Yz{?A85xjmp%=#rKqPN(@?J(i0lDtoMY6kHOGE}`e^|6%mz z3rD(Ivr1wYgKQ6zXQC9Au>Q+pOh088{p;G$sHLcKnW&9ZA`4ks@e%>wt2PV~OSiyz z&x@kw6}p%k$n}T0b29dY+V9p)$kYR#9`EPmG9rXMiQgiUAC1PSAgdO-Jyf@pJb>Fa z%99EBt~?;{rLM7RAasQ`@p`C_!=D$Q8^$p$OD`ArdoD;i2FGBgwsJ#vYKaZ`%#0N% zA(+Yuud0JtgcCJy-ZFj0B4Hp`kvaI;?_z)a8!}us57RAP$mn}2FE=gRZ(D7xPR`iz zBh`SIVl|QY`xXRKd)+!DwQW2P;8k(G9|YsS_EAar%mh7jNMhnQ9OTS?ix`bV(Zei* zg^htv4#A~HLUF0uwUIkA7ME}$VMZo*((aubRY7RQQ2gQVt6tJsY=O>uNL=``=oJ-MnR{^_2|Ag&J4*Sbh%iP-dM#+K^PPa zRv5xTbc&(0yWR6=(vi)^q`-;qCiJqg<&&V1_w?Z6RHb58_xgd_)+nd>X?o0pG!I2C zOtp1TPgc$EwQK~osgCbDU05?Ix%+j0j0_?7+Edupg2Dr zFJnfR$Ah|f>|S5VzoZpsL53gq$DSBRzx=%yB5Z=%;LcIHklv=In6g-{t*HoU#FM7- z9GOylOi<@lx2=8&mt{!kF6_a`*Q?%3b1}U0Wali)%z1VCJ3mue2HHnAtJtI)vi~GK zvSxQGM2?b6+Ry+OtJZ9;M=HOAo#B%REkriYH+aZOm+py6>{;6DLxMp;MP_W2OXaDL zqH=oP{5vtrIU5-8FxH}@ljoN~D52Sd=}JySykBFp=xBk<4sdVE&{VmTEQtO&%Ik$? zZDPLH=N--T+XB&^EHcAUZ~GKsC^}!jGuy6HUu8_=8NPW!H;S>dMtFqI3#KYED^rr_ z@~yA045@stm3m_qo>0pplS5iFt;T{yc=}Ees2em)o1)dUG331u1!BggQkj;+Ot&yN zG@i*e__lEu$Wr7^X8!D-dU&YINFS8CCijn-YMg@Rx43et(tD7y%CuTdL& z;sr1d)TLas(9004n5 zwLD%rbQJk=u0$Go)Qr}N6$`YhNdWVLgXNLlXI|B5FuEa}RDRtS-Nt;gi3$CUJ%q8L zUDrj0$aR zwdW^fPEn~Bu8T5AMoI33e9p!e=v8mFpn-~K<4tq;xQhD1HHGCjxSeXvGJQtvyGYc} zl;8qzx{+$2gB)L%)SymsdEs3*Mhf3}RnHuxqu_~R5dS1xZ%8KFkW8T2>g=H?py zns6tF{H}g*ILcL~&m)TzyTv={H{_AP(snb`Z8(K+lYmAiH1F zUB5wq)Hg^>`g}lb_99dF_)&EjL2=Zj&8P6q@~6K0wT_6NlC{l(q*Ibgf~sPx?IIUr zsc9j`qsc1`U3Okw?hRxd5xinKMA;2Z1C%aNSZ+ZUTf~-@WZKMWEvM=mc#wtA^ObYH zpx6*c|3c5HMZY=0u!12Qc)(V8*2nl`<0$>_@FM1p{f2{7!`Yj=x|mj-VB19D5W~xk ziaN-5rWq5SS@hpn6xeCH=VvzdH3qhuvkt)i?7bD8Q{4+-IQGZWmpxvGR5Rc7z2$6? z$&VU`Bl_$-OZ3+}I>t$cN9pPbrktxL$q0&dtVK}4-(_Xqg32iQH&ZKAd~uk z8WhW0-T(6b_|Nd!pEx5|4kypi_D_W{{}VtRK276xU$dv(r@=3L*MgvZHo)bVLW;Nw z-AdHFu=S+5Sta>8CAD!?QX6M*#bT0AkZ^g$BA9Ws=WcnmhS%(j(G?uGsCVQvm1<@Y zD_5x4Oyx9%y5!Wa)d+L(NUzig!0sT`J{UW!F^;EWl|j{Ew>OOYhJ1U}2A}oi&ed|& zRg&f?DM_w6bu>a##g!mSZrQw;`}oBqC9;LZGBc<&cS!O-BW#vis~>6k6Lv$@&Uo2H z#-aL!<{+H=O3|?PGU{4Z<#XS1DOCPB*52}6`{J$49C_)K9-Xoe>uC6{QQB8Uiz&A2 z4yBcbGdUlPh+3zu+ksM@24NCMQN0G*svHm7r94U&Y16f4_%BO%mHtb*67AE73$CxK z>I8|nr6icmoB61F08Nf~%Dwvd!5_07LNi(3enQw5{O`x3aP$UTay(^z|IE9aCZyFW zdmsEnj4MNg<2{e@$B2PE)v@y3%_2NvQq9-n;bP>YsRT+Dh|$8nKjHJEagwV6AiA8L zL`r*Ng-MyAY2w`4cR>r%-CkG5gASW>p0-*^EqRJtb!|*#Sre%@b!U|u7P)<-5knl` zXKB_^pdAWLVl|;o$W}Nl<|n*!yp9;2(bqlgC_1djdT}^+e9dBhe&M)cOyDbHwHeLa zLRMy7^)1h+&dy$2d9;t9G<~U=FnxbkN#Io*>5xTEuppnb~s?CY}djDu32_ z->^V27fWlX6Rs~w0XH&RI?oDxxjawC z+H5t|8O)NOnkiHFQfjWGLJ+p;AJR(tf<$|%mAlv<5p#FGlrO!w(+z!jsuQ6a0hy7l!;>Zz zRY>HJ1;^nBbH9!c>!A8*gt8no17WouoCjXw312!L_c*TRqSGS!IzDKhqubn7{P5l@ z`sv1`!YjP{$V_xgc62X8z=!X|_JjP;U`3Eo{*k{PG@mDOvEs#XESf4ykB`^y_L*P7 zsL|E>8LWCMcDOh8ux~eTUB8~{QuUJs+K@}!zUHNf^58~?%bzKFN!q1k_>uN`O7VE& zh8TRlw|5byr1XNBq}M$PJ6M#|EgnGHl~+ATv2b^&4Y?^ z!{93Bl!#|Z7D?CpKy3$YsqB=aqM0}zJ`i<+kaDEw?zwwbq%5H4!df!KT11qF;tuLA z9Iu9G4c$Zsk}A`(jTn5p72%aWv^fW<3dL8K)O*VS(+-7Dvi$f z3~y(14KF#EbW`1IH$KqLmulKBtqC_+??rsKfpP>F${vUj7VJ#-s2p(%D9Eoq8Np!p z0Q#AI^VGv%>doQO>;{d|S}sn;s=l12|hONS8FSw-|tE}C>4Q|N!&sF)ZA+~h|odq2& z4NS!O;qwe}tY2ujnm4`_d$PsocO)r%&Z27Kc z&BsGw59iFQe7(0b)F|CKVh-7>UUPgH@jlBXNul92f4naoyYTz^l)vWEm9EyGvP7IE z9BuYyjyZn=BL%qM+$NF&z+0 z&%+)5()Y0Iy3LpZiPvPg{4Qs$MovzJUs#C@A(cdQ|*`4MQTG;B~XE{UOmMuq_k6LSCSlcp;xNWUw;^?cTUcKZT^J`Bn!RN#My25*O z{3}m0-j`IOCiJneIOdfdiM%32Voh3Jg*3;3PhmN!TZrR^pGk6fL5QZ{kExLeF={7~ z@d$#VW??i-v-X$mWlPgWS&U+a>;4kobZooAH*K<{$i14HiB281^1qKqN1mSRBz{s-49T5jB(pH1?#X zU0*RNSk;m<&c* znw}pWOI|at5Xp-h1r+uLhFIwFn}0d$Xzu`&LrrnY(%OrVcx!Z(niooeG}f=Zak}N) zW1CyFv!5eJ-dNRfK%jbDTg#NRY%p{2J)D?3QHjnlb0a0 zY+di?&W%V6#*=ZE6{(bRcVx~fc?&bjH^YPcf9+W-$^|7)HFCW%B6diEVAj!b+{_w& z(8ShJ(%V(G>;Dpb+#$ioWXE;9Mx+zFel!nGojHc3g70V672$5gNiL-10{bip>1Qik zelrMz>%zT5kxtdq9P%k*lrP8%AE>-k7DzH4*^*H164P#(e|1aOTRhGYduTuDpJ1-p zwL>(9v}Ihs?LR{a$6a-M>4bc|vzB1kFjZcf0c|n;5M4-49@sb$=5%#oVc0*|w`JFK z#7IJk{M*l{>PB=A@R43b!k_p?Qi}z3I}vem>(g9#pgE_OO`ZDmE`BX=dBY`_Qz(Zwg?EaqUcjy5csRW zQS;=my@nx%LFQcDlk#B-059hdSSI2@Fhxh4w_r>{SPAT-U;OzeGrOiVP$^az;VRP* zyLU!!Xd&+#l)i8s-7HzuFwbfiS*-lt;@CcpEIR+0R7uwm+{rfOe@^ZaNgX7xLC|x; zV0oduODpdmOTpp(eK<>H(`$0U*Gqr#z)ko)U0U_m2 z(ntj4{~vH!XPZmrv(e;Y{HeXxdufJ>Q%u#TUcY^3zU{=-+ZA6(oToCgsRK|YS>or`(- z6z)cA;mTEL+Fv(cWTn1WMg-J>TH@5XyWnCU{WOgvP#X5ODTcBiv?H0*Uq5)0e3d5o?_W@B1#=V!TM)$*&fl71sXzq^Z$w zleeBOqQOjX+tgv3&%B_>B%o>OU8$}y#+~oCsJ?UTOLd!tU{Wv+$} zNRl&SIkE6S2Z&w z>H$T#2+m%X!JiGSe&phankJP}JE5y)Fi=C@GCVs|3uTGLJv1YG`opYz*N1m3T$Ef~ zF_@}`NhEJrjGB6(A23s9fd59T0I)9Stu30%YwPdHc%2idhmuh^M4;5cSHG4 z==h%We?!M@cQgzJZ{%n{t{hQAJMuB2?B^X$lHP!i6!&f;iFT`lLZo#8$G?-%ZQtC$ zlHbrWL;vm+#Ja*z{xUFyWTZ(Q?9< zp6t;@@DJuWMIl+{1Z5&rK5{ian3#{`TIH5)itCzqx9$h=7y@6IxI=VdV&Nj~UE!F* ztow4i;p_9)RnknWQu-*!%#JWvkz8nOy6_Ea^Y`8{`&BX)x=226p&qlMrYVfLU7hGtNfk3Q zE?CF0w4$MNc`$1XRJdP@gbsFwHusGoJKm>}JbYkW(SQV_oGzI9D ze1BkpCI4HRZSwya+)q65pW%Kl)xY8XuDAaN_Y?dZ+`mLpCARke8t!N8tYep1UCc1| z@BVC(LBDj)ojZ0KXUcT4!CR4wPj=-D+ZAIE{a}*OvvkguD|6guXI?{c8*$yc1h1WP zw6iz6qz}($ic4qz&uyXg_Q^PCHP|IpOPY+d46e$oEb1Y4mZpfKiO|`#6C$&%BTpmD^kT=9PK?-8nNMz!ApB_EPO*h175$9& z1E#vc(DpMP9GXCbQ$Kl)O1+(#1Ru8yLt?TKwLE6MmR7aEao+YVxe2}1rbfog-N=Y8 zM5=r?-;k)Vd{m^;I{$T#^q-UTvo0f0y2YcChp^L9)cjyKI-_IOB+$IeXbZ8SLqSSwtbBWjEZELQDfCax>DU~yj9I;F9nNw zV9-_Y&9&68*BC2SeI;T><1I<$Y8k62r)!||#g`|@h>mrdtpzN)+uEC|RKG?6L$?J> z67w-gGkG9M*m7wpl9oE6=8A+${#B4x;MJG_r$dQ=O~oY)(M`lf`p!Me;EFS~OAOZ$ z8NjF)Rf!+Z)%hWp$+A(up1w8e`gHM4l*8ZM#8RuTRNCEcEtOk z+>sq+pc;M>G$CBq4`Zm0Bd%NMWoyFIh;Y;qwKsn7}ASGxn|J%1g@|D;Mk_`iI6QUB|7>XEju$20Nt9^aYcE+Hn7X(!rt z#$~@L3#qj!z3FZDqRNKD4Vb|-nX}|;sir1pcn%(6nhctC(^Hb?KmERUwXs5DcwFAt zDR_MgavyI>aAz&Xt6uN0IY7Oxi;Q|5bzY&xv=qDMd(3DvhzJt;K5U)7-lpfRUp#&@ z@&jpEGtYJ=PVewp@sNeYiUt+@;(Sc~6ZGRRuJzpAGu<}hGM_fZb0z>}nfxLbSw`at{O!9l=>ARpk6j!2o$`}jh; z8|2Z=oqatp*KzVd5cbH9r??DzPB->Gzc2y+w6Is`5+!{+)1*xrmmz>clC;w#r=tqL z=+=5GvI0pP=cYKG{-V&II+bP05xsmOlLB3o3Q~qr!H`NLAHB(ZXDHZT($!pGwmy~H zl@qsF=4O`)k!2x$O&VK*wfs7ePYuDz-S%)ShSq&7jE`o}JH^dXbw4;nz{noyCU-Y) zS{HdTIV~y3m;>5!9+jNkO5f~uv6*$ukX3?1{i?PRT-vyxNj`uhQ;uz-KK%s2{A6?i zW5C00zTJ*ssK8rkkN%#0;k7+8r$o#vtuo-z999@1uc+lv;wk;P7<3wIa_!6Ng?H=6`nXmh`lDUuQ}%SV}$7me$mijV2l`?JR|n>JMLSsIPKLl`pPs zN|!2P{Dhgg1Y7D==}L@E8~R;GaA`t*jnBi}-@0(Js(R%GkEyrw-Wc&7tk2_k54!4H z+kGir*icuwsrSBwt)%Tn#8b1G+U&m)f{QAqEfA7yW1?~LF=WJZIZzW$U$e!uCmn@0 znXz1J_eM#3)H$7Oi(_nWlgHJRF!Xh9I3ymoTY0+`t2^##jlroBt`9zzY~h1v6#K+L$obM0EGt z)GRM4#6yF{#2GSn6oy&y4^~QK}gtMlEv^O^S1I16cKeL(*AR zqd0EI7XDFuOI$e;1ao<6V_}0xYa4YZz75wbB|>QOh`qws-77`b#J3!lqnChvs~Y>k z+rwtw&ydkba{hObd8+<6JoDWj?;+pD@CaNFU@jpK3-lm}iY90&yow6gVudwxAqC5&E`O?T|<552CvX&-Nzqo;W5G>x2fC f-3wtPd;K435Zs#SBd#Cauf{SGisB`r?>_zy*#mj& diff --git a/src/contrib/monitoring/ganglia/modpython.conf b/src/contrib/monitoring/ganglia/modpython.conf deleted file mode 100644 index 5cd051a8e33..00000000000 --- a/src/contrib/monitoring/ganglia/modpython.conf +++ /dev/null @@ -1,28 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -/* Update gmond.conf */ - -modules { - module { - name = "python_module" - path = "/usr/lib/ganglia/modpython.so" - params = "/usr/lib/ganglia/python_modules" - } -} - -include ('/etc/ganglia/conf.d/*.pyconf') - diff --git a/src/contrib/monitoring/ganglia/zookeeper.pyconf b/src/contrib/monitoring/ganglia/zookeeper.pyconf deleted file mode 100644 index 43801a0f2bd..00000000000 --- a/src/contrib/monitoring/ganglia/zookeeper.pyconf +++ /dev/null @@ -1,49 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -/* Update /etc/ganglia/gmond.conf with the content of this file. */ - -/* ATTENTION: Change the host and the port to meet your setup. */ - -modules { - module { - name = "zookeeper_ganglia" - language = "python" - param host { value = "127.0.0.1" } - param port { value = 2181 } - } -} - -collection_group { - collect_every = 20 - time_threshold = 60 - metric { name = "zk_avg_latency" } - metric { name = "zk_max_latency" } - metric { name = "zk_min_latency" } - metric { name = "zk_packets_received" } - metric { name = "zk_packets_sent" } - metric { name = "zk_outstanding_requests" } - metric { name = "zk_znode_count" } - metric { name = "zk_watch_count" } - metric { name = "zk_ephemerals_count" } - metric { name = "zk_approximate_data_size" } - metric { name = "zk_open_file_descriptor_count" } - metric { name = "zk_max_file_descriptor_count" } - metric { name = "zk_followers" } - metric { name = "zk_synced_followers" } - metric { name = "zk_pending_syncs" } -} - diff --git a/src/contrib/monitoring/ganglia/zookeeper_ganglia.py b/src/contrib/monitoring/ganglia/zookeeper_ganglia.py deleted file mode 100644 index 82903d1f609..00000000000 --- a/src/contrib/monitoring/ganglia/zookeeper_ganglia.py +++ /dev/null @@ -1,209 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" Python Ganglia Module for ZooKeeper monitoring - -Inspired by: http://gist.github.com/448007 - -Copy this file to /usr/lib/ganglia/python_plugins - -""" - -import sys -import socket -import time -import re - -from StringIO import StringIO - -TIME_BETWEEN_QUERIES = 20 - -class ZooKeeperServer(object): - - def __init__(self, host='localhost', port='2181', timeout=1): - self._address = (host, int(port)) - self._timeout = timeout - - def get_stats(self): - """ Get ZooKeeper server stats as a map """ - data = self._send_cmd('mntr') - if data: - return self._parse(data) - else: - data = self._send_cmd('stat') - return self._parse_stat(data) - - def _create_socket(self): - return socket.socket() - - def _send_cmd(self, cmd): - """ Send a 4letter word command to the server """ - s = self._create_socket() - s.settimeout(self._timeout) - - s.connect(self._address) - s.send(cmd) - - data = s.recv(2048) - s.close() - - return data - - def _parse(self, data): - """ Parse the output from the 'mntr' 4letter word command """ - h = StringIO(data) - - result = {} - for line in h.readlines(): - try: - key, value = self._parse_line(line) - result[key] = value - except ValueError: - pass # ignore broken lines - - return result - - def _parse_stat(self, data): - """ Parse the output from the 'stat' 4letter word command """ - h = StringIO(data) - - result = {} - - version = h.readline() - if version: - result['zk_version'] = version[version.index(':')+1:].strip() - - # skip all lines until we find the empty one - while h.readline().strip(): pass - - for line in h.readlines(): - m = re.match('Latency min/avg/max: (\d+)/(\d+)/(\d+)', line) - if m is not None: - result['zk_min_latency'] = int(m.group(1)) - result['zk_avg_latency'] = int(m.group(2)) - result['zk_max_latency'] = int(m.group(3)) - continue - - m = re.match('Received: (\d+)', line) - if m is not None: - result['zk_packets_received'] = int(m.group(1)) - continue - - m = re.match('Sent: (\d+)', line) - if m is not None: - result['zk_packets_sent'] = int(m.group(1)) - continue - - m = re.match('Outstanding: (\d+)', line) - if m is not None: - result['zk_outstanding_requests'] = int(m.group(1)) - continue - - m = re.match('Mode: (.*)', line) - if m is not None: - result['zk_server_state'] = m.group(1) - continue - - m = re.match('Node count: (\d+)', line) - if m is not None: - result['zk_znode_count'] = int(m.group(1)) - continue - - return result - - def _parse_line(self, line): - try: - key, value = map(str.strip, line.split('\t')) - except ValueError: - raise ValueError('Found invalid line: %s' % line) - - if not key: - raise ValueError('The key is mandatory and should not be empty') - - try: - value = int(value) - except (TypeError, ValueError): - pass - - return key, value - -def metric_handler(name): - if time.time() - metric_handler.timestamp > TIME_BETWEEN_QUERIES: - zk = ZooKeeperServer(metric_handler.host, metric_handler.port, 5) - try: - metric_handler.info = zk.get_stats() - except Exception, e: - print >>sys.stderr, e - metric_handler.info = {} - - return metric_handler.info.get(name, 0) - -def metric_init(params=None): - params = params or {} - - metric_handler.host = params.get('host', 'localhost') - metric_handler.port = int(params.get('port', 2181)) - metric_handler.timestamp = 0 - - metrics = { - 'zk_avg_latency': {'units': 'ms'}, - 'zk_max_latency': {'units': 'ms'}, - 'zk_min_latency': {'units': 'ms'}, - 'zk_packets_received': { - 'units': 'packets', - 'slope': 'positive' - }, - 'zk_packets_sent': { - 'units': 'packets', - 'slope': 'positive' - }, - 'zk_outstanding_requests': {'units': 'connections'}, - 'zk_znode_count': {'units': 'znodes'}, - 'zk_watch_count': {'units': 'watches'}, - 'zk_ephemerals_count': {'units': 'znodes'}, - 'zk_approximate_data_size': {'units': 'bytes'}, - 'zk_open_file_descriptor_count': {'units': 'descriptors'}, - 'zk_max_file_descriptor_count': {'units': 'descriptors'}, - 'zk_followers': {'units': 'nodes'}, - 'zk_synced_followers': {'units': 'nodes'}, - 'zk_pending_syncs': {'units': 'syncs'} - } - metric_handler.descriptors = {} - for name, updates in metrics.iteritems(): - descriptor = { - 'name': name, - 'call_back': metric_handler, - 'time_max': 90, - 'value_type': 'int', - 'units': '', - 'slope': 'both', - 'format': '%d', - 'groups': 'zookeeper', - } - descriptor.update(updates) - metric_handler.descriptors[name] = descriptor - - return metric_handler.descriptors.values() - -def metric_cleanup(): - pass - - -if __name__ == '__main__': - ds = metric_init({'host':'localhost', 'port': '2181'}) - for d in ds: - print "%s=%s" % (d['name'], metric_handler(d['name'])) - - diff --git a/src/contrib/monitoring/nagios/README.txt b/src/contrib/monitoring/nagios/README.txt deleted file mode 100644 index 317ae14869b..00000000000 --- a/src/contrib/monitoring/nagios/README.txt +++ /dev/null @@ -1,86 +0,0 @@ -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Configuration Recipe for monitoring ZooKeeper using Nagios ----------------------------------------------------------- - -I will start by making the assumption that you already have an working Nagios install. - -WARNING: I have wrote these instructions while installing and configuring the plugin on my desktop computer running Ubuntu 9.10. I've installed Nagios using apt-get. - -WARNING: You should customize the config files as suggested in order to match your Nagios and Zookeeper install. - -WARNING: This README assumes you know how to configure Nagios and how it works. - -WARNING: You should customize the warning and critical levels on service checks to meet your own needs. - -1. Install the plugin - -$ cp check_zookeeper.py /usr/lib/nagios/plugins/ - -2. Install the new commands - -$ cp zookeeper.cfg /etc/nagios-plugins/config - -3. Update the list of servers in zookeeper.cfg for the command 'check_zookeeper' and update the port for the command 'check_zk_node' (default: 2181) - -4. Create a virtual host in Nagios used for monitoring the cluster as a whole -OR- Create a hostgroup named 'zookeeper-servers' and add all the zookeeper cluster nodes. - -5. Define service checks like I have ilustrated bellow or just use the provided definitions. - -define service { - use generic-service - host_name zookeeper-cluster - service_description ... - check_command check_zookeeper!!! -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Open_File_Descriptors_Count - check_command check_zk_node!!! -} - -Ex: - -a. check the number of open file descriptors - -define service{ - use generic-service - host_name zookeeper-cluster - service_description ZK_Open_File_Descriptor_Count - check_command check_zookeeper!zk_open_file_descriptor_count!500!800 -} - -b. check the number of ephemerals nodes - -define service { - use generic-service - host_name localhost - service_description ZK_Ephemerals_Count - check_command check_zookeeper!zk_ephemerals_count!10000!100000 -} - -c. check the number of open file descriptors for each host in the group - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Open_File_Descriptors_Count - check_command check_zk_node!zk_open_file_descriptor_count!500!800 -} - diff --git a/src/contrib/monitoring/nagios/Screenshot-1.png b/src/contrib/monitoring/nagios/Screenshot-1.png deleted file mode 100644 index 2dc55c58adcba0adf1407a093eabf3556153cd8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196668 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
    +xHGrd_zYcH|R~nOS54djq`MaeF zurMfaH1*DjN=5QSP%bY{x=de)i_TXCXWT}b% z1+oVF=ss~C{TC98S@D0=pSB@{uil9~Od3ZIH&*5wGfLW@@C-)JU}07~h&h}sz&pMB zL0)2&vDA;KAV2{6TsWsuBoYAS8#EY8sAY~?t3gc59{%aRT-mIv3E#B@BCC-+FWpC^9bu+ zR%FdrN9w1JD05@}yE>K8dgbkWL0Pb35<_!hd3>+!eUxd$a7yjwV7^#Los);;&i&YO z^_L#plVz8q)#GM1rUWX552G8-2f z9d}qqXDd4TmzT34aNfyrxst!@R=GA z!f7JIbzSlJOLshPpMsgXCSvu_H>=ifXIc7J$$DUaDG|XHME*trIRqM@cz5PZ&OR&F zvL;>>%r{R?<4i76>z+c6Qqf5`pmKsokiy;!A}dSo+6d=pfQM5+Su|PigdXzI%&_vh?`eNHbUnLznpVOv{Uy+iC(7-S9q z_XK^m)#+odo3fVUx2%(gZH6Mq`F`xpogXJQ*E&I~V$L|zoo>U8wKEUv4zQfg4P)21 z9=})7ewluRiSLMLL@+?;N2R%GDivS{RMKh5+T$!6&NDpZIMC(6ojp&3q*4ye?`iy3 z>WOcor}AfA-R3vz<;{XP?ZUh@<2%f0a53n%gv*#Dh|a~KzGMmd`f^oklC$|XDEitq z2M;v+9=i6yk82t4_{@Vf9jY>qb7+Oz7UJn+hVQ;SyAy9*7rUTEaui2VW)Z^-7)MAR ztcN7jDeUzq&cKTonWpP&L+(-jj<6qu+EL0)-7!p(|KZ0sSjFM6KDVv>1 zaK~K5;1DE@r?34pJBP8&RC{#kcUK<92Kxwr1RMeHkT8=!TczJ|tq2_91ZI3+{Boro zC{=n!e!$;9eG->Onl|!yZbrBm*h6<&c%{$Pg0i7}*YXUP*@B9WE=mhe@M$|~b8k<$ zyWHpP!9Uy@sds{pGT}Y;!TL=hoA7^492rRPuGR)8$bjVN-Yp2TpSr3z%E~DPaXj9{ z(!jMSUG`PV;0MRkou{4axKCj2#}Lt8e~Vwi7a;{GD4l!WqhsZdDsRtsPw(#o_h&P+ zGgyxcx=o>>c}<`Ee`?wSt6SuQCRK~W!^asJ2I2<5U=L^~l;YiY0uRsE1}|aUMfP{z z-bNyH=j4^Sxm~OcUD@D*G5UR$nF>iH^WZR}or!`sSXb@smQeA0KBn`v|3z-W;Fu-) zW(#3P2PA^a3#MMb+pK(+3MPY-LF|w(L&bF0J5n&6l7b`(59fMY(3SCdONftGdaWq& z_kRdR!Vn-JLi;h251jV$D|#%|!`(f$0QeLp;8U*J?0P-|Hc6X75<5kMyOFmiHZC@N zWd4@({uP{LhUDaY1z;ot#sD#evL~1r%!jKOz5?dQ?d%Rh0tCR;$6&rNw5HrdMAO#b zSE=8XwNUUrHUln66G8lZIX5A|@fVng2niSa`W_t83TDi9>!Y(wfr!&~hwG`2VY$~^ zhaFD!|KMOh73a?2Xg`PPUdLaQ&N zzs{|7;$!}pLu*fsNttV=dKYeOKxpUj#fDreCVFK?XvfifDFAwyTZ(Bs|6|e|{WmN8 z%c$^XcFGg7Nl@N_5;c|1yY}vA>OccsBwe&rcKJ z73XGRirBg03&NiKV!AkJ68}y3mR|j6US9hDo;ze2Z#I#4!|!4ZEwts~ZYGyi>uwW@ zc9rX_RuZE~Vm z+5P2e??FWUxY&=h%^8Mdu3VY;_RNgo(9yh3b}agU_s-Fh_ekX2rIw5cR)?o74mxUp zvHqOZ;v$zC12FV*qDi)#dgaQcb3&V|qU-sRfTNi4rAmLZcGG2-3FL~ZO0zk>!0a3B zS6@ARG$KaDQ50P+F$c4hQgI72^W~;ymcyls8!PS1$iCfqEC!qP(MCIN#$DHQMCtMA zuH&Ep6ad6oH;Lvor|E0FQ=g(s>lexLmMeGCG_fT^BwD+XRsS`>y0z+A-tfb#cw9+pcG2Nv)62N_IwuMhKxfW zQ_BZZJei91-;Pd;FV9?HH4@cx|1)z-LbJ8Jk+rEm?$r}glZtoowBCJ0I^TI5HwYcW zuGB2g51jv%zsWpD=4tLG49Mf9f8 zOSmD{Lnf;!N5y6-9D_{A3!r$fEiIM6Yt9&llSDj_x$us~#Cg5gq+?0&936kN-hc{A z_7JdMZ^mqWlHCidfs*3RxJ7_(AMQQ+zvw#WFu9s`-LGNB$RNXvGDb2sGq!EpM#jk4 z%GkDzjBVStb^86jZ=Y-LgXh2QtFP77tE+0&Q@^{OTP-l*?)s{gya^BWLDP+fiHqxY zahPY?U?#ZyS2Fd-Gu7O_SbU!=(K0MHa+md|hzwatk$!VQ7h!Gb<_Ykl+$%B|Q`mJY z)5CeT=RvPJr@b)kT6c4a&%M$@$24qSFXQK6?r&`;XevT3>)lbm>?~^MW)kAs zGr~TK>w&?(o-pqStOgR67mt?Mo=0v4%nb&0r>DM=#n#s6Z`KZKxRi-xnYMp^#WTJi z<}NM{#xa^_W-{B!c;B3sN@C7`yB3W6@ zamo1v%KEZ>>iq;qe#mCE6H7J=9hWUG_KBFI8*(kL+ia)>J67D zEmJx7jJ)TOqGQ%ylPN@{vs5qhPXotR%%zvIlT#n{*_sAO?WIWC^I;o&cjSrW!i1_SZ5Xo0XU%S z3ZoM3KZt(Yt1wb$7+9Fkw(6btw_+1*alJJ=A_4weas>l-xz5lsF|XJfy2N5RU;ZEQ z-vrt(lK`V%4H6CYuX3eG&t0W!>#dPGi}lA%K+(cTgWOzWFsTS+X8W({8=w=$RBx_H z(e^$;y|%<`Cjfqh`>nxE1OxmDRfOB+(TxBDJU%ERRKN+g=#>=}e9rl&NXyu%O*tZ; zl!{cgCH@xzHiy$u&MHo%{C1jPTBmA@Zbo}euELkX1&9au>8}K7Yn31? zyvk;wjUx{Nqaj()bZc;RP-2vO#<`+e*Ko+aV=L)=eKn*YU1*|(Wy4DLqj{9SKo#ps zxc#N>>fQHMqeT{ zcD(sxsXJg`r{45{?WSu658H>>H_EFPsWgM3b7&wvfYpHr!m&s9vu{C?ZY;Q$#2^8Y z9dd^tuFxPB@|K;f)#NGkY`sMXl$rfxP!%*^1LFg;k?H1lGto5RY zD+~6EB}NABPSs{B{l<$ylb2dE&r3_me^ik)F|_IEwl51ZwlcL=)uQ}~?J`tzvgAZk zl#(;D7Y3}xeK3#V;LOLK*&Zs=UCD_$r@2w!mYvbcwJkZZL|PcORrDVgbTnyc0a6G} zNxMl>Oh(aG$p*T|1^)O>z%;mqf>?k ztR6{VL?|2aM9Fx#GgqoXy6llAqWcJLxhrqIe>#eq*tLKxmcCJVTul+(pd7;~O&+{ao{B3R#m0(3Ar0R=e}!7Iz65O>uoB4>0V!M~s> zW__eMBt(CJ?%oed4zFe+#r)WKgO>t&5m@+DY1Endl17x`wx4p4lAdB> zY|_{Ob!=K%w&2CAuUi6noF)`<8N6@AiYD}>VWf&J~kN0acJlQjOIE^yi_9{wKLA*Fry^rb{5`fFx&DKTt*ZGD4)~x zZZ8gxhtt!w+FF+jwIQSWBp+=8_CLsJNO1-3tu>nyy-73X2*sj-C<6S7-|K+D#X<~y z42GO74}B_yXy$!%HV6gp0sJ1_+8=;G!DFh3R{2#w%(u?@PcTrhfM_r5544N=f%&M@ zA@^UdXAStU=Ej||bq2vTU}oF~ce~PTSGEiFmh5X)AE?``UoMi=5zj0NuCdS&EjrhN zRqiTHe)^$s|1#~^Hr|e~>K%7ib!?LAte+73C$&(uTiV{`>^`+gnWOhOVeND`p_~%nprU=8ri(@%I%<5bb}qtR zF>DgYG}`w)3cFaYXu4uJI}7_WQWncX+Zl;0C&uU2Uv+qXJ;~O6tU+m`aPhW&w3ldf z05UIV(%Z=2vIu51dB-~^`}#8lJc}CZzO03+K6?m`4{SKfKj-=tGUSzL=LY~W`rS8i zU5AqI{k&((v3nDV!?IP4JW7(m*a=QTba6c)!L4hzBzS=66Zwm9!H7A_ zx3(6$w-n|1ymIzJ0scrc3;)>J+t=`)GsfvN>2Bw4$GKh*Bj+6|v(~YS&7rV>+x%Et zhQ_uIS99GYlit{LY5h5kVY2&0vxmXKtn_oqm@tuhT34nW@%j20cl-Jo62cI6%Ypm$ zTW#E5k$JqaqGqYUO}q@U=e<+a0T!+PbBe1p)^TzRpi2RMAV$lcSHQXn`Shba%?5e$~4t^3(zqM0w`pXdaL}fLbM}tW(FL$Kce_ndv`6B z!pbF(=4U~uz3mue&;e4cc3KuH3xU%mh|hleoj&?V&Zmnt{Qkz|r0FBLG3A?sC{R5_ zuHe27h0@v@4dLZ5T#hYJnaEo_T(}>bQbhXmLy@qTdvdR>HDx1>4uugvZtgOp32rvg zX10^GwZfdkH*`38T=Bg~O?pg|=+6Lo_)nvB-DlsKY5VxrTeNSIx%k^aDFJY;Qh){D%Dybi$j3@64+t>P81s3#vnPNi2Gu(^rPF><_j8xrgngmmv8`U zE*f)i#--DAFIZW-abyIJE8BZ5H(<@I@5=cVI_qSy7OIB6l<&+cG}-qhh?;&uCauZU z{x0JwMF=mfqPK7i`77aOk??6#vCFQ}cP$04Iv%-wzyQENPkmCK%#QV*2q9S2K1L~rQm-Of7nornpCVfU0#9a9)AFVP)?pcR z-P8a{LXJtJJz|U69EWW2;BW5t<<*Z+8QibXx}2U@w+8L^T7!rEMb@Gv4&Tj+A(`r! zsF($rnaf^Mjyo(wEXEg$>=VQifTq0wY=4cT?W`4-rnTX@ zBc0tz!JqjDQ%npM$<*_fbcsLPPwV9Z>P~h0LoB%4a>1Z=Ep76#?>eG$7WnVDA4rj- z^U{;g&<-u^w{8_jLV*nQPVUcKPes3EZvzrfP5v2>s+j4LVLcZI!z&uoGT!-5(iCo2 z+fMrWeGnT;1zd!b=sH)#{SR^@_woru1@8CVDL5p2mn~z0Xw3 z2-J#vclyKvL}--nKG7`!(n5^^K6=#PKKdZpT9qY%8oT-ZsMC{46YEr>v3 zA;s%GC;+Q?qI?!_z*iAG;oWqeGxQV=74RkVlub^c*N=^+^AeRz$L@4~H`Jr^!$xsW z!2dDCsKOvV87n+qdThGl^D+tjxyS)?({Lu#uc%CmIh~NQ{JA9?n(u}e4pi>-bV&*f zjFZ<>w9<8~+Sj+)*Vjy`3*Iy$OC^ilC0?Vem-x0VuwvC;$k^VWTPG9qnK3a&BU{muD*K0@H_}}qO9H&usqSM7?e~6e1=sE(GR>5ul`({OT8(U zMOZNJ=`-_=z}3QsOauqjvFA-!8hxRv12ec;!oa5u&COEub8kdtU>_Ln{r^p6bL9kB?wV3oG@-1TO$}A@g5s0#vvQX1b~Fn&)Qp%|JtA)J;K# z$gimM0PV?t!;8=e5OR5%bC1;bMMaH&^^C8H8|-LvnBGfg?&O*tI20XA5uT5vtKd{L z`Ib^*W_VF~dfP6qAgWa@sZd^H;*q3Dt9V>Tu(N`b=e;^aIr5q-td_@i_V}zfu&^*I z;%F0?*$cW+ z5QjLKic6jPOUbBO11awcX1Y;WCVC1fpD5O>6iz}w7BUml|FaJ_Iu=I%BxD#`Rx!l> z$kv^}<1WPOL6_C3LnaCkTZSLC>1puw>RT?~RzV;HKs5Y$*sa`^%}Ao_F;RK-Rpo+o z(hm&SPD|3MD=QnLbsWko^Mo?p0R)EzJYkn?D-xF%cRm(#47bKMi8Le|(ouCgZKeZ@;b(7=RG3iOZqjtP5nC#V3AkqGG|UzL1YH%!VGOBkk4?>?pe5_L6zcJ=0YwgCPo50qfVyYL{3XOf znkdl&YQ!nen2KF zQ@Ri|IpadlIj1uZkb2!MVsBMX)be-@zwbPcr=YPIaZbS4?}LxCvidceRJPG3q8FsC z{5q5ge6FV*og6D%8)&zPY@_%lnsRyHf_lEbqFu}$5KJ$3R9JS(HTQ^!V)dmHEX|_! zRRurH`<;c6@$RY*pQy8t$>~I|UvY>yDIpOJA%jN;f>eBF+-51EBw7L^FI1AMekkWJ za~xSf#t1sZNSA14gvH^t8Vtn*`e2%sLTGiDT6*OcaeBw}qZ}3ssc4m9FgZ+jv#Iz; zwC&HuoIeH{aHn)`#`_09>7ZY?{nf%ntK$|~*0{end(oQ+Qm`3;5gV^B=hPm?i7CZ6 z@U{h8YQkyfY}>yKBNoU=s`JnMSgihQv4dZC*!lFIOUIEz)GL4B|=Qm|;nwskB3`1VbKp^dk;O&u6M@y8dm7Hlt-;xy5TC*PkWvo|$kgC_$>$__6h{;cQUj zRTPy?Q7z3cA;!lZrj%){yZy-;g6T#$-ttaf`Fn){S?p!I{yAm2G>d!rYF!3L;#qnn z$6P@oC4H>*3a#zZu8NoV5a%|5QzxV`&v2Y-cke=d3YIzNZ|LQ#1$9Ty{P z(GV!bdnpF)bF_SWxsJ*`<_nSanz}bUJ81^%@T#T0)mFgLJ^+p6RYy!7fEsMK!awJQ5S%Z< z=K{H(sB0daq9i3AB1lg55`K`F&;%u!b(;`+*k&bHZ9P3chtC&G-9R?8ic`MLaL@E$ z@^d`-Aw3zfd5upBh6HWk&JS{Xxx>}2aa5si6V8*{Yqh6KSPY&Xf0F{Ud+`$oZ_|VB zt@^|)1?dK#C(eGSAgp1gbh0^?&9pE`Hr5}k{v?8MnqIJJ)u8gVGVrh|6+fst>Lx0| zgaHVpTVFB`rk4Vc8cjQ_Zs{#WbhA`xDiEqcy%RQUqQ=$<4VIBnLB6E1R>f5$s5HY@#(zGujjBh;u!G zs*lR%pBseyaur$1>_8;GfHGAYJq0hd7J22JvcP09^@af6%bgv2-iohh zW9U?@IJ(e%WdTG(`lGq|UM0G58G9kaV?Nim`IttaZC~4pB9DNDs*kvakk`3Fpjf(3 zk2O_AUR{IupKJyR63{~7;``6}8x20b07do49Jb)xcBd29WmR5SCmWCFEw`I}DOuUc zNPJGyi;pf>4Y+p`BYX)Ng{LrAZ_~e%xI1*`H%xqHyiAePFAm%Sf6hC8tR(Wt%i{VG z`uX|k8yJ`=c*5@l@+7tPj@8Sms$!=CRjM>A?{6Yjs}xi;2)GR27#VAiodsYopWY5e zkACO}l<@iTMKw^3eS9}Fdvf%C%=9aTt#i%?8Tq+8sr2PCARubEdjx;wYzViGVFHCW zYS~g2dpmMj*wA(IwBLG}*(~t&lssORgF3Jz-RP>6b#s`bv!){D$`84*@Fil zGkDtVM7{M2P=H0xj_lm@g*r0~-Zy|z3kN5)lfW7ZQV?Xg_eQmYbPH(<{RrM;ab)&| z011cb1(c=K4Vi-C{d0(N@REeO)Ux}Ug#$0dvtBDAep)bS@BAa zP!dadEDSuDkBuIu-9*Os{Cteg#Ls{04rfw+|HiDS5Ed5h(%;+NofOW^&hDiZmGHQw zxhbGMcekbeyU?((Qt*mF?+!_N zA2|faC+=JVBGI2Woq79+zdj6P)2*pxTyoPVo2(O0+IH(0tv;n*K>1s*?Kd?Gjlt54 zqDzN|QjwZ-8Hecp7;SMP0nDw;rzR&~+qM!rKOz?wMQtIH5oO;fFhzXaqFHaXq@~6C zzDWMD3zn8dr>Lzw4S&s5tPONX>i8Nd^-p1F38J=I(mdJLO}C6~jRs|f%0UQRcMAcr zzYzBb1PA<<8)FH0i0V1f%b53D_Z3-zRzxn3wo3WFsr zawEWj-bF9_-SQDL&^3@%3|x@Sy7N%rLyRptF{;geWB2@VxzHLxA>&yCRR*drdzez7 zZN~IB5#|jCy=iQi!QN031qH9SEFJlU%vke$0}^U(BEh|5$?Nbr-%)RSC9i(x%)E^y z{_KafwvALYSK3PNcZzTJBiBl~n!({KkBWJaZN|+rcq`uJNIjn)XExelE-~+#Sh$;5F^~b#**|LD%WN z;UF7SI=~rWu+w!@>9@EXjYsDp#EwV(tEc(gb6;tNmpR!Fr!15ahtw7mHyN;-v8#Gi zkqq>O$%dRV)(i17G6Fb)`QJCDGvP{qE=(OS5xJYX64!jB#J`@e{@{I0m@cyzOw^I; zJpa&xMzXpELl-No^ad*A7{f4)F$-`m2-;`~&6EC+zqvaQmDXRJOTB5>P0N(S^SX9M zTRZ&cE=ZO^c>sgMFeP;HzRLFY3~xv_IH{Sz%dBYj%^dyx%9#~LJ&ZoiN+6S}Pd>T3 z*f@8}896`n&_m)V&IseNlbuEhv{X82y08nap zJMm_e1DhPnqbD4BCzWSoQ+gGWgU$B0fmq?CO1i4IE{vZBpS{#XRSWPoMVo@wUJp6+ z$I6*FSioExz1$AxC5@wy7=yepjb6! zaXk0*yhl{y?TlEn(p0aKF;*Gyg8|2w7#F$YddO5YLI!2;G6<))9^w)9V2zj9I!gGp z9o(b9dhmChR}YR_k>E|#e#ctSTmySqTb&xFK8W$a|IXh0?| z4j&IclZ@PAsvtM}iM_YHT!Y-zBe+1vU6X;E;1|Rp_$4eM4j~>SxCsP=0s!#U|A%0B z*fHxamqUwaX3WJ{tgR!0vNo80>NSm^(+4& z7;Ry={6EkE7@@fgR9HTuh7d+ffRBhI9#JW_Oj7Q~OCE$VKIZ8Ix{dV!`IK#ouiGQF z3xSgY18&h8Go-N8UncAh32L(9oPXvUV1WbrF@j>OtT_KfDnU4f&u;+a-wZ1JD;Ded zd|T}A58mFGrV`zNy6eEwsa**dKW9c+w_t59<5 zoeBPuC=tu1viK3gPNFY-VomH2(!Hb)N9ACmk(SgW7Tc)(Fw@5?3cVRA1nGE8k<0mPN%SI$ce7}}k{x#5jz5Dj({8oLbex^WU-m`HOO{{!7=-z7 zxqbQecs=khP$FoKI?IB%;?@e8zmr{5YA+rh5xIYjq1clY$rwLcOLfW4LbcC(4C-zx zul(>O`#`~fdZ*S<%61(%UPyJ>p3xZBzSlq<QKtdY8+kkxz5>e5=|yu{b7aY3u>{HsENqac?Ww z^@m@pQ%rO;nXmULIsGci_AWo(-9Sj$MG;_axPVnVAH%2i=KV#@oC#=7w46+K3Hf@B zs=|osSYe%#YdY^dNaHc%yZ?m62^i$&YC6cMFflk-G?i`asX5U571zj3)rF_hkB_bQ zO3((coR1mm;sJfz$@RWJv?b;3lQjMrDk{oy$@y@&ca#`>Cx!FwKI1b}Ll`O=PD#P_ zO|f2j67yQMmPgD+7$xht48uTiQ0B+9G9ojroAKYu4Az2Gjx8gfci!atQungu2J_UK znxqA@gP#u#j=PiT@MFlZ*G?9t&cRZ#Fn-_2$vJ03>lIxZQKp?*O-)oj1M&8|y(IH~ zgy)V5Lg576k9LV+(SF3&h00l5DW9CrnAQ!or=Ge4BF}Xu@ff3NmnE4F-eeNqyeV&A zpxz>#L{yEnpvTpjZN^fEwENlZG%!ayvyz|uAI9C>u86jrJi|r+9=E_(4Ufs!aR$mU~1GKmIryF6hOuOy1Hlb!BFuFbzU7g$ow2XU5& z)YyTWXQsjZqK4hv@B<`ay-TTk?>_Lpm(~;7kKqxnpk1A{wwijS31N&|H+z6fJ6vxF zEH;DpaQQ&Q9cYN@q>kRrNZuw;kaaZ6S!pt8hNK*4Lpz>nFRuqJ-q_qFS8fCpNqpJ3 z%Un)SzrcwCfYJI9Q8_m;BgSnl4hI$}pbWYVp!gL9#H=e+GwlxPQF3>^2zM#08#}tuA_FU6ES9rHu;4r=+Zf zI3gBp640vQqh2plZukbdE8kpt)X-5m(`6p3HrW)+yfm{My_^<97t;2HY+`E0bd8V9LLptZaDH ze|0`a0|WNNI@w`40=Y0Ce73dX3;idWxr}~F6ZufMVzC?egq8E@p3U+;b`dDu_~+~n z0xllDe7YMZ&)u{3PVhPLOT{blYb-PsC%5`acRW?*i*OL}-NrzLFX*7<7;?D3fA7M2 z(6`deIJv90nO(!8*4BJuJ96HX`VUH&Uqq_FUy+EE9rk7~@r(Rj-rtziE*76Mg_Lpw zb6V3T+4S$q{ThpNHaPV(63sjbeIASGXQH%Dl{|Ut2`L3qtNAT<81wWSyclH~NO?Yn z@)>3z$EQn+{KPw$Bn*~m5Z6)yCluiC5e3gXY#x`b_+%v6Jym`uRn&OLANsKRvZ}|a z+o8$@kYD~K-+?3OPg*9ePEt4!2oFl2bLeBCn-ulVrMJMCw~{(SaS%`fD@)w}=u)^wHglr^oAG{zn{rGBazQWr(~dxux2d4yYpn}0d(MoN*x%)BW6QX$R^m2Cx3}DF zB-cL_jL4EYo(s->5lLn?I;)z-33X;qPM#s+;KLeDVdr$n5ol!{3D+!l>lf+T)@sh@ zKij!c3OIq?}K@FwliFKAuA{l;C~Iq zOFP9)p@Se4j3P*_Y)Wx8Soz@~c+2L?yCb;TzT)BDzJiYx%jh_9_xHtw{C89tdn!>J zYYV2mlhNGtrFq6@*3(ud&$G+@jai9yQk(CqbPaddhC}jCu~VLtyOG0r$%TChxw)6y zo|zb+-MX)-YcV>x9)?SjnrIwF&DAThQZpL0*tCUzxg^9u;r4cUDM;P0RAjV6lQJ0{ zEuOsJ?r~J8fEOU=NYKSnFv8+VSz=0{LTQK4F|GJv(<6J~a7 zmgBe~aFZ(lF7MD-!m(Y_7FYGYM*F$>q)@}jS)y?V3zH@1n5S-PkyN#oY10`!*+kf~9Nh?vxEoDge`@4^D;lccv!R*K zNFm&)X+=8yIE{}i3PtCIvw`*=5VzaDOygUY#?nITtduPMpLM(s%c>`h!>&mM@R&Gk@B z%>*Cq^6E`UJ|UOZ@np=001)&*?cdqFnca124Ox9z4*K>%7-Zf10q--~ihZSVri^ z=|29(3)|L&(wWY|Y_W7SpZKMiv|RJ$X&viCCrcsoV}}$?FR5oSekNWO80C6zrKi^i z_X%tEaw_eO-|>4}A}aP3N52msha^)#uzrc+Dup=@be1dz`lBup;NPd`7(PtTV9k)=-3Kna~L}gEazc>KgsTpB+hl}tY>xfyhyV55A)00Vq zK<&Dmp-Kn8T(hcpg~`*RKV+DkQuDx*)w+9r}D$x;}N#E+{Y(mr7>5> zXw^3Z7zcFtTxq$_wtGQBrA+SD-{*X0zrbNq(hT;tzHD!O;)_4vX+>f2&XD=~(Z6p^ z^}-xd^XPUPJ4PO7A45iKRo+Z0PI#jAT6TIW@KCw>6|;TF=0uZyVw}-*uV%`s?id^4 z2TP{b6{9+ROA$Kp_fgs&1a(EHC9H`y%3ML!oF5RHrId{&D!oj1O%wnjMP!zwlyN>2 zI@sqy;ROMHEzGGLZNyF@tYs-CjHS$x_%0=SR?3%H4ZkCny__XJPw~f1LuF@9vf?ZW zg14YgQ27*g5f|5^U@?VWG$pvgI{{VY&#n5MYoa6d?I|1P*SZ6z4htWHbKD>OhYJZ( z^nB=doPiY2vV6y>Du^~Ty~_7WHxN=@)(PKj0~F<3{1{r<)U-58W`c0d6P*qiC9Sk7 zFxy*&ZXnR0fCOAkr;XM?z$cfaZ-zl}y|vF;AUQQwSDYqXUL4vq3YP5qMQrLO%!KGr zRdhzDqV-VG;Vpf(KyhJwlVowzJ6|Q_NY($T9C)DrmJLuV`Vc__gQfnbFNv{J!sww2 z!z$YP(K1#4!n%;x^aP>+9{?7u5&$W_E0J<6BvW1}R^FH4?%yCRX6jgR{pGaJB%$4% zaA3dsQyOLCL6L&b?6XP3|6d{S3-RawB@H;{Bwj1hsQyRbP(Fh-g8y~f zmHa?*fjjemn2s^#=Lf}VcR<>721Ld!wn6FGG(h}PqCLJfpbxl@E2O#eD`)cG@Q zA`@VrupazqYkZ%EbzYZTSQ&ev$%VB%0aKb5rR)+0+TI|lbmjJ~IhDW$<4c5Rl(2g1 z9skzmiR#TXcsO1XXk!0y{|+ipwU6osQUk&VP)JZ5q+DM1Ka7V4S;#SW7SNr%s1@t4 zAe|SyU2h&S&7m2Erb);lce&Ye(n9)})#`4nbJvbq(`jQrrNPfjF4hFix_e$IKF>yGgF ze1p3dPEq^COzt^Jt`YGg0v2qTd2()QK5N2y>b0s`d9JiEHV$NZVCU#q))f4MogEth z?t#;Py=<;}uhl4u^z3%Fi{nhKjmokoL|n@|xPGohQ&S+zHDpUnvAciCbG1I z!KD_6$zwP3+i#YHRLzUw`E3#HiCKI>Wk;iH#q;|MS_nl7evKT7h=1N`IpyBTZDQS9 z*Rm}fH3{JXj>mumNLkU*15;J2a;XoSY9Hx!IDzBxQ+~ME%`?d`=dh55sWQBU+%zDd zfB$pRwuYbDZc}W;9|_OhvJ{OgdHHhik4fd!Ynn z(LutHQT`sJf8oRe-|y+V8x7}ts$F@gCuT6;o3V(^vn6VqFw#KEg9d_U1z$idzR7qx zVA3H)Ul$wesMZcqi|52>zSh5(>I!<(pw zYonQBwVKYAzdlU&A+Ca9*7U zDa5gJQKgCQv_#l2(-u9io#^>PVjlu6xsr0btsQs6)MHBY9uUa)L>)gdt>V4$ZpzyzMi;F9K66 z?CWh_`ak%40&~unp>t76yjkalDF>V}<>z=oDY99lHN=z(cfLYP1W>wf7QScKWAIH* zy98@WN6kXUAG8{r_{007p5$9`U0D+wK^mBO^V=6$)8Vi%>I@*%bg3Bxhgm82CyO6P z<-wilZ06DBQl*=#Do6mPtI(m#*#7ZT6ojzx0Hm5NMzE~Mw>AHjee2<`ii}bRPF4on z{eQT%=B^ebh);$VN_f1DlV4kG&rf5<_tgQT%nUqD4!ypc>6Rp*(V~klx9~zF>W4CD zQq!8BrE*zo_f*J~`0eYrt@2P4!C5~qXS|`tt+(X6p#O}&##C>T+cqvCBaBnNgvs*O zMGgxTSj_Z#h}<%LLh`8*(m__}M48aL7}rd};h@!v?3AERTbM0)SnSGyF9Y!TZZ}g_k!ndEI5>$oBs84z6qs zMlw%s_dGr3Uu*FW+VCoF9M+CA`S*x1E{vzMpD0gp&+00VTb*&wML3zXVRF{2bvaLk zg%2X7=ylawR~X0h9~^DQA%Qmj!oTAW%MC9h+mln$BjU2@$`D`9Q%(kb%Y<=zl8HGX zvM(#4*UO}Fm05{QvvaghaP-)&p7s+bf8w7#*S&~5FilQ+<|>|AOO-?x&&DRsBN8H_ z!idh*YNTFYeyQI;vt<2w>nyxy@-K#8t)4CUZ`!}CbJ7&zqFJ6Y0;2Ub&$!cPd>tBn z^%A5y8}(e*?rw8B2@^o$EnWMjminDhGXcQ&be9w{gQnCH6r!!v;jcL5Dy7)piu2qm za?n~jQeXC?-aYnE@vF`iiVL9<`Xj5AP|Vst@`62Wq!npT=6dUVQGcCbl8VHIv{>r> zweb&)0tH13ku@wFOIq}f+SlVHxbXI{b=OjgQh5hPBoP_ju`q6jJJFo(!Zf zcW*BH3dS6m+!KQ)$sa9~otb*wF<`kaW=INBH-hx9a6(N^P!CH9RoO~IHS+#Hn$6zl1ntLIvG>sF6OmVy~;v+8^9NLr~E zPL%Yrvah-$em6bJxysOW`W=g)x|3qh)fe?Iz;$65-$t7o8ajHN!2r_o3yAt{utD-e zLP7QQ_fHo*V)n!PZaORF)r&BfeTK1C=u?+Xkl+K)E6fM;;mZ=yNZ2PWi*)3=`Bx!IFqQ(=%$u}z*jmjs5?&ZJ(eIH&RbrU z6)yT9Bdp84{Dga0^vP7uQd#z)0t4*lQ|t1cxt{FS-fEu1!A`Bs;PaE!srAmZpBF=G zKnLW%DUIp-SC1m&#I@semjWVi{$+2LPx&CIS5V{?H!@$2R#whw?ltLu1RQ|Dfysj< zf(Q8XZCdK;s>eu-^k=KxC(L!kwWvCqEm+(@U;_&+?RMpxh>Ta-9+q!|8D$5-3)U+! zvKQFwX})udW(xc1$k;3fAH}#AsNLVS6->UeaTjCsQKA~H*B>tG`%H7jsN94ICx!i3 zq(ShBraaVu1+k=e#=PP!l2(_BpN)DN+G4+Mr_D@%QI+hJK10yXg@m!-Adbm^_s!5o z;%l&&PM+rFIk=J3VBlfr@UcP45Za#44q1#OR%2`>N;hua5UmEA>=3k33XXCt(Svnx zg?f{J%C8eenEbn?BBOF&+DKaPjKybXZ0rm>Fctzu_{k^LOq(0ii|)T2nGmPNQU;Uo zL&(&QT=#ftvp8(X-lsu|I|c%3D9p4Mh)(#v(J;7_hZ5COj+A$GbzI28j;uA3z~$Si zXeZ2A`2+>+cM9~9ZJK&^O{j5zT90AG%rlt-VyV5ffKROnD-`7E>m^b9HuC^9^gifh zmlDV<M^`F4K*z>cs~Y-xkQBI1|3jnJjwg`$kylY~`_P9T|5qACW;`aHrk6V>;1C z(*7lobG@^)h=W8)nM5io1;nggf}!eg>J3kKR-ohoB*3SJtu^cSMV?d#4Is20c2fF9 zgr|_lu8`*A8_g%5$_x3p&NUe&YbcKL0h$ZFGSZ1=3}&14b^+2g=>O_EXf%JA<6r`5 zNOEt}@Ro5vVgYjADGym&_*|8qGqB7iatHdVgG~CH)w8i89IuuaC@ z9O4X()j?_+1KmkUI`8A-WiblLi6OHfy%dN2DoZQ=Z^VB7dyK}afvfP@yE!?aA z^4%Jj$&|(FI|zmwsaYGN@~`-Uei>OXVaU@>>4zarsb=yx{SQJv|IXwTwSYiuskFhx zmU`lEBU951uD3@KDEA(*J{ChMHpd#zQ~pDBpZE zPF}FBivO1aiVYeCyb5s>Ccc4MQk>%p3W{6|^j1Tv-KFk!=08bh_-g4w4#a9iG0X5H zcO*O-Bh^Ri%Qqs+J3$ZpGHZ@Dv81U^9iA8U5KVR!>6$F41Ds93e9k3&(MwS zgL6U@7|8pzfwGos?UGk}=j1HR_TK3|=75kH=~3K6LsAq~cH|6i1}ROC!0K+u_~BWs z=3|P22Dl#FlY!PHcu4S4=XOulelJBP;dkDJLrv}hh zICX-?;1E%W`)417u(UiURT_<7n&@B1IXX0j@fL8JL*-KhF6Z!`(U+uBRKe8^BOFDA$ ze1_HSS_YYm9-WUUp6T+?MlW;=o+srg z=_q2oDy>QTQdE>9aj+BBHQ8lRo5@Wid56%N zZQHhOqsz8!b=kIU+qO?W?>lpyIcMfOU;LMOWk&AIj2(MN-0QcN`E4m)M>xZW^Vi5l z-PvWPxHV3y-V`(1^%@lHLJ;cBU8FaKwhOF;=f~+KaWd*yv89LFU4t(?agKbEuq=PB zYFY$2Dp{BYJw+Op&+bV&i%j$zzvK10#mnfQ{V&*6`9pKrUuw7PpB*F5U@R2yo=?6_ z(4S9&KxGM9NKJvusJ5T}Q)6H$nckIlr(S+@Bo>uyiDfjv7m15YaEbs)2>|fpTrS&a zNX*Jlr8T9X6;+8)!+6 z&P>TeR`AtM8^(2kQ_w<8FsXfO+@nhAJ6p@%Dh4|Im`5Vm1CZz^Od*!CP_W zmTANR`^!N;w2v0uLH&SPK9ycHKZs;~)YM9#I(Dkh=#%A?)pR)P>n{5#N|17n(LEM< zu`&I!8h-k`;TW_j{=O1^u!Jn3q|lHB3gC~ND11KDTa9fIJte;Q zEn!FIzfWXe^e_iJT(zd7w_v*45cN?-SV5(432^Aj@taUA&J9V*38zK0{e;~!|5@YBR9m;OZliG50B1OsHUzD%1Zqjo^m^xZY|Px? zNLLK9QNf7<$Z-bJ+^NYwtChZ+*VXr}I?4#N2_B2-1@tIXD2fhhFtJuSjfY&h7u zB{(SG8Axo4;Azo}KoWN{zM!`LbG^6pBB-B}2g29iKu&(9n#!X%x64Ta6-xA@nT-Z7 zjiuN#gFWYO-yI#CEH5|>g*0(Mc*7Vb1796&EU18!O%%@DV>{{ZgH*(ij6c!^>mVBW!W}59p@e=NR8G(SCRLl21|m62pg^SO@8LtMBZ-u{v~tcq13N%#&cB) zK4+S)hr<*%I%2$~5oIT&$P~q+LAsjUm_&YrVn{!J4KTW5VdaXedGj=DU0Ai{(z%J7 zTN#k0Gu6S|)>SIMbgIbV_GVew1VGuHAd-`~V!)Xk*DFPQe5FMq%2c5-$IMSF+H zqji(AXcQ1U_P&`A?Gj|B>vIk|Hd6l#Pd8E)J3mmF>HQxVbWX_WSedl=3B9oJpamxd2!QlUU@uLP zdr(Qtdp?CND}Jr`UR(S#-n7QP$@yAMkqMNL+kE`q0M!F|ONL4`EXLS!di5EuG~|c% z;Yefu5l@Svaay;UJF=2&Xk7W&ciz#O@Vf?;eep*e@~h!$1~cRxgo!b0Vic>B-sj4w z(t%#vy? z@P1@U-*y!tN^wrZF=mtKL2?I~ltx3SEtB<}u)-G4So40nd};2Wgzd6U`qDTd9fIJC z*tVaZk|YKqG2FrXT<;%uniUm3^_pBw#(|Vw(7GPx`L=|E9#W_6E|ot*Br@(j1~~#u zX9^4doSK{5;JWC5`JVjU2wj?;mCjr}SQ1cBBp++TYNde(h{rEVrtLMINVUkvVf)w* z%zp#nLrfl%Y6GWE*(@qh#$XDdGv~BCX&jksjV1sc8c%Pb{sapbn2AQ_ZcPiVhfPD` zY~py*de3}6#VL_)xlmQ=O5|K`(er+$C{$aDq3TN8y>GrDz@PHLaX3d-WlgUQi9Rxp z+R9iXdDj*{jX$m#-SF-);Xwif9^VMuA+!A4+q)Y2|MMm2B<`%SN@2tQe_9E|jQmgJ zePIe5@PE_w>O(T_tn3q1x$}lMZ*y~b!K(;(1s3j~<#UbPjFjtdEHq-$!%3h*CrdZK z|0~%iTj+y-qlVYvXlJ`icOdMW_n6wix(Zz+CW$2Jj|zb-1@pw}woMJ@s=nePIx+cMuYZ&!U>z+q;Sw)w?{w-sh?!f-?dc>3SL4Xj9h6f<)<)UX zZ877C-`v>UqE>|VHAfCfU&7RSH|o#dGqAlxq6%fB7rcO=K8ZfT=cNqTpBE0Eig}uF zXg!6d9nHqPIv1jcb-T$+d@m)7nYdBvWb6Ay4zSfPLCuZyFDhJ*$h}9SX6WYaQ`IXB zZKttJz=x99xuQ7A^|eLj-V0$VM$3*Y_-}p+Q0e`h+jK*zp+0dJN8wr7NsN7f;@4XX zngi04K3yy?{}^9RuF=3OBoiFZC=()cag>+Q2itA+G2WP7H&tIGo^NoqrS<(gB^TRP-N`rzDUYG0;rJXMw z-d!aUIg7wkbJ_9dl9Q>E7Af{pPMeRV`4FULRa8yQLa{~aHC&F?!#>D1z&3#sh|-xB zu_vqVq~@BQnRG{{eFf5M*Fi5W*uQ7tn5x~}p`MO%cDh%NY(nbeuh#Zv2Lendhv%AoN#rz`bbtN++?L5w&c z&g8(&_7Yf}vN3SMOh!46Um?1mz7fbtSWi2?exUYH9@9y-!d<|6@qCxfJDmR6Decbc z+psiVBL{{s(r<^62HI*l0jJWYzG^j z7GU5(F;Vgq1b(@V?Y1JZq=-v3^+nhVS3f-8E>8s5RwnqqgI>8T-M z-y{)Py`%Ge{EQvi=%4sIrBYIleXMmQ6-BC&4QuprAl@ytkf6QcB}>ekrr!Km`%J8o z%orH~;%mPa`CrTl%I?yD)$)y8sFE62qvZ`YQsNRsIk|;$E$Tt$wyAg&@o|wdIl}0H z3QLYm$O33i_P~4UfBuo*8{}r5%OG(`nN%-28bw_B7kv}3Lk71%dNx+9Zae)?oOdQ0{r-Z#9-Eb0XV=x z%X1LzB_}TRmvCr#YiC5hQ=iLK+|@Vk(dnAi3yS$&A@k_$4L&#usyHst4r;J@0X@9_@pyGWdp*NWeo%e{8j(@ zc3>j+3(pG%E@Ps|{(u#ZTn@ngMnH~njuH7yh!5J8ymw>9y88U$DmowQFJwRARyNQo zX=;y-<$%h8;coJBlG!ddFbKd1#s2F`+q#x$(oj55mSFZ39GM2alF3NC!nG)?`r$5j z2v>J(Z5pzQe=8FMq`RCl!+i+zmZ_Q_6q3Ivs1_YYp?%O4c2u)VcA&fH+c!X(C&0fE zDXDBqAEXitT|nu1Oa*SB!5#q9^{Rd*J08{L=X4>(kV(S3ggOg|SU@sofsk_hs)wDf z#ftb_L@A+iP+t-or4@o|?sU*Qk5!{WC(1TH*=sR^$FQ3QqnCoE?i`IdaojxA(EJ7q z+k}r#{QMTo#pBRsYmDQGFpa!T#f!zuC`x!yQF$T3Wud$!es^iL_#vTYtFTFDThipF zp`!7)FjQmE6~kQ;Y59UL0>+9WF{NWlipQ zz7d4tMyYVq@Tlu`TU})#@}pdtXFm!C{=|4)h>}^(%&%aK3!OdUf@g53BT@r(>${d` zN2OP1sJG&SnU9vWyyh-Giw&pITZ?9NPHSU$%?l?eZ{cEIVYdm`q;=Qs@U1hk?~Y~X z@~V3O00?hlYNAZXo6+!67g_|W8hs`7U&%Ara!0sGN)bL)A0=jYq&(q=m7rPvN`b=h zz;5{vFu6`5OaEY+JqOK=z@H13Xt$qK&VRhWqT&Mc03cw3=!r#47S&z_pWB4JK)T;Gu% z&8GXC4Mb*7k+xNx*r-gXqp1voDaEQo@&bINl`0^rV!{`=`gjm4%DOhS*j(4_?{8wO z^ig=r<$lK_xL$F5xi5nUo7+>^P~BtPSrne7gGNVo4K#lIFhh4@WMC+7Adi@!(Fw4# zT&Z>6{7)@_rsdzjCBmz|GJZU6$bB(mEtF$?8WguM5P;-)y?{v^WYpcIC>;W`E&$`h zMme~Hh8kPXiCj|{6bb~OV7Ql3xSH|c;*BYMD^KL=?DyH?Mfrp-tubAu(7Wxr5$3wW zD}qrR(h(r|1`sb0j19LRtI?MX-QvPeY2>%9@aUn<_-$9fcOWe=pG4p)CU)->Yn}}Y z(8brlUXydWl@wEG^d2QFP|wsFn2@P2g@=C$l!Rpl2k1sW-c~aai7;T3^{67B(PK(0 zQE?A`_s##CZP$i|S6{QYxPq>q244DdlkOM3Gbj z_>So8V#KN!xKKQ@WT+Cw$Bn2NTu}5IjOHBo6u5GI+xoj<{=L#PVa84ycrG~TC@Raa zyV{CHPzZrMski-A?60<{M9Z$6zw!k3PimeQKia znDvz|lm(r)nA;l4SAcbJ@Sp(A!2|w_)BT%`l-yMbB7&&x3IJY*E%cv|GHI`oT4uz~ zb?D&%>d0E*YYn@_`PBellir?GYBN%6%4g$Y7Of<$xI~GNbh^g!q1A?~X+OR*fA(G3 z1kXn~=rmStOejAo0ud3Bp}|OuMbt#AnmWNoW6Plx6I@{0J+Se3crv{7_VAKW?=24@ zvHXy0K{rBRlD|!sj~tfnFMH!H_42aRwr!`y``pZLBP)LT^H4|R%a)^;x*CG_l~_?< z87Jb==Tbx-$Rujv==Vi`Vwog#WP{C`FtaETRD4RgITTTK{kaGY<|BF5@Gze=Gf8|9 zMfQ2O?9`gC<6-Bdg1B2W5$G2ENxs1(Z_MLqvsw`vQsVK)2VeDp4Rwh860O@71%y4! z_!ex+sV8?Uj>3+(J(%q*pulKeZg*SJL8_^BH zc-{9=5aRWgImz$Z`dnlEbJh-d{4X-6f>NV{o;K;*5nRMhSWRa3>YLA0>J?Fk;P>X zn44uEl<42g$vh&nhk4?Rn$c|z_*xI{n6dPbWmDd%c^EU&CkoM#jVi=-D_9{oQ7%|p zX{Y~mn0_OfI`)GfN9xp@oi8aiZ*WtCc;hB7PJUMkR)c{Fn`JG}3nA__-(xvgQrArgAV0M7rvG4uD%Pi2pik*z_xZ}!e05we!Lrp9rs20fh8=v}X$Hrk?BTAkbn z=w)Ar79_pTdIE-IjxVfVA0KJTg}+?erjPl@jJqqy{Nme~Vm4R4`hI;i$cU34%#U0V zVzrgV!Ck|?>a9wC4chpGr?c7iHGpD-;aY{aeYPP3JXktoar>CUbQbzf?`G?Luz&HCihDPk z*q&(IrG#<*WuEoVh;KhgDTFnCKsM2d_^|G|qQ?df`8%}nK?lP#mNw-JAH00ihWlF# z9-6OV5#d4DMz3r=X_}@=_IFi2IUzF<6(KI0-LS(!Be4%%(y! zCh|dxlIFzVY>m$yco7^_*@XAYsoWis+7|vcb}@-qH=6?aQbv-ql}rbXBk7g*qPunU zN~wrOX zLOr|wlD=Z$?c)sLldQNT&t$02#hC5oQP-0Wa*)Ypdb$(IdC{{srCw-C0f~l-ik7*I zE~&PnkX&;_M3pWT!T;R5ruqH=5119jA=gHCfN z;XA~NbwEQsZT%?_f6cC5D4mThSNn|ev$bINALKZVsU_~=2)P!8y9icRE6#+`Of|Ij zXa}qntlgnpl>tKKQF$>MtKGS+{v^#SzIh9q&%(H{#l|RJOS=KR;jij*jbP;@$}7kP zuMs)oVfEgd>>u!Jf!OFB5@ej_t0OD5LB%IrO}kpKOrQdG7$x{Fq-<~F>zP#S(grLa zHM*Uccl_}+GRp z8^gWJrN55AGF07=<;r!?80Jqggo78)o7op$Ui>e|pLtcI2&M`k#IpoF&zwrAhXQ=~ zr|(f+WcKHZWdi*D^YZedQZSbfFz|1{_^$_4^ZaW`QNd92^~nA`ftKQV91~KbIbk zg9fVFqh~kv0}8o@-WztGur~tBM9Mwj!xEKWTPI%6f+18-lS5=sVig zNwr^`d8G48pMS`PB(MtVSl&BE{~$xy=N@j>e3szN4fn$B{Jj@NQ`ih$La#wts6%F> z^|&~5Bz1t2Vu{Z|nF?od9K75?$#fJnhm@R@l$4GRF?BaTg$97ZY>9@2RUreE>cJr( zxHvg6QOaBVi3X7(?&?A~Y72+avDh~R$3T#44(g@)W&QCqM*`|6yQz-k=p%W*U%ia0 zpak{H+AHu*sP7NFfq?-S7#NLa^Y-?3w<;|iT`XOIpnhmY*fYJD%+wqIpn#+O3uNBJ z`qr8=Pc0az-!tdTh_%knTa;=4XURYJapJXgbskTb@PI*mDgbTLPIfix7xMH16*y>+ z29Q(vJ>&YPyJT{F<(I?wJwH$LBq)72Or*}u&GC>^Ek}*^2OkUVLV?3H0k?DisTAiX z=5FkU_T%-m+#Ux1_ftQz)st;>iN$%A zFjqLhGKe?F7WSXAb6^K@B=$5q-hbVlToIz1#p26!x@wVN_5uQaE>^3I9?Xu9H+S>m zQ^2Cb`|<9e{Hsd37VGKRG zSR7t*yu4ZO>%*}1e?71UybhOWa6BV>03OKY;85}4z)2Ury@P>BWE{7*+R#+6}gSGmg#0 z#T5{6CSO;TGOVbZ9f-*NTdqqoCP211h!>AnB_qBxhr4+g^4C{%0lz0X7YrM!-?Iv- zejP9|A}L6nkTkJC69*Nmod*vf8V6aBGKG3KG2rRoprJA4E7rNQa}^#o8eZ*7_J~}qBQdkngd6Ml6(pJ_>3x-y_q_g9 zDaz>VQ>O+QTG+4Vc>Dm#9wK-9g<9l2?jky?jA#}l?S927X5%}K1VCj+DbT1;I+d^li?|1E zP&83$y$`9=Bxj{(m0G}>{OSsZhi8x1h{2d2bvqB@53sTK!QNdbyGJmZz@hA9-?I=S z((JlH;QExexyl<;NW=7AYJD3X2*yD@`J~!fNb;j@uQ3$e4rL+55x_GaVju)sK7)vX zLi5G}o$fTN2*m)~V8F%p%>buE%`yXCckWa7`T2QRoVos+;hdNF!>qvVzTr!5q4{ad zNeJ>eJ_c{_0mZS5fDf1)w8cXnJW-|)@VCOv6~zrn6MaoovhmwJ1zK8I@l5mKMu&3a zcD={jS`0>{h{ZEYrXsOfELt~#P7{CIo1+G_iFON9qx z6giq&PgipnXPmsOf3y@AA3cyjMfzU-@RKAMF1?mT;p6JLsu3=w^eqB9t;8s_g>;)A z&s@OAubgog6iGBrarA!50wjhLM`nD_4FXcbnQXcqaoJMi1)=_F@3UF)qCh|9#U|?aMwsSQ}?=%{>eUdR!6W>2}4@Ra{!M^ zPD@???1&bp1Bpbgfw`iZn4%K3$$)137IMzW{*wbLm4@Zv!o>Q6pXaXg&=6t z{Bp|`p$vduys^^QP0B6b>C!t}shP=Ouu;1MxQ*C!u|FbJEF)4>fOY+ZAP_*d3+tCu zT%l>#((~SYoa1eUWo3nx=ELFkG5LaBk8pV;pX1h9s3Os&@WSZ(i7!0Hy~dC@)pT#h zf^YbzxUPTTXvmFgj2mGkG4^TbvIT67LxNEHa+ASjQ|_1-c=4a|dafNX=xB9oZfkvl zUwtdUJGyq6+r2|JA$E)6NKvq|lj!)cGAVnR?p`sltQyK#g!D?_i)~JaBu8E%OTl=s zX3<~Swe#wHFkKrp=wI#MxAFty;#ya4#H^)cO9quPmfTtd^M=BiSz0T8YT^>9mUOSI zpiCqtSS?pOGC~-ZS*R(imlA-e_WF>L)sbk-ZL`T9eR1~<58a%1Lrwm2t?Oeok+y1( zV;fo`W|Mrh>Ms+RlfbsbTZGtJ6_FgIG4|cI8ixtgSdmI0J;meIGJLgbOwWGA$Z>r27L<;S%lO!fF<9AIp=A;rZP|!7*EHW#y*GG87?X zq*-C0^tIhX{$BERYl=%*AcyR9gNw_6p5i3OB%gv80QOo@nZ+GuE6r}kuDB%V?xHTQ7 zmlLciqcU-W3RXbnZ)qAvjU<7~*TRDQX5k$W-||e40$qEs<9Z=yr@B8T1roCMixa{F zsPf3MeU62lc;pX6DDnFTev(x5Xd}86h^%)GmBH@PHHI;$7?<8N|ZYEPI9H0Vq!Izxb?ibI72cJri^z(om3UB*0^UEHE;>k?y zrNdGGRLF+TKk_w}ub;m<++7VOi%(S>JH9381kmOJY8RU)E7t?;OsqV ziqKpQ2DwxlmzGI-OZ3GNQP6O^B|W|sNh7dOv)y38wFY-(QaenI$52p@d3&UMp7#V1 zO;}gxxMwjHeD{KoOna-R;lz284ITUjp2r+UI$9%@e#^ix>QBw)J3CV;g{h-+s1JV8 zAdQ2Y_Z!DvMH;2a!g_~E%oZQJ+|v(xOnRToxE2}W%ef=wWH<$5|7Xnf`zVJ2jCR}o~ zE%4h^q%E!8LYQHppG--VKiEr;A_{FT4Z8AEFqoXLAj5AltB?_WHB3D`gF3uIO^9!@ z7>SP%iX@>{XW9&{F4s3i@nC)`m9B zZ{64t5a*j9-|z9(LLg)}5Ufx?V17XV#SK#*vtDPVGZ2<+X*hGj?jYNFHZU{)!}u_RYxkz? z#$@QzjJ1x!P$jCsAWtI3ZAnIQ=`EU;Cih75F{On`%$B}*(ZiUuH!NsoE?k=pR-Po- zkLuq@+m!ORo`v93n~tA;y(=KIK}$d(@4OEFOwfX~M`o&(W`_M*r)@5NWf3_pV{(_6@P_f3sKs6TH z_6LVcV=~ptfzUORQ`DkLk}P^olRYYw;GJ1G+KG`qZI_?o_phVGW8kni?teSG36?JG z%vqzBZ2dYrF00G$_<9Ts=*g_Sj%#nS?_Eux9%k}{7F!d8JVi0OL$`odNG~NQ=|DG- z$w;l0I9Djr@tnUs1~j9(oQ8CQu)c`TZw%hMGUa<4F3G)_#Y}BDt1bMM+;2c6^1oq@ z4l)EY6{!d!DH@WQ8j5ts9{kIyJbn%EKIGN9cL9dK$jE$t8B2Itztg)pUyb$HF(%nh zFDXHlM|j5(jHC56L%zd1sI?Uz?^w3oqea-O$1to(XzVf1%|k7S{jeYAS)B*>^%bO1 zvxnUl`88@D3J_7MW-TPVcid2ilw>vpsy?KO{CS%gyZg)gpqfY{kPPUel zeG0qu*u6D)^-g6wm*qH*V;r>ptv^(a4Eb{fLBG%{CY(msUCiygOhGQ+XcpOYndDvf; z^>|azA<9tO0tg!B5F%_wzqwrqC~F&6O|EaYdqNF>!*`mm)?3oZk+e#n*iefeJg0O^MFX)6cAFJ0=oBU>>@>}@O{a9>VUR~0N(@dJ~PN12dc%blBv(dVl(4%M34Yxz4MUYGt5k;!O_P>u&Zp%Le6PR!{d-B~FS{$j zfYU>Hq@&VVu~Kmv1t^hx-mp;1fht{5{}m3Rq{eU7#SUGx$r#qz9Q)$m=3|KM1%$7y zq775u0JMnlY0m2~si~C0QO4}0{x#%_M$8#y>YfqU0Fk%8sc%|Sp|8PE@oOlLwV*`t z!Uh-Us8GVK;IMNdl#)cJyLc5q%QAXWcUouUe{r0oYSa1p$&IOQn(TRJo5U`qvR!|F zPE(T~7AGg5>V8EzcwO_<6C9!dyd&Xv8W9)EkTf2BLy~N+6LWwr%khWSd13+T?0iH< zphCKr7P#t3VqV)=(r5dZ-ehY0Qnj&YeA5$iAE54fflerJX zcca_l^f4Uz1lYmS5t0ksOIV*Q)to%x+5X}NrjKUrE;B&2g&FZLeEKJawYx&@-Zq;Z zOFXbn0zGx;d)4v9Fh%)L?Z$btPPVv0_J%sgiG}yUY9z%2q5IH@0Y;IS2CHdMRu2RrOs6frutUjF1d!)H1U zY&9~7E-OUBpIM$ZrT5MheVj2`fNrSe#Bx;K6t}d@Kh5Xy+4wdnz)yM*0=5VzZ%kt4 z1=B*;akemgTF$$;B3W4#adYfsf|llGgkf+l`eo$E103r{049P-QD@|@w?PI z#5-EsPI^6)f$>+Gf#s6e$A7XT^2jOnLdLpbqtzq>6Go!F zOzdwHE!IhLUNDo8Cwvujwzh%6^E%kRUr2B^p`E{#WN6>2U>lQ>l8WzDgyE+>mti`9 zC{~m}Ni#zWnXNC>xSqwDV+LHHIUx$ym{HJ0DyY7SeKs;m=7UNg%u;1H?Ld|L_U^m9 z7?Uzm)L%1W$QybFHDQX-{b7$t_Q-(~o*-4kW%+_Gd4ijxkD zBU!fgF?K4JZ~0eLLH*B2iRv%5lDBik0tFj$pQ02ifOic|p$?9Mc#rndm0p>MRv8Nn zC)^oJ3(d4oyWT&(xLnJ%uRF0t0&SzRJU_rZ*>C2&VKrvt7P(gKqw?|zJy3&nQom;T zx>J2nvzrkuyl{&l7xuIL77cJ7d?6|&F2m#QxR!7U>W28OJt_X_0_P~H#v<=&9vIX^ z^#?*kn#!TWDw?m7?J8@Xr{=+&?wb9rEzDMq<7?4!n4{w;b|-#|bBWB(WAMrH_VPHa z;WtIxCUe4alU_-ZUNQ2~LS2)~$)b-dEMU0ft38F;^7W)V?@+-f6lZZ{fALj~g1NwU zO$Ki5EkNSL&QRZCqeG#_M7s{dS*^ZjsAt z6D~_vvQeKNkC3#Li5&;A?YKXi1$D<^YEI>j%(wAz18dvhBZ{HPFzNQM+l#ddo++D5 z20bt3HZ{JBz-}a#+8~pmDQ*fpBN=JS&x4@F=V_s3*C@*m1jApIN!r?bM+dks z!^66K|4Drkai$ZAFI%b4S&G8*t;oJ#h(yF0UTitF#Z)O4yfeIK3YU-D`UndV8pSKh zt;R0Y%q|jeS*b!u9fvVg5o(LSWo*s~7LlwsUSFDqg4w1?bK8=&Bp8Hi)LIUmbJn=U zM8;Z#P>xlf?$oN(s?!`Vo%f7_rMNBOGRssYBQe|ftc(Qm#y)e=4*jPV;C}{H=kBvKZmNPXKn(IB@)kWc5ZC08JNO=)J4zDkg1&IB zZ3}KrU4AN8X4@Oeg0o=C)~{l29UJV-kLO*fd&;`X=~%2f>pt|8_pvOPfGf!tJlrjw zKr-i>Cx!PJVjYHNl>VPA>H4(+MrR}rc*P=ca+1A(FdUaHhexNyR1^HB)1Uz!eM6-U4dx0Qff5AF4HaerV9}vX+8Q)wGd$YS;YrCQTGGK> zJ{`dI+sC<*dc9pR#QGipa7~!kwUkRqsr1($xvsN+?^I@pCZ!Qzn=v>m1z?j(IvuKO`~+-*ct*$%9ZL$Q007dt>UW~k>6P#Qke;8) z;E2d*C5q${0+fI_LO1=a{NQ5EwA7b<=!5}X@of+_95*HZIbJS@I(One-uyeLL>?Hb zkFiz>vGJBT@mL(dU&kyS@cCa-UB5@c{to`?l<5_QP>m_{`}8rd&UVKHD5#L@ zVX2Q2S=2iuc<18o5YXHBi6H63v@e;;P|#V&(a|hbrBcX~OwiGCqc*M-w3L-!IPmrT zyL^`K7@A;B4tt&eS|FRC{UCw?g0qvWtJ+tL3P;TFb(2*YuI@xXjHw`o2*)o-9nIH# zKT!R^co!E}6dpTfw!RC*w+yTcC$5sZw|}{A#z0??$go%p1fael%~E_@MHAiz@S2T1 zPMZM+&C%ya)|oAiKK$cYqQ5+2l=8}}vN|nK7<}NKK=fFpbph&BLMn;H5QWDuYJRh` z0AG+y4|+$mHJ^xR56mX&PVClaca1mCpC3?M;*X`iH(jS)x*lKOB)4wTUxTx65oq2R zbYTFz+b_X>g&^*8VN-y>WiRpmQnJLMW3y0%;=~WVL*yq}++mME;slv;qX6}~oI9C- zvTs7(!E^5bKu#7PV&Cq18Yc1s+x1_W6O;JwnARKM04ZX?*y`O-p9V9=9|Zh9a}z?Y z^qZ5nq$3RXht5v}U>s*Oy_?SZRd=5^(^9YVHO|WYkxuo|!&424tKgWyC^F| zG3NA%6GZXNmP+HV1|tm~=9=A_R!9VbSfX`WKNbJ5Ihs#`xcZ#UmhUD*Cp&TC`ttH?@}C)|?h|jWzdez0ds)ShutkpVhyv=0A!fPp{WFzWt`lKn?0{ zSb&42R^QIw)BBnd|NZ0Z%oY|9g{#A3Fu1r>osEieSfh$%aVEUD_-F*IYChg~L5kYI zdcFX3JyHV1V?Q<4^ZqVdd}H)-IV%x6oxbw=6ZC$Jz}xBOY5uJbda29=IyfsjYw1@B zW*4!t$FGik?eUV(G$I~248+zSkOyN9bUqubaviG(tl%~K*5l19=ZUZ#N7lzMr!GxT z)NgtV28B`-006_0+*qfTX_=Ro+tPbW@>co_WRCy)7%{mMU3fNqnmR44NLt~SZpQsupDy`WUKK(pazm{5L zs5n_3_T+(t-3AqI7(ArRg7P)oWB45Cqsqs}CnqO2{00TSOAwq_l9Q9u-!Etw7H!h` z7e8cqn^&#KQC<^kKv;$y|A#4O1-N@C&A%ylfAC#a9z<0rz*il?x&aBsjZ-`E4H^-u z{|Mea1M1&&eOISD46h=Pf3O;;N=R{fu}`SBIWG|7kAzqO9*g}mYL)dw3Sz!|;x_A8 ze?KL@K4&f2zj_a$30ogaE?lbGkm5Et+hr!Bt{Q!ypva}=W_`A{r$^uwi?j3d>kLPc zON=HJ#QQ0+J!DF~05PW<_)2Z=z8V z-sZBGO!K7G+5WBw_nS{J5`$=LoG?**`_7F}EY1*!>^lhXV^02o?+aKK13(J!L`v|@ z9Z(PEiZEMnbW&^s3|hE)zIhm~{kI086TIN*5||wt4|Xl;+jeuqOilfLqZI@Yn~uq0 zlU09zp}`9ZznrN}Av>?`44fa(8T4a} z5dYP$C$#H$IxuzkKKtQ|NJPl2wl-vaec2a~s;T|@4Z#(_nbneCTx?=%t7X23{4*8C zG334ru9kdv4yvh0D%}0^o!J$19PCFzVJirwFEHI{Wku#5`kdHcNc?$(6arFqJ91PG`e!4rHYGDl-T~(xiNZgR(v5 z`a3%owb1`KQuLN^S{S@PGIA>*u`vI84v}J6h|%}^n2KkwO)p`^LO64RBZ`-y{(|ws z7itZUyvFZOpFS!qlNwdalF!YMe=U@i-Z(zRtM$1;SDkN{70gbr}w{wV&uJDey zd;YYv*mzs>y6SL1xd0h>9<@LVy$*2lzdXW%Kj!)UnE7^&4Nay(ouSIJzJI>nGlyVe z@nsQ9%P>_!LH17*n*r-@zVm;U_}3$n!`9U&)%u;cN&MY97uZAx!^Ls13^B;dg2D|h z3_~~f$pq{Z^Mj9TvN0T&wYb;knZyTd9Lf*s+gy3v6vHenZu9=6djDFr71c?5G=`Gd zJd$xvBd)jSk79(WVD`^O+s9nW3rGniHpeo`-_|o8>6=5q4@P~QvOmy3c;1|f)d~+B z2c4fqnJ+^Hy(!G?>Q=(A1A!(KBR49LEc%Kc+6#5#oZ8ksv)(96Qbc0WK5>U2?@*U} zd`u`;8ELgWSi<1U?nm%3!N`uzP9?-~iOB(sNijhmo9qzQIGR&rH3nr$_z}l%H8mUu z>&ofa*j?ESx@!2;ONFP(vE8;Q{VyeCO*b+Mr3K}-F=0qQFG=>MTJDDp3Am-Bs3e^&z=*CI@k zzT92$Pz_=yh~o8p+)0u(-OS-*5Cwhiv)uvwg_Lx}2hP?`m7==pjt9q!3N|&b7e~~m zhT1qbE6t4Z>r(AXim_D$b=25u%pbw%e00!6k6K_pDJy9v6-e4+p&W}IO|)*_j#BrV z7PY4nx9ZZHO&%El3XnkuX++K}c~aAgDf=VxzN%bB-|Y}z>5l`}yS70%L;Ms#B(r*MUo1C2{|sDHfA9@6>26jM z8aIskRbzHmPA~BvVN-1F$(+!)%oSbFe|}?`yS2F>sOc#B+4R#Kphb7qs5H`}aO+SU zzN(2hzAl1h?P(g307_*imb>Xh@fB@Al(*O0trOBtL9912KN|irHM^hAj)<9TtE0f% zu``Ko6F1j({6ymBvYXI~ZCn?_e~zg0SJ0=z*(AVLc=8ydW{DBQh?^$j%d z2kX=TfkBCXJ%c|+{BRGe-gbfwR*X&@N6InLdmlgZO(t;WM}UbsR+7*JiGQ(Qom~LW z6z*ojMFelshRSWz-Ol$XGibc~$oF@t7c+C%AUSry!V~6XLC<+JhL@@6_5(&MHVAXs zT+M$ARw5rHraI!a)G(4L)!c4I>gu;xoZOr3Mnac#Sv>!@k|A@4iQr&vZYeAc{^Cs6 zw!`l~#S*WcY00Se9+vI*+mord7u8j-Cz@4{6QOK-Ec^FSpC4kYnawq=*b2tDKKX7A z_LB)Ipj+6$0%+_0`%zUEuJ`EKLe|RIENxbMwaLF)UE}|YwYQ3jvs>2%KOnffOA=gy zy9RfHySuwPB)Ge~ySoK~ySux)1nv6wT6=f*IjcusbkDmg1`J@%Syk_&ro@(FN$|3+mZb5<-cF6O|7(; zPiR1usMxWsf%;ez{ z7UJ2f%AmDbTYV2)DyqbB(MM^N51+SLUb zy|Tc!w31pQ|0xMxQ(A9FV(#eCnD~BE@rqCXZ~6wj^1zW+y5SA02Y+rH#HCIMf}`(E z=!~O(qrgc@w%;|lEk`EiT2|HOeyX2fVd*VMGk0)49B}IR_}8cuf&}W+5svJ}&X^;O z6LS0Oj1-5_Ie$LU*_5yOl<0Y(?>c|sL*;wr2@8ZyMgV>G9*9J5^-My zsZylajKm*2xjjo%nLC=B_0nbaS=z5!77)Wh2{R3hhA(e zvpLgkeE#gJWG3ER_Q;2;B8Y+mQ0^DRT2}spqr@~iKO}hDHWXS@ecHA7_Uoz6m-^v- z72S*O?0?96*-^S)yfoCy>Oo?z{+pANE{0>is71TE*nevtn0;1>%8!4l2WqO-^yFP$ z9gM2-`KDLClPNRYT?xBNVfn>hX4KX~JZgL8ar+FeeGI{TNNSu6_%<|}=rnKo*ZR0>|DOyvo+}8RGJ=af-*Ia9zqf46zQbaKxRB#G?e{VOq zk-v+=3Qsyq9h~H;<#C%)^6AaJj#MZ9qFk8C^j#Ttl(H2Y-CY;0Wh@HTMn(OO99=sk1vFv>jUPfKw-g zX~Y+jokc3j7Ke5!(tix);QG*9cnq4Q0zB?XH zSQ=mxwL({lI_-lMWVXG$+luYAq;_J0{fJ3TRW{o(ABC90nXDL=^;Scf%US+;{k2}P zHcQtYpxzA5_er(mr0AK{-)Vq#zh2yQAZWjP6-8y`Z9Py3p~sU}wd1$B4*JUOP~#z^ zmGSczSI+nUO!p74O)nX6Gjqvki2IxO1Eq=wU5eqS;h?OkpFNT##6$L2@cGVo>MDHq z7s;P>KMWXpO**_10dbBvc!+Mt@Y1*8f z(mm^y;uuK$k6l%4PU6>$1r7WUODUXSP3>wL+d-BlAxHilsb51Qk}ybsf~*QsqvXfe zef!U!ifBgi-8DO}M$!q0oB{o0IOeV7o0C>2c=TB&FLdBFFu@L$~^DIKYS`t+J`L}2x z^k1R{nn>n>pRNRXO$5KCB=f4nm0q*}0G)AIUGDcHNMK;7mRaOonDD){_5t%6UlGa# z)PyxqS_X$1nYJ<&BRABbCj<&_HlzR0P@D-bmF52SyzI^NI@`{lmtRcG-}T=6YaJw? zc|6xN!m$Jc%$Qsw)NvfQ$wm!(8hXG8L&o+(Ama^n%->zO(@fj*k$4nVp6J7LX7an5 zlEmuZeK&b*2MvD-b!W55U9QG_E}(5~cb+TIjbpY3p=&MXj#sYh9Freolhu_NJMcmO zfe8mn(9BI@M;qt{tN6U^js;?@N5Y7skBJuz@J8InpPjLa2xXXfaD*~jO<&o0v2 zbZAlKwf!{oHMvw5!y|3Tg}sJr7v6{C6PY;G@pKa3XNeJYU7Uh=*holDZ}zE;$GIq> zQ|u09nK|7;?`hHH+Q&oT_W4VzFD?WirU(dpU(hD${*z_$WIzkRq@fOoml2f5b9JWO z@F-X*GN{OjO!}f;3$5PI^5btsrl);#EZzm;?>*Q6^i-THGfkSb{=$Bqnro#-kkYO( zn=D^|&bay-{jXTvMgh}a0yF7?mpZKDkrY?=o-Kz&!jG6l(j_NoYHWrtJkU!~)co8F zCy4~<`eoz{%T;FjkdMWkQ-{^0 zps=cp3oHelq{wQ?n+@?oW9$o97kPQuek}FSRu1I^ABWv07P{hgm8>?48?JJ)-~}WL zD{ISsQ+v8UJP3YUTV)j>n`0H4<@f%M;8gc9#EU6@tFwwY;^GHO>%eYLJ93ldpbD4V z>GPieLbbqWD;}{Xoo1W)_p0suZmf>>4wtpNpGcmQvX73MZ6mnPHyQ&nhm9J%E~s~ zv}_+5l;x>bO`Wk0Gs4#1+KdHMU!Wc;p!O9AGjEU!%EC_O=U)npRHSBalz&!n*>q1kuhN!k2+Pna$9)JAOwh?qtO|F6o|W7 z+S-cI^)=?^NHF~)l9nBXu~M~Tn>y?(1&+dXcwwYO03yc)ia{pHG&E|W2g%r$n6+9$ z&5pGSZJeO+k_$sP!(xSu?*C@T6;om74IT9N7zpLf)hD&!*mKhg;h`RTl}K5Lm2tME zQWZtYiQ!dW)M$TzY3JA){I?chQK%pMI%a)cEwo_#E_>Iotu##I_3=b!O^>NWJ zw>udRA@gc<&0gWV;9|EfZ)V-oODZc%9mj+65+i?M`V?=daD;ppUCiLBnM!A7lwoad4~H?!YK|oJ={hLQ&Z)#68P^+J`p8A>n3iq{Fq#;$WniU*E&R{ z_El4>*K^%$ zoasp!zC%WFk}C6fa0jgvljwkeCOKQZjKy@DO|O!?zp_3J!(~ncS8+w)SCni7944^5 zlO^d{-Lw(8Y(7MgT|F#Ja6^m)h4@7WMFbKOl8l`Ei=r38?XSciIQz$+LFyE`7EpK``L73SOy z0}(ihWuFGs$;nMnQFmh%|3w3kP|M><#{z&s7O3r7D2aa3=Yt3U_kCma3Tb9hgaf>4 zmj)_ZZ3X<^%YBWfD?mP_$89fH`EAQL*cF-J%?&D;(CrS&PgRvBYIf7$#DvJ&kOE#& zH4&dQR%d2yv$&gOeD`u9Wzc;;YCz)^iRZr!hez(ZN_(wl)2a6~;-v_{pQvx~E*D<9 zN*=HBs3MSm-qKRjPL8(qEiK`BbX;5>x|g_Cr?1F6Q7-G`B`?I=r=Ta%lJS%(9DEfTCb zTO`8k>x0h&c5u9z1`Pl<06Yo%hqB8<{70^pt*p&Tm-{1KYvOIrzU5()V6~&rzZj@% z0}&Jz!Gp*{kW6on>N`9Kx{uFlM`%JAaSXUhz^YpYO^z*E@m^NF<n5r&Vk4F|5IV;7_IM!! zpIq4i4M4fbu%?ztltF(VbtQCrV6)I*rb`P@I%v9`rIp%f- z1^K9QO5iRZ8nAgTSs3ik>Qb$K0a+9GNeef$c*h<0+8j-F_et0JS^K{S%WHqcEodBB z%l>Oy03gP7ewxmu1a> z0VY6Ca2#^L-EWvNpoB{*$~D9*HX$}X;hiH#rk_z(GuwML$a!aDoxyT{pd62o9O~k& zUSw8ZN~pMDu^pSuaO`#25`B|dY6sl45KLhBd_Mlr!`AKlZ4pJ_de7KclbG)$77+8u z(b?eT>9DI4xKhK{<)%2}B^eoDHL6N}gP4eH=#t?r$8yqUmfzmG5^T6cM{&8yJHcmo zDBMidwF<9+kcrP#Xzy-TT1wXD2E{nN9$3s#`NVL#T!e1n0Ct4AY zjNLx%sOCP8N^5Aek&~+s?=Wqn3K+wPfS-X?#hDt-HCWup)uLiE75bU8-DXV5V*`H1 z$204nwv?Y1&V%b~po0Q4idl{q*I8PXpmN?;VSF!UDUdIS*3<49LdKv`5{x%x#65)D zE0Ii^PZU@HK%CfCr|WMih7z&99PIHWL1v*ItFBXx$@fTP3^1UB3mXsgRO)oEyA1m& zQc?yqW-1fix+g0XVA`!cu^)`boo1E+5`y2=rwir%LOvIDy~u0vW-uDK$+FG=Kd1a)~^VrR?nyjtBs-n$1yl4MvU}+(mNqAEemruk>u1 zi!;N+<`-)tZ#%v^358Gizh>gZ-nQJ|n}2=w@iKdO;dU}puh!X()Mfn|RG)*Z7nO2A z?t>rBm&xUBwZhU=gRzjR8ka85t@@4k846g^ z!V-t?2M|{tTrby3A(5+7(NkR?d-vh7ZTLs16&IRwEUS${@2@$HqcLF2i<2kP0?YGD z2~3Uk6WYU24B1D_zx%5aJNtlcNcQJrx=kdChE=kF2{^P3n_>qw` z>y3PeetXj%;rkV)!Cq=c7oGdyZ5fgD;e+{bII53oec_+y-yJb~Wn5He*CvEw{#`vm z*v|2{jXTG;uTdq#;XWUcy=J_x)g_hTpqgUTP2g?fh(gZh>i+HJ?Zs){Gzt-s4gqoe z1Qj$NC-B1HA5LmD_E=90*pR%q+1gHJmuDw(!Q)G3bm)J<7l?h_+bQ$A8q6L#&wX4O zb56lQJM(dSwyh^h@-B303yA9%tpo#{tVAL2tqLmLe2ot~I92(M-AXg>A4??a$b4In z;nj{HNrq*j)Txx_?uGLy%Y%Fv+uYt)ha7Fs);oB)9&Ai%YV;UiTi+P%{s3o#k@ckNnmHolG!#8J_@H4*giB2F zk-z&hJg%&`{^hK#v9g@J4C&Jje}JNZfP-LwFe$iR`2qsCU&=TXfK=m;*aO8@!!4I> zvy?A(A5F+yB6|wA=u$vQrt*v$9X*ABBN{S^5!qZ|C$yXwX=J#8Y+jFsj+G>L@3lbH zw;P3ucz~TnBz?22wi_8%jV_md=qfCeRkgtmm%sl}Smbjw3Xu!7qKhnV*dzDmfWA+! z({XisyV~Y#X)K-KJ?FaBYie!!>8Y)mbpT#rw~0>2v++30F}4q1{==dLQZn?)&_4k@ zh6ny3ex3dl9r%jpw-Jmv515V@XxsWMukv55`e>3u$7JgKlp7qMo|(NyFJ@RzLk(h)OF$L24la-Yq~ zq%Whmme};&9>XS7!))>YZ0ai}v`9?2_YrLsG4gdyDI!P1&S*GhJW&=pS?|rNBt_Kc z<6szN%pt%`w`50B`CFXO>NJa5y@bwgD(By$>{p_X5lAoV3m><+*Fo8~R0XE^sKT$` z=Ukzhs*}8EF$M0#PVkY@kW;%^bf)seGuz>m$HP0HQ-7oFLGTP?uR>jQsO=z~n87@g zGRHZJ>6W2HydkIeyv1Js<5zmtQDEjtQ52j$@Z?x{X>#3E zE61ox&8`BDB+)TbJ*ioQAb$C0pF-OCXjR_HC&9@eR>^GgL(}PFqZ#+o88)g~`z2HN zLv|Wc*81_iwrD-BhQTNuqlPC%3|@H&02t=r*{-kEkE-}?gLf_#>zb#YYEUN8T^G|B zXX36~L3NLpJWElcPQMl*$;bhl!`yOo!qrL+goO5Na9x56`?8xNJ_iKjI?u6MAb*(V zklDiskE8ybSEi{>84F)II9yqnSsLnapB%>ja>z-Y#WSJUxl~N+g>rFfMS%iy_B^qc zt|1CW2~T{U#at5@3F`CRVq?DX$wdRiw==KQAqcD?!7ZWLKX~QB=NJ1&Hi)v9mu-5U zHNV{FBQ#~jf(}A$ZEP;Nf31@;KMuZu>j6hY76y%y^We=L-GsGh<`d;Gf}=U})@18f zt+ln6hcjLeYwm>2^e-e7EC9z-`=jZ$>S{WaX+xqC_vLCtna8MV;Ou(stc#3Y;pS2-G!d2S zodVaWF{m!4Wdfa8g0dxEZRYi#?+;>8>>Es{yQOZ6r|xLi4LG0AYC*?u=v)$#J8lwc z7!0S?r76r!aV#*agLPU_u=B3h((L?UD;p~|J+8EU&R{;^alR#n6ZsTA7n9}=K4h6h z6g7I}6n4~6aIi#uv(#uz^W{PtsqreuWF0-q zZZ*Y|De{s^9qK~VBdXE-9=g5E3V6Hy`pZZhN$y9{2 z$L5<}7Hb}&mbz!g)mXoh`4VO?S}N|uznEgDY3p|031)qHoIAV5uYa*=kB#%-x2Rz1 zUHves;(5HYx~%yU9g<#J+T8bSRFRFPRSQo+HpyA9XY}KtCiBGy6jwcIU9#?y!2Ede zJ6(_r7}Ck)`&)WmaskERSI4rS^*cj|kr8{M!q(IJeeBJ)z{x z5!89dfB%#?dufYv|0>QWd%YUkmmfaBUa9ScxG|mdt+Et7|ENPLz09={_g8$D4xFOU zj8eNxI*davtvWpKRBfCm+n~cN?hXBdQljE9+MEVGrh%15$O}^{^raz&9x`%WPI3Wn zH4^e-c@^phdWdhtz#)Zhf^2N5n)%)Y0aGibd9-#{!fkP)9r3Be|$rCDCFYe!s8GnVWm=Y9g7r7M?jz*j92uq*EXo@t(xM96RqdLE?K_1U_IQ4^>mJul!&#F( za7a4tqJF{8Pg{x8=m$VrroeKmG1banId|#QwSuDZe~HGGzGDTx4iU|Sy>VJPb_jqe z6VYvBWGiN6>G>(ZLN4Rjh@T`!<5Fe)ic=q>RGFJ)RND58nEC3gTx_T}7w`#)VKVDq zyo2&SCRG=r7%03)^JbTtH%LH(Da%#SwpHFr@K+I;7T*81+_;F5AN8qme$g>#gR!fa`V}4Mqydr&dk5F*Z=jJay z7Aanegw(9XDW4jO|E=hfze!ZHo?e>fgh8m=q3Z`dUm|PWiAn$Y^yFku zmv}aw4zG?ir7N7hH*?aV#QXjX&=Kr+GDIT6#lu~>1Lm5F zfm!h$VRFPC8TR&%y92vHnx(B7zgg>QUZoA0A=o^eK{PvkzXmk992Ry6>HfCzU*h?N zD>UUEGSu@FKHk$2%oR7$xXGbm{XL^y`pf$Fto(Y=lrfmRS5}BZW-gnR<4%nZ!DehF zfQpBa9XTw?5GR3qMFkaFn0a6&*T1Kb0|*G7aR?_U2*8C7bt1gP)Uv z@%E!6`JU9L#hr18DLHp9OK-9uXYrO+|I8pSE+|25d9Tt@8eG8`Zor5`tiyaG*}aw} z50y#wAowuwRTwoil*lgWlV!(j4#wS%`Aq?RW|d<8t%CKfk`kThyoJacs9g#|8@xoW zlYy?VPbQSLTurpEKt>(3y?6g?Z{ROYb92{hQ=MP^KroO5`~)ZPi(`Xqw#~k|N2KXy zN{i}DiBGt;^0(+Vj_P}D2f|IWOl%ZXU-er!uf1vKo~^wtWM}%O*tm4# z9rx9>oFZR6^tdt)$DE~gSzQ*ImIkZ&y@otIt}mC-*^C;q_ZM}-yUJM|@RrG44^JTE z;KU`99F5QIOsl6b#wYsDe+9D>NqqYvF&(Ha6(H9+yR`-QF)n`^b+|jh6|D_d(V0+P zP{o<;wQdo)NkYWF;?t?5HkB-pYYzI;86QJdDzRO**1?7@&SHxqgI|XLM_M!8Zcn}gd3rGKp+ii9--smNPV3J4`Q#)#IEaQ z)oLfjrgg%>G1Ku%et)0z{NPc-bx$0tO@z#a$AXH-w^N+J04Xg5s$2cS*~(PDD340+ z8}JKi69i?f!mUCKhL!^5w1X`^uVSz?_r7o;o`ggQ;x(l@>teOj5*G^mH!Rr|Nw)rU=%tBz$`tqL6y(W80mf56&l_nSx=o?2ZbI z)Da^#ssAN%duE9`nKvBY1)8IeI_?rh=absH1{9nva%6r~dwd@9`8rgG)^Ep7R!>Cq z-qa@gitlA8y}Mh_%Em^J0r)_dbg-R+9SChqv0rW!l1zT_IeF<_qd6;ZIZCVPK~2Be z_d8Z|xrgE7^Ynv67m!^|PC;62s^_p<-jk()6YuS@(c7AUMBkb;!K2P8={vjd`4Bf|`vf{gsctU77cZ3K(mb-GGEKu(oJmml*`YYPoCgqU)wGBV zz{E+>*s`scmn9P7Q&ZyhQSLn${7+hx9U)3AS5NK_&>jn6o=aAyj_&lmktV|Z2GBj52B(dzuwEVK35`zkl zX<5&k%(4=))2W_3IRQm_);}q3m|a(SojgZ8TB_P#eeUd}sOjjaM}i+LYLmjMa;6fn zG3Vdb9eN%1mJ4vfr~AUq$$3}VN%_`biMkoi6k^Z79eqU|9w_);kd=BrtGpRN<%Xsnk$>|)7Q1P$eW-A5a^<6JKMj5YN zo#*#D%#pgA_NU%zj|3~GSG$udxZp*V-NpqN8?I-klcO?KG4$+Pb3WzC*|WBDbG)`f z(1j)^K=Kn-V5}tr)4ToKWMh*b5D4O-G=mU^$HqQ|e)2;^gT4A|<9Nt4K0eQk0&^FO zjrzenGjkJ*?vrL0{+k3Q05;p=^$Risrzpz3&zFi)ZHM!t%~qkeHSRI>IjR-rE^+6z zp@p55$ffq@z|~}8`X7>YXwjd38SOFCJ=5RWXI>wUE#*qx9y3!}axxkOrnmCE9j&B9 z4D=nQs%`=i>kc>X>xNFdf)o=T1IHhSP3De5%tRibI@lX^P}1Yqk(o!^jV>NshxX}` zzcT7W#2^^=+AzmMR|?bqdbw>p9a+70XT4YsVAx%%-TG|9naTjMuc4UU9h zO)BV*4D?J(?V0zP6w!a4gBjgen>gZIuxv<7^??uqrr2f%K4-I`7x|Cc9sst@bjVLM znfIE|AKT>0QpOr>j}{2Pgim|MC^D-{LydvYU)oPQAL!}>SrSCtqitiG`{tq~t{_PB z4{}d&99pqR_=Jrb)i4zRbQPW#3=PSV?R%aj#O&Rb7^X@{BxQ2rcXRquJ(v!D5P8`;kWAO`Y)CVzpcjj1W=L}@CzL~y?%zHCSmP>FRGIUp`o zsQd_(V}!;zbyg!GAz8RTXs=k!8oaP9VN0wAiVPS4A*#G(-9C9)u%P%E5-bUeDpE}4d>3IAIQK+d|+8`sI_O6K`&cN@xweiOa<+*5b9((bvv zU=FLx_wQWSJg#hUhpiP9fFzb4v>&br&A`^)Fk)AiZ?`Y#0FBk==KjjfRX47H1V|<$ zaOwpdBPU^s!T7WVs79n=U^USmMBnEjM|CeYJZe;XV5&t{&un!R=V}8Tt|Q00#aQ4T zT(sL-?=xRmL=t(QJm*N45R9Ot{4a(B<%9ZswYmCr z5|q#UU)T*-&VOgvf|&k>m1bMest#+p*ys%R3J`cz@H$2WRXH=1Hbnd;Y0=7ziMbw9 z|M!o&&s8p0v0}pqCPIa)ynM!S&JM%c|A#%O!+f@N1KjVi{pF={8aw5(C9uC)@q0g4 zGn)!l>+l}!V}@ja&Wl^6uZzj&&@(|2ln`R-DU10s~C zI9jMo_W)IMnpmd|fT}s^QI1wY)trbX#uD!m5F;FH!ri(YLf3>cAW0_ltzeulhbqlR zjke|ulJC=_l)`P#q2T`=v!9nVyA^)@vYk|IyOc;Hk6nE^WcJQrE2=Uvbr)=0YYOTz z$GhXc!QM&!p5bDrc>--!#2;%T<}eHz=eY$ zfzg7&Kymo_1J&4pP&9ikAgeI2 z+iK0RDaQUkpRC=_M?W)G77=~-)t2ncijBWJ*^do8g(Ri0 z32`dxEs5pK@0Ze24ztEHd|?4MVfz-g@pdO47$%tWZfe|t)u14SVV*4 zH%($%8!}TEOptjin!^$5WCiA~lJJT7Yp5XE5Hn{LQb$fd*95N<;n|(mD+iS^;MW;F zOR%W8DVeir2>z%7CX-Im(U;09u&9z_d*1W7DTG1uJ!|58HV*r%6016y_Qv}P3lNvdoi+-Oz;r^x9{D!uGCbL3@Vy76f*~_b`sCW~C!fz9dg(hkRVl0?v zJKgnCp^W_t?<@A>e4nqtXi8E3;~7HM+HaoA=Z(tXNfTU-s4UOdBQ;AzrQ;B;9AQ<} z$_@i2dW-aIOHC=mueS%h6t*E5hb3+|Egh3Tu=rpeotw~MKDl_CUdYhp0|~#PT_$mt zev6XSxY(tu^L+@wUH+WJ+cKj;QR8m;d+DxGu`xCJKk$5>hcu-8P6WFDSI?#oyx*&$6U;K zsZ>f%a`ulkGgc$Gu45}Mk~|rgE1{MgEctYdPVsT^@zYb%;8(a_tX5c8UpEe>+WP?z z>hG?U*eY(TD;w9|X_HGRdS^AdYI|IN)4EID4^|5>r-+lKKhqxrZ*5Vk z=y8YAg0dbd+YNDLl;UY`z<~M0M!5Fo$}6w!ec%1#^xQ6{J6e;wsyH(-$2c0+cb%HV zlAh5233)(6LP7$Fxnniux6&mtSVHbV3*g+|-O5;%D#{HlyiWA-1LPvOl< zUvJ(Mm3)7rtYs1${2&edL;@dr%5xq2Gk`mkY~0o{Nmp-3$5J{wKbO6I%f8HRQQ&N4 z^(6c6XGc~OPscExFqoSvSVzQF;g{>9R88j!dY?TmsQIscB{@^C)?$F!mOal`Dq~mr z=8&g|44QUrHyk0~tI$(iPHdqzOYrjJ3{44KPHr*Q9W|sZLT8H4UqSfD?VsF4Z@Oi* zzfKE*Is5HMJf8Ubs|^NTzo(qxLWhSW+UjQftBV3wO~xuFq*3~1>^~fj!@5ln?^ZJ* zB2)}t+s$gNib)ShjWZlAd;BIpOjDIHk4wf~wH*IyS^U@g$w2rKZ+IIEDZntEwDG;x z9P~R}FWUS8!Arad)_s-cb>vWZMQ_Q!w0u(k(d)$e>q54(eHYLldf=**~! z|HnejQ|wIl*rdXvf#m-KK4>w$SqQ`@UiYn=>xY)0H-0 zC;l5Gw^r7zNc)^e4lmyZx3Nz9MM2TVIzQ4Xx3Pg+Tf~F_nf_j#(<l`HF7Ola_cFX3;Eg_mol~pEv<{Vvou;q0r-%Zfz$-RkG-jr}_wg6@n9p5#kjm zgfe4`PiIbi<1r56FiWAVV5x0}bjFr1ujM#CE@WUOwfJ#ZXM39v6346Pm+-GL_dbCu%?_Sr2sRjJUp_xJ!ac$q;E+A*`!Q$g9mHIGMQFOr@ zcjJlm?^Z*G7D5OCP`?IxCClgq7BFo87*Tw-$~H#&u@a=N%GiAD2$HlZcQ~(uz7MlJ zNC1yjX2Ok!)AsvO0#<)=pUK&nk;V$Qxv&i&)F)jQjmWp+B5X-JB4%*T(g~uNgu?Krm6%W?@r5T%T{uj=ROU@F^S>rD>R7sSU6~U?;sF z9gN196da~C!H6f_O6bA@$=)CspbCe30ZKqmPhdVj1l+?A3`F69zS1L);95LDY!tSl z09_p@RiK5Q^JOo`3{kp@OwREBqBDs37_q|EPN@1*LBybZ8h$j&GS2*5(i}BZjxI!$ z_wP<+X9F|Tzj6dN=H|y~DQey!qd&y}YwAG;71$GYiE`|Rv`+Tl~W0=I$`x+@tzdNzg|tP$S1d>kks#T6#2ZNsr0YW<@*wNH%#{zv zI_^}_a9@?LQx;CjQKTcoRERY|8tKcTnz2q**`%Zy2)5^9=^(eyA&hr~>^qsPE@(PcY^w!o1L zuKGMzRd3cg1F$FvGY$8r=q0|Ru|$GP6D-~57-%yts0*E1cd0q3%NTbD9Y@!0%u!Lz z`4>S<{*vlG%sea9B1;vBCaVaU#_+Z862`KPOADL zfe9G2{+-tll>FZ~Pf_+y2m(4?;zTb>l3;qqoDpc6*GcGwDJj=mAw`ptvu``XB8xAD12}AMCB)$ z_w0^|fn8W2Y}`EUllGehAc0aq3hduFWW_VdQC}kFcjWz^3OCVceGUkq?)~9%wZB{y zyIM!P<9W>ehsHf^;nj4h7$B1|AZ@=@khMYNqPtMC__Nl<%FTD%AWF-*pNi&75&e)U zx~0MH`i;W`J7QoJ@!myJT6@|1i*ur_Bop6mS8 zqn7wm7rpJ?BavbV4!5*|39iiirgIUbp-pdmIVx;U$tfRfSoVyPvGLczf05ABY;~EI z|*C& zqRRdRB#mdcyNFIOMkA}mUX}7mRSZ2+_+)!(Sw3F}PZDeK*0WE|AEe19zZ zl4~to9yafE^<#Ih&weqgpT;AqVX3{FydcCQRpW$h}B+7GnF=L`CE8=^me4P^@1~J_6mJH*EF-u|o z{LhdNt3UU5ptLZ#>?*x_)tcqstbDb~@u0`$pjNLbdko|)i?Qo%B1uvm6DEbr+NN*Y z1uO|~CbGDk603W4s>CF4;xYRSi%5s=zt9@cSIE6dD>v&GiTkA_+^`3ipDR2d^rzGf z=#6Z71TkV$j=)SMY6iFwB?GG&pkDXf^Td0_Is2C%;ral$coqt|DW1eouyiK292mr~ z$s&LfEASeWFHwy#_AF7U>!?CZ4ktk=VO+Tp#(p2e`ksaw6;cFK5?w1Tb!?|-FixE` z&0s!DecDf@0U>jGE^yCC*y&#(iR{&SU8tHz`CFXaxThG0G|45co)eCN!tIw?aYSreWd*_np9+fjOlBuQ`Y0_ zu}g)9Oq0!i`%<;@@OVikO)`-;1Yq?Io_G08qQ{AAGW9g2%i^rUx;9$Z_bKN7ZfCQe z!fMEZf3&(2>+Q$JkhxL%T4p&i4;2ZO`b1)5UuLJEA`3TiGd9tN8)<6@sR*(Q9W39y zY+xV+FkpmEJ33u%iqrp1!<{qRPF1R_SFP&xBRrjKrjHs4r5Hzp+ZL7Zf^}Hjf}ZL=;J4YL zP0Ic0c^M)(Mx3Ge$;^VrT)KPn>?8l^Y=lKkXvsp?+JG7Fy7aBN?Nfy;tef`i!}clX z1+ltqLIKtit)>hu3CQr-Zrz|weAHUId2!&Kfedh~l%p=j--GchVjSpZWoF$*!glv$t=q&XmDvT}nDoa? zwfC>X_o6yUad9qogc?~RfMUV4*%^Dmejgm=X{X1F4PD;PF@a=$f~R$g+dau>R9Qwt z>(mhkheap+3b$Cw6>L)LF^K<2$Xm?v5hhuFiU00=wYqxbb^&pSuGH{~z!n=M5~B9% zgkyQf`9hygL!D+KtM^hn08;x)PP<$a9{0LRZV|qz*phiNITvwUTP(#+pg3flrxft)gA?E0G^{a!k<8LbRz6;_8@h( zy0*Gx>1Zd(p_BfhcCfU{l|R;fF7f5H4@FYTb~2!RqSHt;Zh~K{eFL=taqD|2iGps& zr~gx9|tUfCnGe5KYYUlRB;NXQG#?e4jH#K+Y)w9_Tr0ozZ4f<#XCwsx=3LZqwz;{MJG797YA@&Qc5i*6~W7!t{s6j*Kh(%d9ApSI+m)!^iAjMSHga`#*Q#l`L2!r zphO4DcKoq+gr*0bQKDay){M{KI#h`Krfa+esbtLB%thBGSiV>{qn*pdD|@Xg2SdI@ z%WW6__|bO@20Fx9PBU5&@a;pV+1fNsA*^% z?Cq7})k{f3fO@miJ8G1b@rl}0N40C*enJLiQN*O3l`&H#{#JarI@TX`Uw7|NJ0VSA~ z9Hq!rDvI)n&T2?pzH435`2V;wPOH4^6azzZMLA~~r``KcWlA<%N>gt2ROG~RFWLFM0AZ1*(Q2-J%rvcJAa8nrB zviJnv&ItvVTkT$MjC;;`^0(=^KmQW2o%p~*LBV5Yhc(PUj_~^%1Fdj?Knn*{g8k4x8$y(Z57q_$=5s<%oaV1_W8%--a6atU&56}9>rq}esM0hT{2L+_` zp72F=fXu+(TCI;;lBAOm5a4h9|8{Zzaty+5FHP`A99QaEk?z5u5 znp&JX$`})em&Q%hp_i3S|Gzm{fpinjCRs`Ch%1kSXBx54dw(~=5da{J&2lNJy$`=r zv=Q~ewJs`;Uq`p^uVKpFbmN0ZcrpF2VTxiMXkld=NFG{)UX|iWeC3e)9`$? zSRc>XyGF#kKt2C8J^f@}TQ6ZVhnl1HT3QZ(#%_Mt@e`x`8vlf$ex(@t#jIrD?-FAth$@iNy-{(6D& zsD+1{9)t9)V?8kti$an&)J7W(5`@pmwd8-vP+c24N&!cTaf)?^Z z-fh~V@R&m4Y9gB$7vnmtz3d)$=Gmv`J-by{2X9W26ie_+t zIRV+o#V>EEu|hykP4%N)+OGSF2F#s@)b{50?Cez`^o=`IE#8yqJs978QoEwI6E(QI zinVL6M*7?SC!LP_U|gdZfZX6)pFr2#S3!6HtY>iB=CC5X8QIW+A4dJpp_brE|KFM6 z_~Zr9eB8>f%`tY)UwzgIHPeRFIf6a%koHn>njSa>!ku33=dG61UHUmydY#YxWLh6+ zoPxn?ZiZW#B|{_o9DD0(<`Fo5Bz;vV4^sNNcf9U>?grB7`OR-L<~Q0V0oI8-s-rbI zmC!GoXtmzcSp^+}>C^4Xj#C(w4XXx{;u1~ih)(|OEo;2G`ZrEO(6%FK&Z!txboH@) zc~u0WzvaDe1oUoN+?Mzd6D#TROxgtLwjDebgf}M>VJ7*g1$Mz&J zicMVXrPhgnKATOP%o7LApRUevbIm_il#R{%Q=7Jq?lKuhTAWar>d2fYc~AX^8soZ< zzXNYj0)KK8J3 zvR7K|bIu6!or=5^nLz0a{Wg}hXV$I7cFuYbP5y-fl^HIO_OcYybGqsB=rB_S&E0=T zxnQ11>vy%%`Qy36|HIL0T+fu*{>!qDt(vUYAX}+Iun3`lo{6F5->qB+y^WLG$8>m> zH>f#n%M?ulDAhUYkPp^^?x&O4x3t?3Z`d<<5s2t=*2UzyMy8GPw~nr@W>Ih;i)q2G z>);T^6J}EkZ0gI$I&JXB*DI~@@XWMS)c<&|D==Pak))$X1^(HPL)~lAy4Xk;KBmaMF9E2IZlg>rp6x4)Rk>Uxy=||g%i8CmZ8dluBX_q#3=p$Y ziyMN&T@#~8V5y=RQ)EzF-?0FOgm%BldE?}4$!qeTttM|I=cmsH@Z2SuaC3RI^Iv4F zVUce+yUuI{dlJP~(xJ~uFY{wD;H)zfLJ(~cjdh(&7v;t|6qr}QIjtz0JJlvDDOqrm zL1Kt@qJ+&n74(kijofCMXY0~3v8}7ls$l=O1x%-5EniwL{RWmHH)Vp6TYgub0l>ea z7|CAZDP=&kpO5z`(-?dPJVgwWt6d?LybY$%gY3?{Q3@ ztnTXF(CE#Oy%g){0X>VLzuYko&l*?xF!7GD`N?@TFV<-h1m9OMz_8BH;r(jD)85BQ zpz)F_T}Mn&c5Ez_SBK3@hN63&ERRe_n}zOigS*sZ>{deb|Lf>77GN&#AbzNFWKv00 z=j*vyJ%h;J<1j&wfFH$cblAGYeTFB9&`h${*j-7vuV5K9+ix>FFD9b~0QEilfm9Ia z8)+2U1&OStRz1p*g3bRT=-|W)Q&smGBzba|}=tWM_Agpd1Z|1)wY zRXxWri!zqNqo9VbO|Z+zZr9kB95VCWz9ZxT{)1Zz0s#1xK`*%|i0D|pmpM_h?l{;c zmC*756mfQZ#!P zX;)xCg^(g%h_9z7V_$Jtp1^qK^%Z1tN9B1O>iK7nSL9M;!Ec1I3d{>28`nLpcxV30 z%QbRg)c|=<4pAW(ByD2vK+1?uHkI%THW@6H^qCdNd_hZ?-=%53x#2Ltc6Lru&s;3c zeCguvsMK_p{$GqAvi7}FD|o~ADkOusAM42@+^+a}01wv~hz^o-zAD0gQ@ZG?YseiR zs8wh@zvA-)`jaku>D<$Cwe+@(r#$ifpAQB*og@=E!Q`ArD1b%0x}giqX1?oP{wk0% zm4UX&hdb~I5ad5z2-kE@l|hrIQ}|u3PgkkLWkeW;==L;i9x^)HeEi+$WVo&W%j!i4 zn6d0F@1ePao_4sl?9^9&33jPXW2EI7=X>bYpVhHW!of*{&)~9BUGL$HP?}9ubm0x# zX;;wPk}7hhIwgIw54$0W>>vUR3`v4$hmPO^2Rg?UCIEoBRO?_fb}3(z@U#!|NePAU zmP^JgFCxzWLnDt{PWRuO6Q;-@86cZAUPN5dRMDG5Aoj|jWbe5#1Q_^u{`@=M&3s^8 zT#@}ScZiLwQ&>5X+$e1UO-1g3p8Bpv_oa1`yS#J@RfjQWvCediNb=^?0+@?!q0k~O z;zdN8ic|g)ntX=>#;?YTTqkVtAx862@OADHvq1Eu^nwaLLOljRfB_1$WhI>~7C?hi z0ChqP!_jAi70X#nEJHe7gY;TP&&SQPxrFm99l|pQirKz5ho<~!#!KTUC`Ja&ge5xO zpNiaYq;)BTHbmzU7FrSp%Y#hnQn4#0W}2R((_E3?9+TPn{XhX3I}YE=fi|*G`3BMa z7j;yCE_^p;0vQ}wHo(^p1mdSwS{b$&dBe{f@Lxa|lq$P=-+AJHCa$oYNQ$MO|C4d- z`!9vkn2Q^+4_F%l?_ajzW(TZJ`?()h-{|c$uPOH@JDxHb5d~RR7 za;bo0L#PSf>~<1XJd`vTsX@Tfv!3x*W?7#R_8O#sbwwS%41IDmv%1uRojvuSv7dW5uur+7ZDJ6<&a zz?X~qrYerMOVL%K=+(Lml55 zeMrS03?Qgk_sj+W!2Z%PC*NL`N;fHW>z`B1J!P^jWaPycZt`8pP~EZ!m3&!204YZ- zKPKa^9Z|tn4FpWos%pieRlLbt*O)4U#Nw0`w!JC1%swVxkrz$2Q&?}-Y}vwp|- zIK;&mxQ4iDYI4#1vmI4P^eY!nI3g^Uf@DTlC|0)z^dS>TK#9a0mDD+MSq?rI^V@@N z4`dR-_XiPFIi&>O?1VO8>J*j7>+3EB)ESKaO?c?-`_nVYSqCsts$*rvjY$JM%qyzl zJ4@~C??orV5 z3Wn(IZ76cLx;X}oGI5HI0U~LTdQHt_uHiDxJqrfML0vkN*lTNipz)7 z%L4jKn5O!vS(RU3bl1f?vc3a@<#R!9*9j}M^h%!&8G?4q%4Ko@jRCGJCVUa;&oj_o0q+xUN9s+08*>s|>)Ik%Tt(zfZi!{G=-rVIQ z_@{E#4PVqow)@LHl76JcFO_pHi5a@4RE~KY47d%c5Ps$dHyYdst$lyjvlzf;kKw_b zrN{xUfAPS2#KCBlW%qjIk$SJ1Pk@?)ew@ep0P>k8t1#Es`YSm{%I)&iDb)Dvag&N` zlqXR}=@;bRIu46?17qUD(mNtNy#QZIB@IyTH|n|sUBis+3$Li-j0e;TybZ69BxX*^ zbP(9rWFVfEs_@k?6|yY%%DHot6;f%ar?kn#^^rP3ODabizkxB-WkqhLX@QG1mI4fN zcS2G`@7ZIV;#+M?lDCDsBHuiocD3bM=g#+6j4Zs)YjjLzC<21x3))Z`sO9JNRd z)r!0rKdeUl&#?;&#IM$TGGgnz`3T`l7UD5wYEvQMqVe(GM|#jWhcVxLtnnA&r6G|n z5smj4P3D?JFus_Wk-xjbMbBu#I#;e{k&#gveeAP%XMV&+@7K0{Gd|BJvyAxJrC0ym z`JwQY5f)$z7PK=lmeH$@^~Ly%(+Y`>l0yn6|Rsn4964t@ZX4*CLvihCFmVUs^so0hkZl*cea*>}jMS z?Z+Z#z4WRaOv^7yQjORU6x7P9z~OVS*y&5Q+1z`x`$%4iU)*=h!dm|=dn&pb#t47k zfMH;l0@-Mw3eg~@*}U_pkKkMQHjvoAH(uy75HTK}#bH;b-+PeQr$M!Oa0tZJ5{6fB z-`WuriS(^eOOyjk)`%*xUnA^yX3-93#hjU~HYWwbwWz|UOqEzJS#$WP%#@WRI)5c} ziG{4+x9(oim1`^}?4ScX#GK^YTVG<3?oUmXa1C&~?CwjvOekMqh@{)rX{Hy^cj86c zO769>Fn(me9E+`>!Yw9Yf!fHTWYq*6stGA&tfxY`N(%2exAgEDU_i}ff|NhNNgO&C zOexz^>7UAQd=|^oK{1gd=Dli-xlF3Ul!r{rz&e$8uk<+X;axfA(gVh&S}3F_qfi}R zrPyy>CIU9G+X;Ds8X!(i4OhOd8K71VHMjYvj{U4Oc2qlS$_Mea!w|;6k=j_8u-jgx z;;QMctvb;{+^%KO1{lV?)TI+)1MBre>}8f-*nzG`(QtsH{_GQ z#hzt&i+P2L(fb;_2lLLFnew?%X)zRbHe{7)fW%=td<-hHgx@4wA%TzMCdt}Z zhIWd4us^E&4e{JK0#|yJ>Xqc z85-DKXV+Mg{k<~Sre)5Bpe2jVh4EIrP^E zS0`#aZ&Bxofra-XVuqwLqNefX_aH#GG;ZfjbQqlD0mzjnaT7*hV6>B(!XUjJcDwG- zdL_53xQAbf$)G2OpWf2Mj4)iUC?}mlS{wYM{Z0X>t}$MCFSm*4lcUC9!};e%z@{-d z@D`Yy*4xGRTBopga(pUA82H{DVa4|t8Ir|Q%py6ZMfCDL*UU!dzF^XN=Wo^}?ax1R zEIU94qY=|iC%e0yd=c5i30kTLan&$%2+kH=?McdQ>$hM}K?C%lDFplIBn8kRLcO&g zoW~;;u9FC2voN|TuW>w*szL9|O}iRsO$&!8L~O4bOrL^fCyk7JdCS9<2wlcjp`MDn zY+ll?E|jbBf>#&Lgh*NLgZJ8)^-7b=`%kcH^QK;=%4HM)U>#i&k8Tl9Da$Q?BXK4cl>$+T?(aguax)WaqRhu8Ii$sC7AkawvnPd>AUhRWBn%ne4{mydZBY1^)IBLnCD$JLYT z^WXM>q>cOlFLXFavPe#r^OviT0}W7#hwo%=8EMr`TbsT>y>6wLBMQ-rNt0kEd|8?8 zG5m_`E4MbjtVGK-!T#HClLBlcZ>Q}Owl#OB)ew8P}(&8<4Ts{Ilo8{6p0^z)vpY3l4_G zSRd&_RRnPm6}sPj^nhYlJKnODn5x$_vB|0CpBH#{kwv0oJ8)d1*eoMO@?!b#jLlPt zyzi}T-Y7U-+cPdY6)jPdH2;F`KbOR?l)460ZDJ&Y)xf`iND} z%`x^%6}*i+SHqbKje-rRM{7SYI@CupjIz(QMXDcR5Lp3(* zv}WtXSodh!bmo_ffvHZ`OOvE^c~yVT*Rb{YM~!s#Np%51dH`3rC2^(>TzCdln}3~( z569(e5Ipeb*$POxj`#Stx7TJ545mySskVl8o55DIbe-u=N3GaSk3kW}tMw5Lv*0DK zByN)rCzt}tRxicgF|OF}i*mKEqM5Y`(N%lJ*lJE*fkwTRby59Wtv=MG*Bam5-$y(V zhVcgaW9#Cl*4x{&$?<=)2&sfhT+7k!gnZrVyX`A^kZUc1OZaqWxlO;l>LVX|Dr1m%V(g;?kK(JGg zfEQP;HSk{npw|MZ+MOAV?oL8FP0f^w>Z$2V(tS^FPt(&(r;8(`ac^g5&DjLpQYc7lc2A z6u=Ky5093O44?Z5z(602!ivwzlnEK&M+(rC@x=j9&)fq|4u-nkVt;^xm>BWH3+TDt z1OMm4+tu1x4ADSPaz5s#^^iTi%LB>`miTC29 zolV%Sn#a}v2}}!m0vBilLT6hHPX|VlBVCPRD2{*0pPf(Nh=G&sKk|v@$OOqKU&S>| zk3PEj3823q3DT+#vmo0`OHtGCfEGQ82+i-QGcMMDF(#6~=bNh%`GzMk1Wael_8!1m zmbZPH@W9L)J$SgJ2%#(Yy27f`5fu;|;oBpCa5$IiDZCR&U7bIr-EGn{2Ghs9DQrmS z#j|F0MoSIF0T0$@t1+veFcSpl%karp0GDV&3VFnGlKlFH(>m2fY;)qYf4!PR%vwNo z@|io{*8ImKEAtCctk~Z2ZTcnA$moNAY<&rQ0R+%7V**66DBu8FzrKCjX2GaXxg%su(>Wj8djlwUwK)ILVO9D!x%U7i(8z&845;3?HQ#HanGY4z9Rp^oRg{&;T#)^zd? z_X@}2eVvthACh06vDW^xz)2>@=PEH-?3nkZVmvIB(<`g}bM{AQGA${dI%f)t3GRq2@Fle}m=0~P#@d6%>Hi|;qa<$eJ}<4dB98h^@)rY99uUqel3 zdZ*uG->Y!MQ~q2ODo!#oJC-V~tozaaG7692@knoWWW0wxFizMB?qUj~)62*t%CJQu zKP{ko)Q{li`uiW>O>7ydj=l-!HG%@Fm&AimUQ$*<$K7RjHjoq%n1#SfP1->=AoS(} zzYxvoabYFqCH-pg4zQELOKO>PU6z%C2tq5o0fkCi4Ie*ig8^%-h85?55D&7Rn|GWr zBly*NgzRcUH7-O{XMHc2S6Ydhy@&2{K!73|o7xd>yJ&5kS}5-Y@B#C1bUNWPkTr%7UgK1D zzT;1h9Yc?NJUc&_9T}iE#Yu=;xvxnE& zEsv(Ey5Jue%Oq6y?&3KA6rfPJ)j#T9FO7NQEC-G0Hr>Flx#)`HGl`&7%mxk$LN3(@ z{uOl=6sQRUjlVm8(!Ebi{7!}Z76oB`gP=9&i=IY8t^#Y9ez{WXOtLV5_>Lmb3Z^@b z?1LlmH$wzs-yh9T2wO%Lv8g#v5r~@1{d5n+@D9~{_86Iy8X44n3L-sQA}OwIVe0)E zHAWI$8#YIqw6?KF%QHj(!XHU4naE|6%+0Ps$Ucd<8m?Lu6a#U#g5u`(ZnVT0b~!l0 zvKDEAYjUJrY6>pH|7a)iJ@q{s9{7b2(K$|#Wm5XbvAnQEsw%_{adLlCoDr_IeX@3T zkPX!>%Yy5Wg!5~>YX{D*evbVQ`|FLl{Y6zbzTXBEcg&@gs}At{lFqu&YmSXidd4GF z{xwuS`d1f0rW5h>HVw30Dc>V;a!Ne?Ks0de^Bv74j~awMo$A8kC%&W%%D*IUj#9UD zM0Y=6N=Nl1)dUeWg+u}TUhsjXw9;l0pk=4&{6~Iqu0CAy%(_y;{i5P&boNRzH?hlvd!oIu zW+@LrQl=fUEOsr|OB!OcCfWH_W1+S3>h}&M$Ua5mX55f z%MWvnml}VpN?641Z}j!H5Jt>gWa+9qTThA{0%2PhV*&YhAIbt(v63ZCaXuHe4imEu zfuqSD{>J!hsct)Z2OZ4}rA#HH<^Y5W@o{u*J}WJr+Q^6+jC*MKjH1Hue(p?mTUZMagXu7wNc=6cJrd40 zJ4%fk_Arilw%Jg&HVqMqrlJxQL8e}?miB_a_%jZp%k=gXWX!k~QxCu*pKbH2^oJLi zP!Mnk??&znA=Cqtpul5e^DF9@g8NF|+8B@H0K-;ooLsLjtln4SXiuW;mL<0;)us*`xK8>>U^WlZhUuu6_(FLghmF7#1#9}zM6b#mJP zccuDq-oy)_IGBYeomMq$bTuwkUcg#BrbL-SUCScu6%Z=d!R$Mh(n|HgvhWP@3Myka zi)Qu(XWN0tFRg7FFB)O-#P16#==q7r6#_r4iO4$|JpY(B0E!}T(e#*xO+=UJ5)jby zZK6(oBbO^D$Yl4Zu-_#uAxb-GNGNc)NQboKi4n@cw2c^iNtCgKLU;h_*ZG3d0`N1P zFVR28cNkonU(Jx0e-rtofhnIF$XK4ppzTQfAqP5l3zz&Xj)(<+hWp(XZmPj#cM6}^ z^JI7w_qMKpdyP&Max#lWtiKPtRO~QKvQv69-JJdH$`FHz%(vt6?7GJPk+ci<%tnpH zwrmV`%#O*zK-0%yRW^-dek1{=P@9;GTIabuZa&FSb5Ci0k?rMXL2KCPV+F9-o zEGtmfFIH21J&Rn-hL^|E{GBrfaTRMzjrckgl+SbTb)7uAcsOh&6=YF>M&vd|dEM$~ z$ZTp=xd`+mnuhp-6G2$4ioG@n3CYbQl;|dF7xiYtM;z;i(ES|6&izad<{E@LN|gn5 zN~UrPJv6k(JYxJXP#r4$l7pnE%DyC*Sxu&n~mxtS9^vHt}!y_2~xBCHu`J_RV@}&qV%n6wOY<@>MZ^ zo(LvQpMcb0`Ewj`Z!DmTpnKbF6}sV&)o4-1UmUBza6fAY2l6?I>5NGx()^oddC&8`XsxT`i@N&Qo zA;<&q3)1LO1`Zl!Z%J_-g9J*oVa!N3MCtkrQc8nc7aO1!`$(WS_U4uc z7l^U=$ZDERggd{J@%83>Q!nd$0gJ)`I6oy#kEV={(-8|Cs#nBfbSlH9!~F!h9&$Kz zxCWV0q#|bJ+^Ggy1g#TEz?iX(uEW2MkBby5k8d~CGwL(<=0`&rY0sYcx`kF8;5<1+ zfT_k$9r_9u&I7gq+42cgeiJ5hBomnFB41@+YlfDFfElLEW4&tk5D`z@WSEZ#8uYUR zNZDT7wPr<71LUOKs|=3;jR~ho>8~$vX_jacLIUo$OLM@GOizPAwo;fxbBk}&y3#2i zZQgB_?O02Luv!(!S>Ih{CR3;R2}p#_mCA1T0!V17EyD^5@Gv}x?%eabi}(l2N5He%0n;^8w)T=I!!`!vz)tC>HEQfsW{h!lXYxHh8_ zXn}9&G0k#u;fzSNW4lx2NIl`jT8PT>5`wgJfd~x+MbCh_sUH~N!4>h*-J`e8kJtC< zRKUfD04dVX)5}uEl$($K<73x@vgjcIvA!QGp&TK}`24bMawb%> z6QX=i`MJA{P+FveNVwQcR*$lMk6?*44Wn4FFrFJ1=6|yZDGEWc)T5eW3}V!$ zBrY0cg$lqK1DxrBpe_TnHlW1-G4IsWsBRP_!2D0nkwUjxLlz~OKKbS5-vq*MfI!4Cvk{S%CSuUFG3RwxvcmXsCB!1Be?Kt%-> za3qhgqS?~})_rqaZyAfKH6K+>$CsT}3A7c&y2JcO5VMR;Og1^t8e9!T9HOf%;T^+@ z6CBaxSwN?I4IGny{Vsbb3Kf?~^>K^LD)QMg$W@WHCd{dvQi;lHVY&V@#%5#x!TvdR zCVpz)It70*S=6O|R8r3i$*N6^t36oG_niAH;gG~?-BncpM8+oW-`qj;zJZ>h_S<-< z1{w-$70#ymH7g$fU2`I^x?8zdvd1klHU)0ak z)jUP=O5K`Z-qpm*ausgS^5297_bQiggMIWiY70@Dw*AXX!*>=If&+?mMDtEgJ7rLh z(zXrx57DK6)G!sACAUGWt_pg_6@Pc2v8!l<=D^vug$97KD{t+B{)*`j3M#d?KdFGw zTRMd8dYj!F;}yJQ2qLFKwTMcJte(a7ryT34UD%93;4OS`F4X2@3KAM_r>00p-uHVb z2dK_X@<8Y{EeQ0n6kxe>K)~afB^$BVXH6T#=;i3Fkm*R@cStp()i@Pp_{oaX`vk~91gprLk5V49< z&DVgMK+VN^bkBwMV$a1uF2!4ho-@EM8?0~ij2ls_GPP1aXeih;E-e3@Y?8n$`191| z>6lq$OiM!6Y1x)d&USQHnS2+#1&-G%mEw8)O&NdAg$)c2f_CqX#K%H?@{)Xo0i>Af zINbRJ08vxAJU+J@5_Wd>@83&j!AM~~DhVGQNnN$uCL=o9=p8l82^FW;bGDuc=PN^U ztKl>_^D95}?qj(iV7Jv8qR6;gmyP4_)RrcI7)iyw*yGe~d(L zA_1C9`cyxd`aruH83DhEOuDL;dF!-=XDYF%N}V`IFVEn0YIztGr5Go;5HefzW3&AA z&K**L%U0YL&X?r{@_*y^2yb=|1jvYB!1W>nPr=<8cWL}qTOqUMi&+KE6}^d|Yn zsO*#8cwdbjSCNw;M!sdV*J^JW`RW1?i_M}>909hp5bUKm9?@Gq-vB{f0QAdwQ;6Q? z^U5=z8x2;&@qGImX7T&+7Vm^G!9OcLBpJNp#~A+Kxx&B81A ztE|t#51QUzD|Xl-0(NZyl^(vrmzz0wcr`f!J7;f{H8qU2i&Vy9=wpR0Hea(R3;*KO z{|2jp;%vPzG5f1DX~6V$_|J)F&o9CL3Uq&u8`(QEBmYYKSH=Gqcn;u*0!!BLV#=Yd zP;Zck4Cg}qMpa*r)9Bd}F(7yWM5vL|o;GMi|Jn9<|Fu&{?^yDjN>$X+DLR{x1eL>W zs~Kh%K0da)@djs$5b%S>#Xt>M`g{SN&{Onm{{{^k8`~b&f9>1fKc}as+e_Fl^w8mP zJ(m1RtE2s{0?!*I8i_?bIZ3u>zirO}E`ZdZ>U8>Ivl^fo%_q5j3bK4NCnXK4&+j!c??5559k$T8%<>v<%6 z_J2U=Xz7KmQWQV^Fn-V4;h*JS@bHvN&_V1u2xI#LgDw!^!GCQ72>0v*HBktc`uk^R ziEYDdgoGwenGMU$aL0vmPkah>B3Zc;qRapQ{`WAZ>b&i%^hbJ;#EJ7Qc_f{muiH%8 zT~WUR;zSaPs)N%bwSKxLv`-al#BCo=dwt-#p^Q=CRpD~#E4JodT-R=qbB45f zy5wdXt7xV%4~_sKsTpYH6ICuq=R2OaAn?++KL<0FM6ts?bQkVY`#of-s(H7H*?OPx z5W%7zL0eU@o&&$DwTWUio;qCaz$5R}pRTs&0{4QSKZ(OA|0g6nKmY*3k3^8{FGNHR4cG<^9{Xb(h=Y41>@sf_XVI2mmz6(Q4}ap?%_O6bdDOAGBnZb-!0XmK*zADvX4ridDc@IQfCA zT(;m-u>Z*q{+T23&nNg1ZMsLQP|%4#Mn{w3u_t@~TGmVRLaX`d-v)p(l9{!6Oq&Sr za;GtNE%Hu;N;lMwoRkf6dDc<1zjf4GvN>y6t^Z-~b?9dOS(RFprzpYfu7I+#fgp*G zn6j}2`bq!k!_AP$7m?CP-SL<-e>bGM*iuOIPJ0uJ!(^YzD~{GcU4ulb2_1k}|4lOy z3j^sdcT(=94d)A-zy!Yx5i`%{y4bYNVh%|en3b5B35R7Qf_#%OS)*qYWgG(x3;LrG zwp%Cw&@!ukZg0*=kuaYBF}Sn9E(8I&nOei}Y-O##AihMEo-J<&x1w33XTG5uZ+XGC3 z+R|k>#^e@U^D?)X5$-|$;bVN@B|dh3J{|G?J{Km6Ek5<_#x*lSEw)ENk9NGI_!^4i z{xtr=QX%!@0QZBIpi{`*J!~kz-_6OSh}y5MYS-V0&_jEEYG}B3zQPYo>r9yF)fx=6 zQIke#UZH@Zy_v}HyrIdb9G~GkS@gIjoAa_PB2^e|CA6A{v;=4jX_LP37kekv&@-)e zlk<-}sUIeF0Ww7i`6a)>|1)M>ilM=CzS%(h$Z$*h2}aryAM?0EAezaFV?%o;#)5^% zY@r$cZKlZH427BT@@Yz(Rnbd>>DcSuCbY!5@!r(uSN_r&W#s|2{{c`b>fiRX{4r$T zO~YN-!n@gg8VVW-4jsVDokVw-mIC14hs4N{Zr!s7g(g`4B^c>`XPQ9NI5?M1-DVP# z=g=!&b=6B3OXWl7mPWa<47XJs@SX;)%<3Y7IcAr-C$D0+I$+{$k4Le}HlU`3Y{y1bp`5 zo>Xd!f5=>pIf6uF%Q#+`#ZsEolXavLMWUvaRDvx|R>#u zBbO&a&cPgot0pqU1x!eA|97amZyLb`w~MD6`v~lxWzZ*T>DdNTZzVfz+ZDyB+3EnP zGusjk&!dW`k=YaJ%;*+Vrm^@)!m!m$XCdh2vSizP#fcR|b$Re-``PUw z-_&|0VO1HQ?y5||XH^p;lg(wi@A;3I7S>#Wsek%4)2{TN&Ke!4m2C zfXbG*{5|fBxo24Xn`|#*JyDO%E~NN9d*rwFS;SbjIGu9U*9RUHgmb-KK=y6o&em7O z-{1;2+-2=*hl||jX{=MNdBSQ3Oq4L)r`vORXRhnS9N55zH_x!x?BYtx%Wo`Y@nmp* z>$QcIm8GWZF%M4%*wGr;-|_Jy1y@AIZ-oByhe}P)alu{46}GzSRQi(%L9G^;Xv^PV zjPwE$(e<$bx+VSxl&iNCKVlgjYY#>k{NF~Ip*_;a#aT$uWmHlb{A{W+crsfXqVF4X zS{ggU=%YAwYbCk?5mk6RexCLT1@VHqZWtW0>Z;>6i?(*9=i6bIxW$8*{N%bZc8Ly4 z0V!Nzs5=SO>D&i$$NkQCg$HEzvl?R{^&xO%_vl6Zg#PIG1xx;y>2R;3bX z*~Q?ch%ZoaWlnvpO9&I2Ln#J_pTu^Pe%VSVTDI z?0ziWXpOmP?6}UcUMqt($l5-~)tD0_JkU?tz9Spsi8MHnJRAK}!ct^LJa4V3Xnf9` zk{~ak@ldp>F^x!I z*h%+PIQ>Qdk&tt@{b<5f0u>-+3mIF}V8-l5lU1dGFbRb@(2Uqf(->9Q2F>Pt_~ttx zN&TsuwrK6n#qc1djN%e6X@fS$(kK6#T70-t3h(sRbMHm15A-HhIaY7SnECy3ES@U& z)0_G$(i_bxC8m4GU4hpi3yUQmmhF?%bA$epvW}>8eW~syMIwWIfM!)C^0GPG$w5WU zTg@QrT0v4LS+206EJ#TPu1E#|fW=NX3!ht(X@tn2+B2_$geL=IenEXJ%f98~f8LEh zX7%rlWB_E3UYtiw#Y_0RJL&2I64a#`y*ZLU19c=f!ed+6+vE)NtrS?o7Vj6j7z#1d zr$7RwNZ3F@IoRztqq#q zjhEIT!G=R6Y6R;s3KO!Np8Fy3?$qLK+I1q#^;%aWOkl?&2>QVc!ygr%0i+vPuaY*_ zJ)S@&b*>f-L(M|1F)%WRwu+)gI(WN)P2~)R(Y-WuL`6h}*-`)q0{Eiw8p#!6*QH&9 zFxBJ~E4BYb7?c8hrJ{g@?M~SQkSWn>&S(s`kN*M(BK7F(0Nv>@@C^P&ebKNT=XueOoU3O|(L*cNWZ9Y?|ji@n! zbP}a_U5V}EKt(C|xJcBlR317gg&S!lwf42EY=6{zQwuYzqT|7zyM|8_jMkG^{5`Cq zS!%EPM+oyUMQ2UjFPmuzZ?6TC$+TK8?>iSGH29W$2?gaokp&bTFccqj5tRQ&J02ka zFN9S~nKJB!E`JRu;fb?2&*D_3O+rdMR^I13FZ)xb{rmr6?5(5X=$8HO!GjYB5ZqmZ z28R&b-66QU1&3fExVyXi;O_2j!5xC@z&kw8Ip^NrIp?nR&L6B<-961r_wK!`KJ~3C zk`QF1(I(GiSqPC2te00f4{rA}EwCK{A8i@s1Es0lxY1*jdx~R`f6oLsux&$K!ESgI>CQ;d%w`ANMdu4pT5@VxMd&AuW z4mo{UzA>-28huEBD)-ICo2n0}eR)?K*Gqoa7)rL-WQ9;kqrwWQi7LO?u~Wf48W%%6 z!lsTH*l?u+bc@vfk+YC>2kVZDXO3e3cGypixJ1}v<9!Bk8zofekJ5`2D*8;~rZmAi>XK*9Euz#BKTj`ouVWuG66OGnXNor=; zDyg2JreXy67UU4?nJ=$HzG>*{6;$>#w=`YfJACfkL<0oeUiB~g1^p9phLWcIalW> zC$wnoJZUFm4y!8*;V-sqjDvDYb^Xyu{Xh*kq^9J>gkd9MwMMf~#D28^I7}!MrtFFjo`3Lx8BO1YEAqR{qz)~s z-!iL1{mmx;s!uTajxCB=N|Fk(9%%m|$lmebvwHT%J+R8UA<==hl$b(*R#?*qZjnC&Os!Yr9uC0J|f3j7T! z>&R#(O@5%qcN}5jM+XL1ocjr5KcBV^`b5%S=34aXpDVw6-!?}tHugVy8wJGPE-kR7Z*`j<=t-% zzX1yAZik&})^beqJW`f2X>NHcLhiv*Z9SH+^ztz0=x__qGG=hTwW!B@bF=MlBIH-h z=0Q%11D|8$H!X2KUs;MG8)k7$Q|Ih=`Y4;FtPKGh%uoBJ8%??u8-VZ?gQ4v{70O8s zvn|?4ZnY=k0xe0MUFzYjt6v9>nDyk=yCLjMkJ-H2zxZw%QgrumTiRh8(^ORTTGb_#Ms0~6`k^#tDrA(8NBc)C=uWS zBvWFoYKkxWx*eO?lxdH116u+W<>ojw9R-E0)z1E4{-TD{EY&nKRM-w1&W%`ShCk!D}6Z8P^EeQ8sl!Xvv}JEXiA zcxUzb++jAyovZ`-(z3>&=qD|Iy#KlUVaSLO5j#V- z;m060l<(xc{cr>U7^&W>;Yh&tgA&qHEROd~%+2BPWo>NEdG+htCojKlYi+8EocOo6mV&F>XX4MC4JhMGy9Bkrg=>ilSs-pAmh$uq_OV?*Ikh4p( zVCMH1`1^|yx(|;Mr_8 zd{vXb6a4NkaqnMv_kusl?Fp9aI%H6pjaoutJPdv6_w7vj^}!24@Bp@jQ{P;r3)0Df z$GuE0W#w>dC|>*B;UO`4$kzzIjt9mqZ4@E)CYf^;cH~Q*EA1oTw3mkFQY{0B`;j6} zp6K9`GP%$@wpHJvgYh4dQ{zHijxhm>KhXXyA9vP^B$+F^g^>tl1BZVrWPOHZa_{s) zIy11_fYl9vP{*-SZuFiAkZLC%1CvNzu{v0&&UOG63)pgxQzf|KP+ z+4#;x`%g%D2#K6MT)_4I1?6vcm`*L2O!7v5-^R?dX=fN;PDS3^i`<%k-01UYsGKag z!ny?4`)NO6vIwnfokp;B4r2YM5gnb$(Y4p*ixet_ZgbpsvqQnl$uI@Fv%!>;~AIv>87JEhTda2^c6mQoN#2VLar zmu9A)wotWj?;fE_p?m3w+c@JA*|{;I*4C=7FMO9k3~{Jr1q@Q>Z4~UFi+JY>bE%F{ zPx8w2`Dyq`0%iUcBKswK;puZK>{KE>$DMct!51@+-dZ66lp3p6Z1zj^;!A&M{tU;d zLE}eqmD!r!FL8F@VGO;s5o05hNyukB{AfNyYC66e$QyY7RbiKsp73y8fgBy&Z8Q1C zQd8qp#T|CwZ|$EkBBC&*$+sL(&vbWEAcR@ook4+z3wS7`KTa&hY6wLKVygv|wNX;w zDIyY2v_A~rA7{~Iq_1ddHJt9?RB;$Qo0h176F?#~OGe_=IbF>qXM|J9j;6Ez(?CWhE83gE3m4SmBzue|^1WT(5O`RVc__(D3;( zQemNIOc$P{E+a|eV^8ntlsSI;Hqe{}e@E1@NX8SK&KXZeX6LrHdOhAfxStH?EzoB1 zsh{HfcWUL($}1EfY&1PmKE6J~?k?UMZ)l2o7~C`R zI-StHuagT}xH#@GZ4`}x18fTec;My%S@wT*$|3S-X7_podV>nmc)-_xD7;Wsq#zIf z&mF4R_!rL~;+6Es9psu19{n?;mUtd~%T+tQ$NiP*rMkNGCoLpgr)P&S^J;1!r;5Fx zlAyWE|4T#&W7HM0gt4%Hv*FG_c4f2reeBk}HUc{ekhe2lK0m@(KuKkF+aHU{M-2ri zy5E+M*$TC{DVQtd*06K9tvgWMI9*_6wH3YdsJ?yr3FqL&y+Ot>w$4X(@@u;WF|sD3 zKu6oa)p2dw(N+Q=zoS>`%MK)$;#SYuGdHcf6oXi9IZU zVJI8tf;YdGH-vX<(zy#!jkKOOCrf-L*o6CA%4`ao)=|pP42bj*fEg^fmUJR}L#w~R zIyFg1iS#cvRZf}VEEZZbYRZi&I)%VH<|e5mX~RnE$yTjD%ZcI5{^H&DGl61%9^c{< z-Q*IQrIXzME6p@N68ppJm=pnEo0TwY!8#4enw)vNnc@6W-w8b3;|L;4YYo9OJb29! zOL%)$y&fD^A4;p4xL28whKGn=PP|37ij|^;gGPm3zIa+NH}!v?3cnYIFP^pU)ZqGua%AVrdo@$1RQET7PRqmt0ub|E0z2;U6F((BP`qyF(ezP*TkpEXnfq zOl{?yv%BveVl-cevirV6YtTrY_vxZ?W>-50eG3*T{`G{M2FnlOC#0Qz`ftj< zld+M8C5d2`W$00Y<&0oQ>o#HGG-M|Sgc4G(r=4sm#DSAVmKv3rjrwK$vRnwb?|kof zxNDtdAmjnx@j9JhrOI%aAq0`~n)Ul|hx|9L=-n4_^aW{*psT_c<4ZnTwL?&kV6uA{ z)j=BP=R4&y#}aOc^<{E-9Q|ItkWJBw)}o^vb&%BJ0qSK|!pXhv0s}lJGRl~K%uU%A z#hM*7CqSHr2p*P}Dr8e_9MlKAAt?7__Y4GNaD6Lq^wNwn_6F71c+ER49WoT~0|E)0 zT1Yr(^2>Ie_mh|V^?W%*JO+yQO17gZblAbV?jX1}GUzf{MxZ}!;`QBG3g?T#Yb-n8 zM*q`onYN|~aZ1?##cCW7VK~|LEk$soDMihT?i8eGRd`zXpvdQlBr(#n$M<1LILaquC6xacdMtSYxPwR7INd#4Nvl7}F zv!%6C&E&koX1a?pd`Z)Fsb;+qeok!;2;+62D@Ooe3smXTk-jQDGTTf&l5t*DnVjOE zJ{aSkrn?~7)b0f>2W@&Hl11%F%wn;@sU&cFZzD9RcpLhP=F)pB{77DUSKrTk&rt59 zo$p=mqiZyjM|Lqo@R~`O&axS}13oi4#@o<*u2-$z%`r+AW z(KIc{orMj*>&WFN-T^te&H|(g{8&gw z&miIISRwR3N&(c?yuC6CHkzF-zD2d&renq{*#SXS`X;WZv^yfU1Otr>?#ZuuZ zwS>OC>m5p=YP7>8B8*_$EeO>~fKn!E?Q8(}AOi8vx&Tz!e2f!b%0@;D zvil(7I!IEyC)WhUeSBT}_N&$q(zy}oWR*J?PqOIJ{Lg%HQ;iv6>}XxI5CvI&vOi{= z%MF@qMSOI^5kM^r+09Qg=-N zmapZpEaK&1^l<;7*j3K+*kII59^gAy^Xgiy7L&1G*gnV;TeY+>Ri$Y?3ydAe z*eT@qsk(S#2#-L047H{UqmeW8P)h#j#4R7y?s(1Nd<2DY7ND}=_-cQXFvP* zL}F%Dl&AtT2NR$Mx~J1dj8YM}7B?!fSY6UNekOiEuiE-{*;BZ*lAJQzN!(xSX{ybqkJ6N}Fd{e5h!9Hp(gnAqL8&}E$&f|1L><~$Ixq#a85_+n znq$r=i!IJynbuF8S=Gwq^zV}J+xG`b@|}g-!PaSu5*^a=^^1OV=+5Yr;Y3&7KtL0y zgwSSzsq^@=^F{@{j^0Vy3F!3N+MsYVJ9Oslob0|Mb{_ z$h(%H^R#mVb=Qaui%#k%gyg}Al>*t|0j)<-n@oc$+pw{JLlFA!Y}_`ri)rW^EmER($K)2tC4pL`7)}AN%-0^ zuTtZ4Uk7_VVL>u$!JRzvwU%?cWsDTj<#S`iqadQDA=G%b4{y-1z*!1hoW$F%?4987 zwzH9PiN{qJNA73GFfFWyII3l49CF`y*LJR9_gK}c-lKAX9xi5G--RKvYdVrP=2xWe z>=L;@FFt-#DTbVCkVvFl&a*w5d6h?zyIW!i>YKb#1Ku(N4r^0=^440s4E96lnj}Bm zyJoeW9>#9{ue5I;hLV#5TTe#MyC;3UbfcKt-z^H*>m53iHI@T=R!F||pFg+it^Q^m zpwjGE5bJ$j34O6e7PzOEa&3Hctlap6h#?C)GW2VAx(BCS9~dv}qr9_i=hKfMdlERo zi1`N_Qn-7Yb$wu*nO;&kWNI4D&99e{OaN)nXi!Fw&(KwvUvti|u;$~{I_nA*0Va`M zdi9eQosGaXcVL-zb^)8p)$OB_o1%^voj`HH5tXMC0q)jYX7$DULM?#Z zpWiXtufWz)gee0?aO{T*HdI zijPYv?rvum5Q0zVkWiuzo*jdg1f8#oEyq=~39qQX&y}1K1AL1Hun@JXKcV&Ye^vOP zP~AqUzF6c-tnaD=Q!Q8-c$c*J?wuk+aMW3BhX4SE_m{KJ#_`RS`*%X)iIbDjlardv z^fj*H?9C7X?j&;YRwp_bW_!C7mdRFQtxyt1DL#|g*AqQu2@PHNtOb)QnZ)ri=M?Tr z2XmG0cL)4;bEYu>YU-gWWRox6GzbjZF5^?EDOOW?_9HJgmCe>jb~ev+I*P2yJoMUg zyrWkZ1xYS@0`^hnt*;;N-LgwR4Fs1Ql5>w0G)n*PXRn5A0-JYQt@p9MMEcL5V zJeGS`#q~wHMrP95=~%H^>^ksdv7M^?^td(c_)|bVy>YB?EP{oSv0^3L6xNbazRx_? zBLsnF%-_+hZ`{e6LXZ*lijB`&IGHG`S6hW8jU0N&uU7CXuU{hY`|dTYc*zh1u5lse zDK_GDT47pLr*~uOVZr?69my*Pn*>pD$od*-K$H)IyK5-g7jK4ZZ32=2H1OU4`9=}& z-?z6N5lmhy;kC7T8XBKshg206uddUi!$sabID^h9e^9+06R5=w#qheg2&YzYpC}if zUAfUYK7QI8!al3}_u0@cL7NG+wcyUJVr1L4xu}zb-%-8QwNC=!fIvk5AG}3F|M+J+ zmV@72={Ronjk>MNAY}m+zSwq1f#r^88>lWWx258FQ^(|)*m@Tjg!ZNjLB)G10-zPy z*=C~ctF251i}H7X%dzyHht}`w$`$yhpTBtX#pJ{V@ILO*y#1!<{>_BgSUK%gvTIl@ z$l-XY6-XaqR-VJPmZGDfhkf4XHpm{I$J#A!=2zJK7Qvw#$+HC zS>oz-?Zkj3{rKJB$9+6CL(cYJmwtoLCO#f>ci%LdPu>u^81L(F_w)0~ky^ej6DtVzo~<7*-N zdN*~-LE8Q>ci%6by>8bi11{Yz6!>5EC;I-l*=C%jEop{yead)W(}V$N+ce#FQ-I?x zDNdbG=NWKTm13t{G}-?0(0Wh3(TPY>?Wudc7S1Xbx#DoRg~!iC`n@?27vuNG+stZC z-V@|Z>`VumUXSle#*ZY2XVWl1U6{mephbE5~gykv-$oaWU%bs$(^^R9YlG4Vt z7&qFzp616o7}J+oM7JM@6(WMsp#>ZsZ?9X$dIH+*bho_^7t@pYW8_P76miovql8xZ2tA)Qw)> zWXg?nnlCLVE7Z3YI(@h6$*QPV5YP8B`2=l z4nR`3cGxeIibW=E$#IsI#dvJb3h7pBw-JV_Y3%57JDwr4@hmbO78cJMEJs7#T5sQR zsmrZL{qHmFBWGMDNyCYa6OyI^75O zbt50y_jrZ|Tbq~0+<6%?t=Z${OF!x-G zRbKgcGM}-dbL5J0OuLF}Lc?ca2Vvb!{Xx*o>#pL5Et>+!et)#WXU(x`hLBYhq(JQSYTs=X%wC)^%Xl$TvSfx0~MU=`%N?p#5;qaVrm# zgRwSwzs~h7Z|lr{2!OiXXVq~*KXV%%AiO4+>nD@x?%Nzc@)8Qs&N>E|JvJ*65sO8M0+(p1i;ZuFE#x?Ku`+WNB zDc144>F1i8d;WA2y20(ZGJ_64qkM^9G94J(e~+Drx3SdtC|N+w@cm`ylm_JW8u2UQ zGq(i#!DlAwa{7xuWiVZuSN@qI0;>Xb)X&zZs8hdu6#1h#Yilah{@lwt-~_+0JdBn29N_kYKm1Pgrwyr5J!_y55r3O)#$z`e2@`vmJJQ zhgXqr({}eL#FJa1bp6wTXl*LVg9pQMwGDtu?@)Rap3tI09r5Fk899DNk44}{4n zu_PdvJHJ&W0b*PyjyL8iMVudTwVv6gO)ih4cVQA?X0Rl#8|C|ypw66FdSo7rw@0Lg`o1HN(_E?5~Q?; zVp)-2G;j9%z@)6M@!FLW^(s!R!meu4q*fUhHB&%hzbo2OuHps9klQ(jChsTLi9ozjfzIf)BJ;R&^eSJZ$+W2OFI*Q@-SuwgBMTvUc z@OGDl;z$`@WbIe3_t@yi$cH&2C2dnB?6y78J!5l8NV`D<(x07S!_M+Xeza!0k?poN za6xLYHzzCDcS?oo!EhsKrE+;sR7^Q92{a}icP^sR6Faj=N|w}_aUd1xFo(tBZvvhiKu`KsG}1VL;Gg>f>H zea5tAlYZt!TMA@l8=vq!MWX|i^=(lQs|lC!nm)?M8Gtx4vK;*oBa%CEs3-?PR2@in zcCC!cprJiMP}9t;6%97C#huTHQvRP>0QwO+2(XC{Jk0rs^U!#Id`$6XjvCK@#DFWL z(J(#F9|7lCSSIhYb}`fh+;Tmr(u*MXU0PaGroxBh^^+MH_{kSf0mK#{B77S^@0t%* zz9aO)t+ZJ@V7X)^-%c>1Difiz`~oQf3I|Br8}YS&a$<21+Q! zC3ea2_g%g)KVu?5oAG?R*Bbkj0>-~JZOEl%Nj4$TU4<0gn0{%%AqnE6Rzr^t|CAaI ze&-9DM9D@9H_0m0FgRT57Y-Ljph~jsy@Ax}Lq&f*sbo zw)MQX|IJ8_y0p~}6(M6}sSM7KGr@!r5FP*yJ{~`&PERCO{69AGSk3YSGA5mOtH-ml z-?2fe0$t(?M80|Dm(|+9x}G69$}!~B2YK~_`$R84#`Ffa-npTy`x{bO;-OWJm;6KvLJkbqW(BfySR?Sg&!N~Cn;!l4q`^} z5!!gjZ_GGoh<%E_5E4^Qq!+GxN-LmWRU<>1Ivy#5)iX$QeeW-pa@I?%09&hH+vVGu znl)!gPGOE2b=_jEI9iSUgVtv6VA6RW6it6C3UmBff;vq#E!7_?i3UdV0tA({O=b-P zRJ;N+UAbqkMQ)LI(&1=(7!qLOG^kts>nw{bVPJiE^C|OzlR7rMBmEu zQKs@l55q8_#0hPmgx-u*mZO~n+)$AVu}3`o;%5d>fO8@S5x>;u`HkP|eXkq%N2YcM z!mT$>g;M*G3iGj%LnRdsmKs;XQj4Uo@HZg3mrPIdyE>^BbD~%ubm8*cZM6I$#I)5! z?xi|*l~^N@poi1264pg^h$kdLnh(LCt&r?lJ|mj7sDOTNHf3b;T_?E`|*7E zK(lS?q$1b$Jl0ckA~#|430uY7ErHg=^(yx;Io0UQe!id^@gi;k>~{jE2j`6`XGKPL zB5yK9P=zNy3FDaL_EkK|d#t>+N6S~y^SX-TbS*kRYu<0lhPJO@p_LWkOcJJtNv@nW z)YIwBPTVn)b^K->UH#CHdd*+ld@JuL?^jFqTZ_)HGn9>nK8?g+#IlYxrRaJ6h|$O` zJzl1R`gUALWYRPF?PYY$90|krE4ODFv%fwPhU-@b5mBEVm!KF9N!y${G&6`;oUA2T z__wj1zKKP$W63<%Qo&E~t!m10AjnU+LBmDqr7*j3MOCB-~_VgKh6_nz?DEknJ$n$7X)op;m90Y@n`Hx?NjhuRhd z(&>7ss^-{+$&5{Uj>ap_Enl5~2VG_gd3TExtfuNt&QjG#)R-yOB5pN=-Dm+cY+RY9 zKz5rJgi`v7WzNR>p*ENAjJuiTeEoez`cW0}!s2im2L8f8$vSrX5AF7sPKf@93Xa^E zaUJ}p`Z2m%QOu2Z>X)mQ>3s2E|=N0$=H| zS{fSzf`Sy4(7p^Z`ANtKzReoo`Tc{<`!|F8ZL9yApZbh-03q@b*HPJa zV9)ihx74eoe|;I=I}Xayz%= z<}R!@!4>5Xo(rcCuoOr<uK+G$IhS`Q*>-B^QDVN^)-Mg_#!4o@Dh zOM&D=&yHV1At2y$Y?rAY#N)gjgL75z{&w7^K$j~lfa#b;B_E`Ev))|}3jwRc!$oi{ zuL$|))tLftMt&e>hd2LjBKps1Tbs=JN@p^#OS!L5u^&6^U8Ot+RL>8Hxf9B5WWQd) zq$(>9n4S*!J!ja`@Y|tjj2kt*gNjt{GI=}a;1H{{z{|BA>$l~RO6}7g=#*o6*Px*B z7OS*;Vnkgk>GwiM-(YQW3={sm-a_8Svd_z=qe2&moy-KTaLW1vc-EK*)EXau0X@pJ zIrP0gKcr(beqU?%S{)w`#|@3DB&8V|$9TrV1LglzlATC*)V4Xg_<3+JV5(imH0p{J zh8f4Zve$T~5~!2l{fA(&Ig zmHpEBTIW@0h$o$spD`MTaczZ#iCIW_3cr4?)I=YV%%vTMT%#c?2&TBry?V?EbuXbbX>EDc?P~st_Jp`6jiI2gu`+n&C0&; zoPkCRNt)943ZVPmuO10Cq^WrK$m|) z%G!?;z$F(3<0>udF9$E!`RbE5G)hX0;-ZR+@@>Dpy|Q%hn?Dg-=ItMOH6pOx zttRh(-%q?_j`FX{EBe~2xS(L*J+0NN!8y<5Gda~BqHSz!S*$iDPhlpNG*q`)U^P2$ zK*93lX>ExK6}7{MVh3c|cWBxJ?c@JqOqg$0)%}s_{8W9cx`cYLl&@3DXxI>-dzZ;q z=b8v)SdLV8Z4(V3bCkpMafiK6xq|)rT$)PhWIMjgacaUt5nLAaf=BneAr@9?7VITGiF4WEeJvn0AnMFs;6Z)K=+^uijN) zlqEtMu1f``5W|^^*iS$?>zv09&-Y>3=EvBt)#5^QW4i*G>KT?DFBbSr8516EjiIzR zke4c(lU-bNJ*gEfw=B0eR~_?g&F9s(lA^NrnhnRR$ecO4Vy|trS0eQ0NM+X2ggSXeNYJ~aSV-9Dvh#>js>R#Y)W+c_w74 zzzN<+uDk8~LOlnaEB8fUleO|k%qcY^hMN-aKjeY;ewd>r;(l3o z#RJuf$~!t|ZqVXA@O zQvJ2rRJRXYfwVLFJQQ@=0kLQ!HoN4h5!S>w} ze;-2K8J-c_qwAiK9PmxASGuF3C{up!ojAsEO_0KNh6OP5pWHF2_!S%0zS@t@gdXGS zB%gw>={My=hMb1N*TuL`Q--3ozrMksVVihaw*kZ*GS|hYPAA3V{WMeAm|jZ4`1PJ5 zq5%Klh2C{nYFX~sIkHm+nTM5bDdj9Gy-2{1gn9pzGb=WR5T|> z%6x?sJ-D>hAI8eC8O+EAd=GREhSUmV>H6!HO{-^9B6PWe@m#sdbpe81WwrL}{w*|L zxREiw4Em6+($dilPfUFA$m-|&V}nM6cy(1<%J6|IbHnS~z$N=xB;X*4Q+_Wg9C7z* zi>Oko<#lnSLClmx`4b+;>w*9Ag{Q7(cN4}LXzasBz`&%1duO;5m|A)s#ZPv=zuEm9 z1TsBAM)l~~?<>?sxFB3tPKI#H?Xu*xZQbj;a=WnKmu&$mD%=}M#CNR!*lF>}@VF%u zROp^lTFBfNU)&80xx5hYVGNq^xN_PbotH$4xCoPK2`Yh*u8j^YaAq9GWZp5xjEOp` zjv{|un(E^?O#vQoc;4Dpl);lWo@sFdw{2O%Y*hUqpW5z>StV>otsftV0vP9V~6DybMqO~s{^X8--` z$mJ4x3i4_)@u1ZB`zd~CfRN;3^OT{dMs4JI=9F|aO=XU6%@?6Q-Pkxxc;ZS@rfulX zsQ8#0PmSPT5;Q5&tu&8pekH%q@hrSl8Huc?Cd=G9G>-AZbH;-L=^v}+114LEXyU`v ziO*kNLMhhLVSTu3hVK=DNLZm=$~J7C>*}CI#D;q!52>Y8sLbowu!m=+j z?tz6qdrKM$s$U9rgauw4+&)Mt8DBpvTXsRK4|CIw{(=f0i;~RKYf1njU;zAwOqh4$ zc4-~Hu@DE=foG4R8zkamGfa~{Q`R~ew&6Q8aEo>EczAtTNkXZHb{7${>RFj%JA``} z1Aufs_v0m(Eej3UqKwj08rzpolIBPq>w1zyH~B$gRJAl`%f^Cv9TuK?bc?%&FSu9R znDeVc7S_uOrx@o`C1R?KzzNSZGu^7;nRU=j84QX|@K@X5=cTKIF8G4(1*$bDUGZbo zkE851tYfF0U&ol4f}5By;h3({n+EA#mg+|Hb!SqsSO288G5HzJ0~?Zm9TqLkB~shB z7yKG73EVVwDGYb2KCJpCvL9>s(!SGd`X|JEOp3@h+411^O0kc_5#=eOL_wUcLG5}6 zw%Fa0mT?BtxIA8PckUsthbCR2SSLWT zT0efddrHiR!eMv21*T2|{jOWdSC2*xxG2EZR-NmE0d*}vfA83-SoIwCCR2il$|NMK zAGR09Tq9rcr_uz&Lms)}?;g>mBUlUkT7`Y4af?9+V$8kP?a;dL9djy4gp3CsnKiof zIvyicDh4b-f@qH@twyx1ltLy{*3R0kn?Oxt0Te4gvhSK( zSrkyT>E%u==pwSu=Q44O+?A_XzDCCl@0_hqajl9o0lC@35ambcu!7#=?$=Btig zhtN$&_8wkEdh7Sq;#h+v*7N#xCfdxA&`bPPZ=>_}QZh{!cIb8J`}@r1gN`$0-fwJK za+RBHGJd`3mfEf`1XoM^=93Yl>t06?N2-iZOtAOkY*6x*xg@1U;((%CTpnAXjH~}F zYx{egK=#BP&oQE`#?TC&h{v)F7w zq*R1PLPk=WhBC;eBW)>*q#&dP+xqiZsHC|2)z=vZtIPH!wJV;MTpn= zy-%txy3zSDIro1iQ>Qx4IUPfk#>dl7v`o(1^z+O$U^6H73yl*8Iq?k3Jw3EK+wMs9 zDJOhZ2-s7r$1sh{E2zng5zD#3ZGcXgFSw(Y4UAk3 zdm=4q3PAG37PL~$S7mW4m3;&Iw+T^(lu478g1PB)dtNrnxEHt0{p+wnbuQ$FQsbr{Y zfAo6e8~&UAM?(-%9||vEG?AUToHW6Pj-KM!6ocFZjf;zoJvI^M=uo#o7EcrkKFmJ= zc0~qnm(N*-=|i7+r7o`=V}fUj&I1wtQ1Fu@;3Esg3c#M zL^CBh?ZxWDfK5)gNGfnG$n}I{Rdb;r@wljIE>Zna)O)Q|O;M;AEfdEvNB{CwLQ1?O(%i{vIP|l^KR|Qy+T*hsnHTba) z$on4x5y)t*V5}_BK6K&_F?zGoAjfm5`;Pjc0k`Vp&#&tK_yOdRDwU=6=yDy0INa;x zy?yp!@eKCmm2;3o!<4luyc4}Bid97~)QZt;A%z5E%(1$0Zm#DYW@CBsyqi}qOpBgT8qu59esz)GpLbs)&K;^KpHSbw^oyLc%ZtJ0w3U4>X zn*tym6n2siWrc!;l9Z$d{Kv2gOO*1pDhG{BESxWW&1)xZt*2p20@cdwmP3)liq^_< zyW8Px_KyP~#?k=#e(_4*vUrpRl%P_+K%3NUMPf5AF39Yf8b!=@2E)f$?s2 z2zzq3YZ=vc<<6xPiaMe1`Nq_FDPs*eD44U1<{sfs*ASA9R&_Bzx3c@g5P}j`_gXg* zI^l2dBuX##f!)@Z-Z&e)!)+ie|pGW`$5mWKt#uxTHncCQ|SJ>TT3GB!mhg5b1c45y(A8}pqh z29julne};u0U-Q*I0HyP{7bG7|Z=|hp4jxHk zv2JTVT%F#<-X0zqIX{6Md=Y<~tVI7;bqE0urI+PdUnJ;c-=2fMUbm`^2O1|5rCbYm zq2eNO@*Qq7#TaO)lgOeF^S$r<6!iN-38o^{TC`l-1}?6k4Z+?glVNn;F==lz3-FUA zQx81cTpP1z8s}6Jp?RM^qJN58S+>=*Kmnu`%y2X_6TN~;em@~fFo7}Jpj>x}WlPaU z*O(~=+-Q-UAsgC-Fjk}w$L0b50M?~>1JqsnJkg@WIjiCLR#?X^FH5FJ&HFY_39$Zy zHswd;?AKhaR}8jhk(U{At_JPG^wX^duPDY)T(Y~V3`{yu_xcCH^-hD?9Auxx!3;Dt z&3n5xkl)_AZV$2m5I6h{#b!zd>KXGufiv{piwCWm#F$ zkP#hCv75VWLH|E+UK9~>Ls~k>(2z*GX-kh^C8*sSiif8GgKA#hBl?w=iHWINUHbHI zXy{EERHi-xW;%w^7Y$QzW~i-j-B~d?OAVF6@=+KCu?Tg!zPrl1Pq2py_5tmBPzqBW+ z{hEN~|D`>d89zt*!~cg_tuO=}|3lRDTEq6I=5*CvZ}BtePPT3V;!Dv#VM0db48SGY zG+z+`8tZ#|yVM76?Oe5>o~XC? zgWdxOu3(3e?2w)&7V!vggC;`dnm%mtwHCAZTXQhRO@#Wi+MK=^GCee8^q=6Cz%q}C zGN#NoeOBv?;`|S9sHF1pwn$3vNuHmHf0*;n7jgPbY%kaC(Wz~qcJn>>-0w1Mvipt9 zoK~H-71=Ve!IF}JG~UJ>O9BpBmVftp0KY$K%Ahvd(9fuIQclco7tOvF7%~j$1qdvk~Wvw)4vh#13?c1W$c)Vv`SSBP@?Yxn3z zy>*!MZaUe~HfyQNTS7plAdl@~@6eewk*noihnC}v-(x9l9sx5@`QPLnIP%P{yzX`m z8?ZR0s|{sNs8W?9>^5Y%i)oeS9k|J%tV~U356l0$yE7K!(nWts4ygI)A>{n# zpe!}UykU1r4#+x5zSBI%Mz5s++k#aNOSUS}dAef455xe$xczUXu|uSS^^POqg65jN zH*l4|c2l^ta3O6WJ)h+$SP74pHDxLVI&0RepG!)s#`?kS&0MEeu8eLmuHbc=f1MHC zt=@c4YpacKqcT%NiZN8WQ0}v8DpdP3fKAN0A6q(BsSU)!oQi=0@T<;KteqQ z)9ye^#YtC5Q$m*V&&y{&Sa`X&tjHhFTvQuv+s7qzPq_8kiEql z@)GP2J3}xm!JK%#gD8!QQX`N2+s}`Z2F{EPUi;%ct|v*#48@I_X>(-K9t~6*~Y=TaTlkamr57E8qd-9SJ4VOHO0TaJ6Q0?u?mUyVQ2HC zm4}_Z;@L97*;&TfIoz^ev*v@qI-hq*r*rjKQT)iM?{u9S$=Onlrs~Ci3(P)le6WEx zM7_s9uXQL}L)ZYi_K4(YB0Tikw))$Rv8+^ANF`HCv~JzqdjBcxt3a>*&n4>Uc+WLa zhv&%k@7T>VtXTrD;*9Nmo7SgKpBNb#!E26pKtu*%Tg#guF$#Ba78)GBF|ctR*{GV& z*laLgzyRNl9mgc__P+x`mvZ;h2Vk|~zY7-sUTXw!ZMb-z2KxCM9r`*vOOU)kuwWaR zO13Qfmh>y7guUi3@dfp85t%m)2-+9^Z^OUppKaE--EYR%?@i=bOsTHK`uS*3;0v9G z|EUFNv+Ej}cOj|AenB4n^0? zxMTZ#7Z&B$$;V}QTCQWajo6fXafO)Cpf`_yF-E#g1ZNY;O^46lR$8Hg1b935ZoJg zcX!v@WbgN!bL*b^>bw1eDjItAT93{#=6J@WUSC;W+Xqc=SZ_yY#n>bF9s74!hiOa9I7Dj+T4jYv} zBVD_vZ)Rp@)wX{d>LC$^d-7bSeRG+{yOe)vuAh}}(sjh@Jf)h?nN?6=X(tkbBc}GN zwhA(&IF_%#4d^t6J9iSFi><{I-R7q1PLyAI@6g}gsqq=lU)LTQ&kec_?8l6WpaVKK z{W08T-9!_fy0E7Lg-G@ZOCHaHt2E-|bjkGD3`J|REc!;?45Hf$u5rdrQ^#NuZYDE;Vltb?Z^zXjjC1TBN|G(zS9l)tPXJ?pWGTqMe~G`WmO7U6n4w3Gg)XS!CoQ>4HO>wQ zzE6!d{=e9cHC3B}b7!{plw`IZt(aluG6B8&x204jC#~i7xlD`$w8mz*MP6P5D9#`jr3bEYd3lxtvrZ#p zN|0Pk6cy9;ACm!^k)~>=>-}IWRT~G>+<`akipu&%0*#K(^Vx93Mo+w=031DH#2zQF^=_9xz);!yy zy!{VXc?Ye3IEDb@kg9D7uffTt$YROU)oNwKCPYDC1l@9_-E41?s#T79(Sz&d zJ-p87cKj)+N2RVd$ubKfgN8hm{8!>aR4L>ByrE|C~)W8_#fMk_uTXO3H# zK+J^<1c}N31h9Q4f2bc!WK>XYD6~qZzwKC+V@Vmlo(Xl0h^~uM3j}qbVQw3UQTyQf zuqLu^k^kNKBpbH&K6j6dFdVOE`+ZjbhU^GG?wjqy)UbHn)C94g$}^AQbc$iJv2Cb; zqOaZVd}L(U%^7b z?(yfdL_Ju@Xo*3T48+nbAwmBkz;@tEO~+aWxKh#7sFUAXVu<|mCr8X3E0dn`qcC+d zGOI=yZ@0sE1Rr@cGV_t5&TB8{V=jzSK7&d z^H7@Zozm<)^VLk${=G}bef!+6@fsW)z63X$#zhL-Ski-+7v-G2&_nz_9yE`)^;MBx zn1CFXzVN$5&LuQ%O$*dvHs#DoVtYXe^Z>v4j?!|n1|LPAIRwNI$8WFk>85*M!%xrk zu4H~sO8geHJ|eQXT7ZtNC(=oi!e3XfV+MrjgXsBp$HQ<0Oq|azE4w z@v=0nWQ*$sAw-f!JCh`QmK?B)NeV--Un)1Ym zf7Enf)6U;L0$1Z~K7m?*3{9|lZ3MM5Pqv=@ek3_W@k9g*8pW7N6V!DUp}w8LPyFrj zw6B#~u(#)Bme|2q+McIossGIno;@d?7dE3v_0ang8wu$N+sB6=6E^9tC5skc-7!Fj zqNPEoY*HxP&n;gkI`WLH#oN1Qzx@1kvQ=mWrC(@|C|&XCSJhB`;avAr`sWQ?&ADBH zi#e>{Jgz=8@Bj$*9o2{J;g9yyC=k%Xr^at~HmzFn&2ct*?I!wkoj`aI;BWI>3 z6b%Opi|Lq2Xk^`NLvhqj*>--FKQ}37!BlM&_e%9JpOraFqQ4<6e<-T=B+iLcp5c%wQ`AN0(aqPAuj$5B?(#Kzg^? zU&coLuPnK_FI}3My03E%=wfcJbvHwBu%EoY|3MAH`j9uiPZ;6DopYP#1ntNvZNXt}y_5j%0(>=EH$Sn6?LPXz*F3aj zw4X_O!2no2!g?|CCIBixd*nf8rouJD&pn9_Z$ka^t%>s52S)2}MjioHvld_3+6aSB zK3NerSUX&AA2GtXV6sf&UuA%%8UsJ(^nlzx=$t3AZfLIxEffVO2$lav@u>e9bXk^o z@0Tku>#6y>cZ5Q?<-E&sI8XJ0H+8u9#-wg6GR$4&`hj4X-$E|@9WqwTwZAIdr)#!# z0IPfG5U)y{#sIa6`4+dmadtu&n#1DZ)XphwG1j1N^TVfajYyf9D z?fGVwUp5yh1c>)ix{H*Q7Y;XS&|OIC<-Vu%a{pK!U zUDzOYkgxFVC$cSiV+Bi9WSL2boafQXT*M&|eGtN(lDQBaj`)hCJn~oe(o0j%DuDj` zEpmfVrgbU1%vDLZ+m~w-U+fKs#q-P@_FGK2M2CdHJ`X1oM2n3n=lFplQyG9KhOVxz z2FR;SOTr|8n5DjX%jTN}_;g|GWo&H1;UDri{guLuM4N3>(8fcgTnZk6RI`MMUhF#H zX^GbziFzuDWxts(A|&-ypT${9eClZDZjoR-je~Hjg<<06s3UGr(Z<G1)>4TzOs;R5=_D0vo>YzJI2u`j%p>%e_qe-R-HsB1-_ zS$*y@&3;$lfaAmi<+F*Z;|QOZwUr*>;xDPXHsvOWg@Tb;_H<{-k;xCASGHQ=%iCp6 zri;4hSWxJtt^bWfVBIm`*?HF)${+P_Z$cirPp)&dMzhsLM3X|0mT@=)dA`XyEJcWH zML|@+v9F@0si}dpsQ7y6?AH%(ySUEy#P}HPI(pNx-ht3&PtygB-rHz@O)KQ*r?4VU z)Ks*id;lzkpz@h!6JHIHu!Q=w`G3Hwe2Na+twPG%&zw0F)cg@GPU5U$olz?)iB%EA zmAUUgqR*Z!M{s9KxQP>_|HNQIXc*`pA4Vf*CZ{GKuVMqe@sg8G35NGCyV`g;xe{u1 zV4|g!@$ssZt#$;uh&I~jFMVzMEE9F*MqGnB(@LrtzZjzdtT-XnFV`9eH`D@D9&_z~ z@Y699Y>L>&4Q_n`x$Ma!F~6qkuubI!LrSsLJ{4^+ol{H=kPo-DSfTH zl;7ebVG!$o*Yv2?>Q&(9J;LVX#buIV+q?auHG5RI45)mTbS8?DXG z*{V6Qolt-8J7%B8Hwn=2kCE|a(kKytJUXGh)Tn+=ARKF_DigBw!5Wd{)3(t`)cS^$ zYOs-Nn4sj!BbI{lN>hSDoXLl+55E=;K{@qA#VrLV$A(Ut4zz~@f9BxOJbA5+e;RJl z3?XYi^+t6-Y5LhLzMV5oI;`(_Q85$wN+sD=XE!oZ^M{CsaG{9BXvVK}!M*i?HKl3g z+SG{hHe9@>{8nOFm7-xhC<|S3hV$e%p;0KgvhwLc+-&riWjon2^Vw&QOji1{`weL79cZBW<9b%f_xPaS zvhH!K{Cj*=b3yu!3(ThAKlp(etN}JPz(@vtRsygun-i3^3%)3U< z84H=I(^AeaW0LQYDK&LuG&NPq9=~g8ymJ~MTV0P~H%+lWpu>{PRiv&>Z!;KseiHJP zy0&^(WcMJW(_-t2fu)Xw#lKlTYG_KF3;Y8y@LMb$8FY?5V zJ*X}~>>tCie^#K357uK?k4ypIH@pE#5;J|X4Ly@6>z8x~GuqS7WUEzimC#B<0(@4F z>f(}Y(V-0()9@q4kR)ZpZsVl;;1=UE=a3u@9BMJT?j$k0yoK{=sqMCb3LR4!G>Phz z(iX1fiGZGN1dJf<(1ytmc9c#2GP3^;*CUji-^PH4GF)5MMwE&Thqv3K5%rspaehoA0|eaO*~~**KX@!3d2f1k;2E43sDy^EZ%ebAyLC7re$V|i@kZ>6k89H zqmz9i%h4iO-^Cq-c@0% z4u=|88fCu>nWzOEuR`TPYPf?Kg;Pw@FHyq>Fr)(UZaht7Y?oh}YBau%S>OMf{@J|U zNYr$-uWf8B>3F-wfds9nDdWVP@Hs!@kar`N-cn;b=#ADdhS(n$I_dM+@8W6m+uyY7 z4OVYT2Qj2%xwyx0F^NIX)kb1c@ilaZ(nWeGZK!x_(~d#SolR{}7$mx?@lYW6MXJ5c zW6+Qol?`!Ltnowz3Rb`eHpcy|qN|d@4NW|zhDzkiJk_5q`agxX_35u+!@2hE62q6) zHCGk_cJFilE2-d1qvCx?`u%&=68^snNhgbg|EVHYSNy6#)%JGOGgcMqDggt-|LK(ih)EU+fx}X2KXp*4QfVh;I*Ub z>&>@+|1Rd=vM#7nirMl3I@ z&h*1mK=%YX6xG-rH(VesaP5u2=lY+I_3XN}GY~uS|9?SYy19)rR%$_&uQPMv`CUiB z^rLtg-M*!twbcIIYglwAn*lPRi<8{b@6hcAFR8b!W&ivqZ}FOK(Y=4~(5(3!RC|rw zru|Gz$j66+lQ23ezmM?uvpz(W{USLhO2tB_)cwCh)AnofG1?oWJ3B23WnV!B(wY}xG}XH=CkgMS4t zkbizIZQauEpgPt*P`5{UKwdh-hB(nXa>c*^fc~waoB_PeSdeUPz7_({;bb`@JOESE z+O;2YTF zP%=e-IeInbK@AcA!S0>9l?uOXxe2Gm)VYF8O?!ZHr2UJ2)muR1!~OA5@XSdI!`)D3 z^7Cmw22TMSz;@qJx5iiq9~Q8hD9d+Py9=>GxoAk77C>FiP*2HA=FH7{J4Yr3Ir~O5 zCtO3~_>)e{qRs*bkw&XIHpMX1o9VX7o@YFy5j+6 z1E;c$xTIh3n%I_~cY%MPpTru}u}faH9-TQ`k6oG%!8)BCXt5{xzN-0Pm6HL@nZ z4>1Y95nRNsXV{t8JY-Aif!YZBfI_fr`7)7Uv;fi&aEhG4|Cu?ly|55uVUAX3Q`b0Z zUdMq({WgPj`bsNE(IUYChY+8W;Ana0wCh|w7U?*3(P>*n6hiEk=A?+tJQ?G)RAE&y zS*K`lJ4>mbW!rvjdtx8m5E6oEp7>I~J$kH{*WL7xZeh$d^$Vddy~OE5J(=p_Ea~Na zP0!^v#<}*>D#oAJj9!3;W)mz0%2Aa%fX({_czNDDXP;+%aD4@p+20ara6v0ogm11Y z8ZWhFFPMI_Z_FFy;?q8xsWUk326*Sq&#DB6+1dnT8?O9~T5LxJV-0M&;^!T;k%I`d zX7@AHyB{|%q4CHInG77pv`{RGQlRqOCXD9+r|YL@YOwaUo9vBnuaf~n*yzSt^L?9e zQXWdI4)(H!8dfN~Zbft9$6%YwttV?;=CXOo_6vDaOit5Ou)U33$BF|Wi4z|@Pop&h z-wgX~j-#6$<&L8#cSrs^I$iFnDmBlu8u+Tc+!1{A*INgFbLhrj9hxBmCM|~L8Zhn8 zhWbNIMwX=%IY-x6%`FEl7A+j<0Iu!_D&rUV{eV$dJsb#`xR~Saq78^{=)0i^aiLF; zgc4NOl&3q*h~8@PN&SuqGD4HL*MYdqnR1M6w>@JV)%b5)A;z5Y~e&hTZm0 z?!@@p_iFY>v&Eo(C%|N{gW;_3tnw7_8vCI$1gz6r@L7scFsPfVFKoYzRBtTLgg|+< z@d>?GgDDH-;e7MX+hZ`7zFSF8++p3o0m|0B6!r5?wYO(PQwdXn!`eoqLm?oxv>p!R z>awwX_TKGsBu0RA*o~mYZY02$uY|tD4V8e)eW|iQnFa6zBSLzY?a8A@_xbG`3B~p< zenyjV!;{Mw4vJ$(*TB_e;4O4BfaPZG{#@{atUw{}m>26fd543;A_m8((K-oTb z-V-H$XQS!L&l}K0ZT2c5Fo@>@RqBk2PXk^O{J%-=-b;Taq%vI)YJ4+Sd`7vcNk@i@ zR%-Xo(dQGo4MXH(;>g>-^C>sNn8Cg=^^cy1lBLl^t;^$K&HMTu>(TJ9{Q}7<=85d` zZV%kK;;2E2F*H1kCFb29WjX-*BjcI70i|WF# zH;3`cu|!zxz~GOS=jWJC>#1(2gOqd^d)}GvdP@&D$N8*|!$HGZte{~2uQ(vJBVOXC z-vPPHvu@)|7h+s$`d{MlJ--je3nv&vQML94&dEixC4=W%ZW08cqTkN>uwlOTG<=UG{na+NL zl)9v~XJgY7F(RE9gsZ)|mpNxqa9&(KAoz>(8~`zHGp$5y@TH1xH-vP_1E;t>x}2zw zKI}9{Y+~jX1~r`uLUEw+Pk9w6pY>8#Zk(1Y(>o;GxgS%as5q1~?+QG1jLb`-v-SGK zk}dzE1^Ap^4mSapm*4)}ho{QIrcbD?&Baw;QNeug9r!x`2Qv)f9~!-$c%bY%nd4Sbbk;)1UeksMPin;9Ld2lYIdYkG9^rUa-HG zyd@H=t(cPXUojE|0ujS-h}t}!Bu|S_S={S_^2JG|kaX8tspi~d`nHO9>La3wG%G&x zGH5FFYl$EgM{kL)C^%fB@B)u-*YnVvt-{1{+K_arShYIu>O<9pT~HIWT2m;X+Nbxf zE|Ntc1yeq}BuMz1((3TUK9O6O**9l$#@DX~P+Df; zxw7GvAHA!8SAgb^pywfFROEq|Yl%ig68goCG!6T8LB0EMNW5Vr$(F(AzEYX%o1V^1 zA|coC-qCa^?+03})q9aH^?slw1K-rXt|)VZ6C~dP=HHZplEvu7d%~x!)?4(xp9SeuxAg#RghD&@vP?bJBQm3~?=k~4I zT_-f$@z%=CWZ>GDmgIn&@OPF4bw7FG3hSYiOzUCY8JEL_df;%s`6+D;_kY&fLdxNe z-15{kDr((+ME{`Ki4>=!y61sVR668YlJgq8<+ z5`W1!I$NNGs6D?FC>)V4>dyoRdhEz_$C!EM&qd{=J+S7^(F$>u$)DER@4gbcy?4iC zAB;fYXs@@=w&n9+ z%9fdnNW#?LPfK%zAGiI(9xoYg@v_SU<3aZ(wE@%1BR}T&h!7u4B`l*BX7K5*FV}*r zOuN~RVSOM8QdJP+z-Wq*hEcKSS(=?E%Zl5hF2wS+a98Vo=g4&xoT~pErtBJ-Eu>N%n}x(Y10T z*%CpiHob7G4<)$QacoBrnK?ELpTbp!C__cD!_2(dc}mdJX)n5)Y39)JxIXUF_dlfSTm-B+Hkb>Myq~?+t?uXADIV}1LwWEjk^7S0ZJ&QsJIu#Ch`Ag3251UoX zri}Htgr~K!VaCzaKzz@INeC~>wQGt11KL-XeoZ!D?R`t5RSp~ zcBCtQe|{!s0q`fiw$@V4q>LClTwQ)xm}5YD z`%U6Vq^}KDY1Fu*T#WKD)Lhw4^B?QIJJY7E{eArRcI@2GAKYNJ+u?t7YN6{Hyov+mUB>h6uZ49XmMaVW0tM?f9o+>FUOYn>1=_x)4FFZ0Nh5Q1iz_{Nfu= zQNP0L#mg_`9;;S8`8UC@PYr>V87kE-7WbEF`ufddNVf2_qJA~-UPAK)TqnzX@Bp)s zC71_KlurZ0@I04e0lWLH7$=gF(ODOJQ1XA91agHa$Kr-h0#sCl+bZ^@irA3_KOF2? z0fOB{Y24f0iiWg_oZkQGhJnamvk-@hYM?=o z6p-sCjLl?AY_cqf(kIvGVj4KHF9wXAcz<4g=`Z)@)q>)kqUP|z%RhRQU=7%b_IGTv zmLnO#lP6XdJu+dy7z@_BKB(a9vxijp=7xKtx(^)SgXBf}KCoo-W$1nV}+8-)-maoXj>25R~xpLdcwUSR{Ck>Ag95 zbGJB{Q}(YTMxQtP()qc4RkbFO%dz!wiQ^DFy)Wh}^(qVoh65pCCpdiQ)pQEoWjM)= zTpbz^iIclU$5B8{%0z<>qXz(lb_3)SRmh8fkv!PW8?Zx8`^fY+Kn! zYD!$@IJY6E6PJzN#YOz~a-R72nKx~+^%(#7_{4$SdG_1PJBS8FFqpa7l*Ae4(vV&kaO_KlMUYpzWngH zL3R`XatSDmhlW&%7$Kcg%a;;TRaI<0ZMkIujk?ndnqPOiu|c?S$5Zzba&W8zs-R}= zbp7RaGB_d`p{WFo2IL#u1}Z`4d#XR%$Y&B@E2ZfZ5NO?vD$s20bv>(H+PV3v;~ylpC@e$$^_S;dOVP0C82!m7jWE?ebf|6@3QW zXBuU^$kzmPUkwY;D&XAZ?6Lae+L?lclLj8>_Gm7d^TOv@ywq_54*hBT2r){C$(?0e z*t#o%nLuw*(2pk5pLHX;`KR=7~?f^o$+!>s(joqdtsAiDE_CCYoW6 zuMq??Y+8v%!NeA;Y9rk@XBI&Nx;4)n8AEi1!XeduO;jjL?`c0H#&6u2lQRX5&{W_# zs=f}O&Cs!7U;s75*0aUbD}Sb0^|lvVMU&JE|8kJYcl~#|Nx2rn)ram+mA2M;V?I|0 zaOQ8_Y!FXB6sd8JhAd1P&0O0##c`LQ@;4Sah}4JCGkA0}iJik)@;Yq|Bsr=6Y?XRU z$NISf$FP8D#0q{Hul*qq-iR--SbfB8VSAK_cys)sf!t@GeNyoG0WO`_#lvm_zPU~` z$3*OZ0TxhovgH{w^i_o~Xwv6NaAV3X&eRVQi&|UGLRQol3N;^SA}pvh`xf8gWo{rm zF$&Tv+RRALeq}o~X4P3BwI-D(hQnyTjUQ0f6PzRB6~qqwzOVLIGN`7ngaO%1Vub5Y z{6y+0fvyC*w+l5PG&@iIpzN*EhSb8cz3V`jSC7$cZ|F@6b^EJ!LTD!Yp zr72UuIIh6YiUv`{C`uFSJ4P^s+vS%>hf}S*YyGk#M&|ftv1#1&LVSeII_AeaI^j|d zG1Z$Nt|q0pOsMxbtxy3JjbJ&qWBwZwym=Jq4`&yf_T3k3)XVPalW+e9{2spj#$Om? z=u)0qoR*)TNZqjyHR-SuFP&0P__G@K1!gl!{f)~lmfF<9c@RZ=qinIuT?AEg7x=t+ zLI9!&UzOUa0Wgv2V#d&pbmJqwDW*> zo~Sr>qD1*d7dGrEn>&w~Y>6L%f1UpqZiwgnzsC(6u2@T|f#S}Ei+d?fk$o>EthvRMYjGS| zJJ8a*rN4o!eN)2%&0!!#WGD2(E2g6!&Xp*W*2QD{rlSs_e>rR4ve- zaEwFF3Ng79ks17<#m#w*VZBjsc-RR&!F>LSe12F&GB@hd@#k&m!I)N5G%aj=NV6ZGKK0sJnw@*w9yPX z?q5(%N6H}Ox*6o_ks<>(mEs~g2R;W~3irJg^kQ}8G_5M1?<-sM8A$W`1eJ6Mri%0j<}*d9l5hGA{_aw~Y)bv(bP$Wj3fDypfl?wBU>-ZK;VpaC43 z`juPv(XR{;Zr8Pki!A@31@m*NZC<}aInfg|21Uq3={zhy zHmQVsBd^rdxPY;6!sP0EZBBul#%_LaZxG58aVX?~tAoSEx2D+TCmWW9@qKuYDV>?} zxMxK%wVB~z273Bm;cs4VPPt&NSw$-~BPIs*V!sg^fF!Qm_4zYioRo6P0?OEzu~4Zq zAN`johS>H=T-!_YmfiE?XJ|NF4bFw{UpwG=xQomjb-XDq10h|G-qZVrNNXI#h>v{5 zIiWgye%oKu9#Q71XWJloI{dTCtFEv2CH7poV~*`Q zJ(6}%!O`?N@gfqv=E1Hk6&2>S2_b6t8#RZD*rK95U!jTE(>J_thG@^RS4nPJo%lfK za|w9ngjDKpNw9U$w|WsaMzeknGfzy(Wq*{@>O1^AZY~}SH#D}?;Og=`yYZ#*QpT`l zik#;mFKE7r8!&k}`Au9+ixl{$7pV>LYMCIh0DYxs#cj|y?N{8%OMhq#4hZz}k&u~2 zyvafRw&L9?Y??IDsv%2JIgN#_3u-$2od8pwzfa^z)_NWH-@8>TGCqk$?j5F&&z7*ADe5?9!1*(_(19e`YxsqYOvpIeyy`wdwjn(TWlkfKN>R4~RLQ$F`n zDYZKG0VZAr>0ZEVi$P)@8;N?rn>QtgOA87wDj3(_7?XX*l73gIbe0IMlxd4uE^!w+ z(lHFKVe?`xU#1+@*fvdM0r}5-%rhjcChHnB=x3JdeI&<7*&Z(Q|<1rd|;roc~P* zA2{Rb57ueG_99)MY|Rw&kt|JAMU{Ul|H)9318(OMczetu65|k-qvE zEOIIdM`T!f^S@HLNMiOSd#9_=u&^fEKTKR)X%G{F|GCG%Lp-McuORUK+#L15aaKBJ#`!0W1dGUiUX|0=E4+83B|SAgmSX^?S%t~Tx!RV(qomrQC&XJ zYCr~i!A_2nL+#HKqZLD4shh4sq2l+6mT=lPFiZFf9$nCO7Bz-U;JImLP8`7#Y`(fZ zMz+zvK2F$=Pag=qQ9w`*t1w^J>?%#NEv=RyV6L#g56$-vt7Zc@J6kf2f>&nC1{Go= zH5B()5k`xPCfhBBipI+?zl=+RI@*NR8j>|^AGNP`Ep?9YR?D1H!0-PCR^|T#tAhLr zS-%^ce5KOrWF-Nd0$QNFHZW-cR(9sqDK^+X|>yHvj zbv0aH#arM6;|aLkwtM*>CBS!YQ)H+&?0Tq<e zCHRoro4z%O)x{JGvmR@~U)_6IU9-B{<>@Ex6Nec@)FQEA3o%!gg}|#(J>m~U z!W+6!nYUP*D~fo8yC=St0@BBs-K>J)o4JvrY-F#i^SO$-vB*UJ2{O_`n2ryReALi? z^IPSLs*PK>{B8iBAOm>zxz+<<|BABN&)s>|2x+07Y<2{Y|E5jXyBhpCmDY1wV7z*QN3SuZZfu*)9O3YpkKXfgu*Bdz;uuDdHYsx4L8^>HGop0gl6own`4#*=) zx!exFwH==&c2m$nn=ebh1&q6g!f8y3QzS$C+**xR$tVy`-`O1HP{=gZ7|+jJsXbV7 zm0}5G1*;uMg{lSOGu|G3Cr*n!y<(e@oGhl)G+RU5pCgD)?L|AV`b?y#JE9W9t~()F z&A(G_3S$3}r|L}2BBS7~b9cixFVFaO%{=GVm!)JA9#*a|aJ#@SX4uP%qWf0t6=jMf zFj;u3_9S4jXNgRHWJ-@7katFb^wt83k^8A?HZ@{*px(va8K)DQ3mS$-FmB9{cLp`OA^rNQ=EmT=FpIO2>CE z&dt5+j4B4)e3O+WkZtnr`NDbV!BhR9NFD*G`=_Pde9&8utkwq>gO^J$G+DkZdB)Z-id&C z*}iv!sU*RQPyY*GH7p<$c+z$@^#Iw?xkG@D>8Q(mPWwTAk)bBBHBHgZ7at=_wjpV` zP!s?mJ@QMH=^pBQWt}>=_$NuF^fyVhLbmjsbwDNVOliWgetn@83}>ownw5L+ zcD3%JmN?qwq&kP#Px~HD&06Qgv+gU_ zU$AZA3UPC9Je)v_G(J7xG@Y&ZUIg*?)lsKH5l(-2%-K11K#Tsp)SmNRp!`u{l4C=v zN~CdgbcUL*RNe2MTT*3RstR)^20mVr@L5+tDGN0j6tpjP|Dz#W&yfJZiSecS^>8Zr7<3!9%_`CBKN zzB?m1rvLA8!(U1`!y3iicXt#%8eltzTREbYHjEmjTXM1F@Q}pCc&QR(AGKzedqO$G z#hJ>ZopDn^8w+(D?bYFHl|c}C10T;Yk(E4NWtO#N80y&))!ZBx$K>=BRAyd&&lj^M zEEnr$HN75x&g-QnsgDuBBiBs26SnV#l})XP7_rt*-g@Uw`}iNG)0=!3Od-d^lJY;6 zv7HoOU2v{JDRO9Ygs-hW!4rJ22lRE%WhwtOPJ6@%i78@A-;_I(V!bvhHH(WBmHl(NpUK9kpJWje>n%D{rW2PQ) zP;vCUK&uu-uVtP-vA1}o-8bg^PCK6nrEf9g2jQa~PBi&8QTgVQY6X zriL!dRhZ=wsxmWPx0Md!|GgBC0y$1M>%uHLbfVCxYR6YByG}*t>Tn|)t@^+yGnu#o z5-CPYfJXAm9UU(iZx3Rg4>#QzKwy*n5}5{{#~|(h#~9%Ah>*3A>B@8-pJ{0wiu@zb zuhX~XO}CoDTjtU~8!(LkpD*OiB-xMCrlJm$DUW9Z1uE*@ewQd!RZm)Cm?Pe`25R12!qSogQJ9Rf-T7AEt%Fo9j#%<3KkZ1h+<72@4o$SV2^ z3Gj$MHQAaH*J-3t#*-1k3!FpvmfdpdmGNqI5U0UYJl+#mD8~Zw{*>~ab!~0Pe$svB zKs`QjnVWm+zlt0t(@FErTtLsH`5rdl_$w~apOnE|Vp8#&0D{qEh{r6mMy53m`LgIa zO#uomntKT=G#F0Qy*TV}0HvVHA9d8_-qmQ*ylmZBZ0Ty0CHKPj?^?&S1pN+ueQ&?% z;OU`ZyqDl$mwj}Mg&tfB$cwKP7DGiWzlF*r)E=#yIUV7Tp!LICT;b5S z|Jm!2GA4zy043579x3TB_#CJA3d^*sfrM!w&IN*(!kxo&nmU05dMXy-8DL008(L%z zUu%Q&-mw^Q&eZpjGpEE(^=8}CMTU5436Q(#RhAQ2u}jCnWk#JR_vm)+DuX{7Iudhc zm+@U2Z^gy8liZEN_}iK`C+#l|B+ufv+mK=q54YyVOTU}TP)h5*f`)HZ9(vU1j10Xm z(H_nPluKJ)Dxd+5sSc+Dsq^lK72JoCS81kb*UhYusxH1s7x5%J15O53PyoaY3n$0< zUEOrWc0x!Z_%ztSO!jwXIttx1Gyz#K*?ZI#5TDcN;YRj!QR(;W5sFEg7pWJ#OAF8M zdy9VI9aa|d!9O?Iu5agxU!&+RtsN0=p~y3~x2Zx-0^}wG>^#zrL{o}xQ$}5}Y)R~g zh(21+o!a(2|Ivj>o;=qlTA5wJw3?S67$pJ!O991={(3z{-zWc8YdCP9{j3}z$E*+i z!PQpjawu4-?A6}|(v!a(4;HX`-R6+bvu-jXPx7-e{z+Ir0$&s3np#@zhE;bSz6l9r z91!!a*RBunuz39w4nDrqnxF2TGM<}T*wJA;{F{=T>;HI&mCMybSWB{x;iA2|!Zz&$ zw*UoKt;0ELP0gFgcXH;e|KQ=bcLR?9l~iB!8P9K77F#|X>g!X2F!=oZ6Lga2L)%*X z0-|WHAmA5o@EewPP9ksFPZOJsY7t((KQq`|n%e&dFIRH5OBa^z3!X`o%Fe4vRk}~y z$P|+r4k`Pb$oR{A^}d(*`u}0=F9X`>-Zfr0&{9gV;?_bN+)HsUZE!E{#T^Pkf>S6E z+}(=1yHniV-QAsFC++ip_PgJ`_xX0_3rU!nteMP`>$-pUT9?@F`^)P!GZD=G;xBLe z?^jrH{&Lt8U4^ID7rVUcYe(q>@S&YKf(homS7p$j==}e`dRcvRi;jU7TVBpI_nj*f z^P~Sud+LbS1O%RgZO3#mCd5)1-pONaZx|>t$y-`+f>4E-nL3~O!pH4$`FKhCBOY_s z)9pI`KTpNejH^RU(%Qa}gks{LtRI(XS1mu~(X6djplbnkLSBXt2ndd-U7pho%2~5o zPBMh1N+C&o%3BJH)>hGewGJ1bwUI3)#B1)=&QiV+rzfeQB3dDEt%@V_qHx!Gpg=p= z(fEs%b|`_?66>dA1mCUJ!QnrK;=m-K9pvw?34i2t>i=0{O<;m9?uBpMZzC`rey)A# zVUE9-rN5Z^+~gaDafGd!VC^L? zG?v`o78+CmdtJT#`7;Y4opcwM{b*s8dQz$mV(#S|PQRy((+6H5ULRT8%-vjQuVr0F z9{^J2lCyjY>T-KK@6HtbLY4huI8PZ}yB~%M<53IRGw&l_NcP?qdK?biUAeKr)#7na5I=Wq*X|#LOwhwI91Y0y{*~C)cP)|dCIBNVwvi1#p)ZE`$4c;yjOBL z5B2LpXpu$9JrZwzc{BxWYwzk=oPIM0&wxiE|M=^Sz((~zl7L@>!f*!Dv%W^I1r^X^ zwc=0tfhgOBz6G65)7Q8K(aaR6ym1*mBhNo@__=6l87yBdiigL-G)f1O9v2c! z_NHPsiQk-Yg1)#yjZR&{w5Elf&U;+<)LL@xeDkA?5UhaYah^lQL#xtbpO8Fc?W{a~ zV1bz|P1TV8-x&XZ$Ct!c@KF9~c>A+ZO!!jJxX31p$U4HKMs;GB^mmlE&!01~N0R*p zeV+{@NMB(qS6P=PVI#U!il!~r?2P$xhRq!gF-O?_vQv}B`me%6eEqs~Y-)=C3gtUW5KEv<%-nR@%LyV1EZo8M7MUs=N#7ZT;reDiN728)AXQf3*=)j+A< z{SjzpY8NrLtfK^Gb8;Jd^^SgY)A;G9;Qh9>gU_F87GEb?^^K0okIw2qo^W#V_CInq zA*YF6`=lIIFSsZ^BTWr7V@cJl4d(;fMVbmq%eMS}^P!sMMBo6nA5lHI4qf;5N*PEs zH-me#DsXT1*L+>;}tumn!5}qs$lc$cQ zFJg$w&_Cbw5mr}bjghIy*I*6Sqch#x*~<4>kCn2T zG|qw=L^-W78^gm;Ctoaf)iTDJ6vx)tOaQ{%S$Zrvu0} z;>m8O!#%H?iboosXz_w(i4d<%NHcM-WE%LD#op?l&aNj`;Xc_>47z=?di3SRz~16e zna*@xyWjBfEO@P0i?xq)tlVLzM`+c4d8LA&*hdG-RmHr6^=r%=vQ5XShWBSQbhg@$ z;+zr7v{dc3#Aa@_5E+!9GqEjt=d>&xPR#3KxwJL5(_eCE!0k^|@0|@fS}q@+k9mTV zE3^${Zn^jh4$ITJGClm_v2_~wgS+y!*CS?r_QPh&H8gIHm)|ncKB<3iJ!BxR78Ve` z=P;SgripZVj4c+vw;5QxSxvE25$pjt2_29R<~3X$3zmDB?&sP|%2GXu598J}+n2y@ zWz&~pKgHlTx4FrdUnYe0s|;I{QSw)R>!h4o`EQU!cdphuKItU<=CxRIr)L}~^|qS> zJO!X-N_Q4w4y9^6rMz?ImgfO}&M8f{loRH->m6-%k0qU+44kl#gbM4GUT`-7r(3DH zIjBCUXncR0JPH2 zI}XPvT4Q|Bv;$P*<%HkwpTo7+$2ie>1GQ=Y0MRV`U;oxg9iN9XJOKe#fs}REm!!H_ zn(Qj#uZW6uc<4~mV50p5u@t^X3E1@ou&&1k_zGN45!-?bTT}Ll=}iV&LDCFD6#eLl+(N0=`c}_B=nT!-SXMM7(otg7N zG6D)5>X@n#{8(7$i4=!s1LomBx*_vlg#1s-?5oJzb_{%9523^@MgDDuZ!6l5Zn9d0 zOdSQ{kmHxXea#Yk^uwGa;ZVxy$^jk`paAw(G~mh^_1`Az_VZ%3kMG%7@nnV>Nx(gqKUk;`C#T zK1kThl!m?RdwH=_!D*TEmD~VaTCtk)NO1g|2(e*_7iDfv`;g>dB=L{D!eUKjV-6hK z`1_K*`AMOQ>8hjUf}47M+@KKhRR-;2&q9eBmq!O&gdl8awx>Mc#XJ7cf#*9h!*kCV z*(87FzuNepm^PKme=u$1zp)0IMVRwhp`_&ejskC3Uz9|ijN6y?J^h+KaAp*0OR+%7 zL;unF1Dcam-p(~Gj@NrQnk*$b6!~NLdB^T4*#@yPajHOj$1$*&%GB;a9UcuIQ(7m4 zFM->d_8!?&Gl@|m+GG!`Ph`mcEsvJw*HH7@V8MuT%8^Y(8doB_os`qKvZB^pC*HS` zaw^hS8VF+Eey3zD7V+|P7N5t9S&2t#gW%5ekxl`s$TWY z!%Q?*oH6?M*da%ulKRS(!f-{Ft&LgcuP_;UxPX6z2p6&k*UV1xg!@;3K*YAN>zrM2n;+YE_X$s|k=M{h_Z-C75E$Pnj2=pEXG-eVj*?^v>c>_o{$t3??m`Cob$hW1CEiXkCQ`;?VA zrsEXr61@gM085^NX9FL}$z|bzS7pXt)m#S=@;=7y>(OTbwl}=LP_B8WgXR}jPi&^sECY)V=9+BAN5R8t zmdKUUS$X{bNbvVhSnT7mYisvONp^A1*vF{dmg@o78WneCs0OeY*XVRl1y1+u3j?!@K&z z{^#@*byok_gv?b21F`qi7hSJ!wQ zP4d>`Vy?ZWwf#PCf`24h9qlW->cLZrdFw8|ZIF?6_dM>9T?yKvDlRCC{=E+plL!AR z9%8+v@J~D>_R4bh;mt@Dk@44c_fOjUw4xYXm7&Yqd^4Ss%oUTlPD@Pa{SjObOVvgT z6UL61OnkWiVkP|gujp%Hw)yHF(L7go_}flqW`4bE$>UclKYP5;&7jLyQwbr%#s5yC zKCCmvLeI9dL2u$20fRAN28~tg;lkaJBBV_E*cSk+J3LmY&eb)3mh~FY`hSH33YBqe z4&?8C#1~stKPyt7QeZa`oJezHUXNf3EOCV)btcar;l-W)3U;eGis;eT<|W?a!%K!XueeJzIIzRWIXLfX~Mq1X=g<+Y4jFtMnyuujJq&Y5Ji+J zTG7s1oYHn8;fK6Df---@B?8Gjt|+WT<4MuftNW|DaHuEa|0M|1pOBZ9mL^{QQ(a4> z|1vO@RraAle9;^n@@>qzVuY@F_5S`&)5YY~R>_J9sHSQdH7jW+*~@KBD4pMO6J!?; z=5mKKIrhxy+oBpVg2iB_g?~W$i=f-B zobWp)DpSC7U)bq3_73BueC4NOG*{Y_3#>c7R!vv{7)5J&SeI%l^RE{Zyl6iZwjU$dMc@Mm!Fw&m{I5|1Z{&Hlm&m1!~0H6Oi1ix8_ z-#YP+)WCyF4UdgJgejas|6Fk5x3lr=L=nU; zeW6$9&E^~>85Pq;iR*%wNe{n%@nQDY6F7e~Qw>ei=Pg4pbvPgsjb(T`R1VQhakS4E zUU^$wM8%M3#|g|JT=7WFe%%eHZwjx+Y#bzA=a%c4!d(lKE*2-f!@(SmM%hTn3q$F; z8c&!zYU2O-L0W%N-RL!LNm?I)IUxI!<4gOUPpd^g1y}S#{%K=x=|AM5G_wg!?pO(S z0U)m-zz|={{~&4tDSUD`VZ^zP|J1dxW3LB)`~M03{#WLXzAMG(8JwnZzT9*F$E{3c z-^c#L%kGPQYWll+FFc1|=?_(F4f^R}ch7$DV&DhnCoaWEwz~@(43yJMstmh-T|RDH zsc0+`7iykW;ur)3PvjN!8}eZW){kdV@Wa*K!|g|)Zll-@!?!R2lK-RyT?I`%^em7d zN_xP7l2JZZL%@H7MG)ma+T2{~C4JDQaCNTLNH!}F7|!wfo01P#mx38vGex`Oj=nyJ zBFwFCbH!BVp0&KQoJe#$PMM||u1KjUd^%KH)u~qbZ!pELu|9j7lCC}!p~PW+w<{i< zD22Mc0!37rf4iG7IFtK-Kn8AkgmQ07e@}j{ka+XIy~IKzw!;BmCV-vj^KR5svK@y1 zpCPH)ZkopHivq|#&2>_!$2ah&S7t%@js~kcXvQX{$L?xrd0lO6uwI`$t%KtB{tshw z%JKs_)~=+V_1@%rc6#3lp~d>CE>QRWJlQ=*whQ{Rgw!fgx62qHm(OXCH60!MrxzAD z+Bhb)R5D!?`Nuu(xt93P+_(RgYa~Y-k-{j}p&hmQNw_e=iJ;k0Le>j0dXse)4~x%v5~-$vLd`4BJ> zDgrWJ{%coOjg=>9?DMzZJc`molZH4jF zyME{B<$7c|mW1`sg@l#BQ`_Ky)jYKdCbQ!z7E~^D14L#|y#K^i(afV_GeP(DupfkP zF89JW3}>fnq-?wYWJP6Sn>xv5|5${12Cq9ml{DR9gr@JU0bGslvaN|}DRjkujPO!O zph{zb=0wZhOTVO{qoI@j%_RKk^=r9ZnzurqKADBZj6?wPGA&_N4v~kv=MUkZ`>e2E zN1eHlr69*O@q&+dG~lM0AFUCb7|sClYfnkb*6in(Nwg9vat8cyR6y z!g`M%%fYT-mZ{f(KO5xJEy> z$*wCONj>^Ub18m1szjQdLE13wDCLk z!5=C{(zJK!_*2LArp+a=A%f+4%omBwjVNb)zDV04Z!ic$KbGmu&3;t#@+3W%uXF2~UL6r0%@rHt2J{CT%bt2C@;sNz*@DE++l36(xmc zkTkb27+Q@rNU1JT=@T$dGn~3iLR6Pu7$!M<7?8Um;bB#HX!11NZnIY}j1}!_FG6A( z94ZkTcdGRh#~JYbEKlJb2uAM$d{1J9$jypi!)$#Iq8y(JV z^3)PtL;Uyo^MkreV@a{Ifga@{zU>h5`ImVs*E)KGj9u2S+e?Su{qE)uEVfnUx1~h= zR2+1Yn+IGaXr>QMLhyi>FA$HwgRzFp8t{qiAOX}M(HW?s(DefvG7cmui#*@zjW7W(mV#(9u| zY^DG!9K2lP!Bc9qX~2!ezIQ;*^x&}eBTJH_MDt9P7;AZQ@kfqqXtUZU(A+>^cgB#W zirU^wZ3>U(L`?ay1iS(d{!-`PCB2~L;mf_HmO$#!OG05FO~5olt{>q z7bhnLU*{t3=i8#YkF`Gq%$MhU+aXErl65d+Ov{u;#zB+%kSYo?6>MbHRj97B3!B`I zhCVwAsS|1Pw`dA)cF*9@xN_XAFfhvS5xzzQe8TgswRUm5Klv)`U^ozX2j1Ke49!Vu zI8h=H-5Nz-h^n^KoR%jUKibk3&vAEe89vf4U^;YT6}wQi_o6ia@`SH1qxQL_Z4#b+ zai-?Px>{|SdbP*i#Y-#p;WJ36!_Md+wzd*w2M3?Spf_SVA9s_JRDk@VyAOZI_ zJv8jXCh@7~o%puOyx7iG_Tnzp$81u*c<~KEhcS}j8nfBr_CRZo+MU5JJ&n~;U*Owt z<9UQI;W--y&^$&%7c3;3D%(uD{gB7oUQI!Z`WmmceYBHQ6 z?2Rl-cCw(+G#9cWTd8x)4J|0%&*Gxt>3jC0J2tK~I`)(7b zk`dze87-mQ$)QE@NVq47m+fL#*ImyxL#m!I9|`q)S+)6S~`g zZFLO$W&Ml4{X>ZK3pbm3&!(J+s^=vQ7cyUbbrF}A%61>ze}}+J@lfB?#6SDVpt&Ml zKO;{C3SASl0G5-ug7j&*DsG}>(6itt4;sn1MP+r&A}lRO1n`xE+`R=M;ISDY@~)uU zt(iQ7PMv+^K8Q(-^Ev>KqPP{L;~bsRhr=F1cz#-bNNtTzSb*CzdoJ+|2#kOr*PcV6 z2dNsdW@}tBZg!AOf-MPWCbfkxVj$9W8B?y1^#Mm^dn%wA-(5n=xbW}^HE^#wBWzuQ zY1mS!+S}Hi*+s#sf}3Sdoj}E(cDF$p1iJcyfj3*6$YLtpLTr6uDR3SXu*p@tQd4ZG z@J5|a@cuR%kGVO5?U1ul_H`<>$L&CTuqmpL5^~sS+H9x;wiNuPy8QY*7S{Ofok1kG z{<^2HVUB`u`HMm72mAe40c;?!gk#ag(R^0~1o};;J*8)lXkbMb zLM4Kh%dfndQV=)f#A*Nl_+}pf`nF{w}`G&37h6|RyAu)^;%=$$n#^-!-c?J0m~ZkCeiG3?u^ z4+m-}Sn?IaYiN#LIL4kJ&_6Shr1JnRZ*>+Tn!PSUv-xtP?E;mcL_jG#nmk z4C1+mA}WePq0pbhhblI2j3pkL(Xr%rRBD3WsMzfYqhR$4a+T=9cXBisn&A{wuUz*{ z=$v~6koBTI%@2LAyW9HserYwhsondtXz0_{u_mi64ScWy#O8QYz2&@A)zmKlsgjkH zCB-6&z;NSDU*EBe$1mRyek(=+Ox=dv;u>eu&nPoj5nvmVJ)93MassMTKk%ux)m+=f z)?dbb9Eqp#ScXXwgJI3~vC!PZ#3$53*hneI<29-XP-xmw8zcH71qShxOLbRf!=oj& zjT^ows3SoQ2n?xmi>ch`%NAq@H5Ti*@dM@hP8Qp8JUHv#V|6|_Q)K8d)3{{ImPU!~ zP&twVZP7?Z+J3wbH-=JUh0A0a?rV)HrmiGn9xi#xc7V~$R>eV7RdcEtFV=hy@BL{u z6NiQbEK^C&FRP671yjzCMz%uF0wn=}JxtD71VHP!7i%8)>v~B?{^qkKm~(r$Ik&IZ zz6N`rZ(j-q_I?z!@Xi3*hGK}Num|8vn4cIF2%pePpw`P*Hru^@Ko(cg?IZ+4gqIecDxpZ*laM`P zK+4P7K@5072xMro&Dx+1V&vFjZ`2POm3w^gaFdQnuFyBI|O0`wwUjxMS>C z6sE#g)ggADDB1T!UQV#sbg(vZIylx1E%~iB=}`%avynch#TMI6h8t~e=g>sS)6 z&lv5M)N~o^@Vl?9tY(N_9dpH}3$R;vrJPdhoOg-q0w4#v@16lXb9^MXRq9{)*S6#Y z-+Z6H%`7a8+G)~hZaU$>I`!g#`QMUXyLsu7Xd9)lre+ zC#DhXrX{2J-9-OQc;<1GyEgk(fy3Yn2VAVt*^<~)#?tR|GgaaG%LyOuJL?E8FI6>n z8RnWFv|W6~n!ppN$B(Xb0=H8>TB#TwHfk@hoG>6+Rs7*vfoWPn~55L%pF; zdRmUPGkgGm&;JBO4&bY^n{L6tQ75`haDSa;#2vz#Wlgvu1_FhgZTrO*{)vt3bw>kK;Gj^*ow`Z#F(Mo{2K69#5Ngr-H#;FX} zK6F2&m@_e^g=&mRf8{SR<-fDG&*H@4aJ`O*S?7|e%0b2ozn(acewsg7l$>*+QIk9| zEb03OyvmnIT<`M>hCm4!5seuuoLU9&d zdn7<3-7^4S#Rh14`k1Ox5fGcr)o^~9B9Di4!9;QAcc1%2J-%Va8 zUbR66K!7Vvg5Ngx@QD%rNSc+@cK9&#@Aj`0{nJbR*}|;;?g0S&M=k!BR{eK}6@-dg za<7}ytZvly73v89;8#FY6fSUV|MHbjx>|*iwzjqj4#3mnj;1Nb4psCdG!S`St)~)U z1fiEkqiI|F0moXpIhzP|OfwL6zEZ)8mP ziynh=xw`A$>)l_zd|6*tfL^4fX(w`$MM+SC#&*YZVzZ6mQqT?+et%k9qD&^ipK_g$ z&>oQreWlq>KWkcMX)w{!(g6wTG-WbX=%SsQ1gNOA)8u~o)u5u9H<*k9t*!k718X#w zu5M1(ZR;96o*tjXTV;(o5n2_Htf`&@0Q*FWZV|clecyAa@mZwg4Wfee8158|l`2XY zvw5gLBQ#MM^uu@BX7Dv-N<&RRGWJ>Etb7cBlQx2dmE;p!R1?90jVsU7|?R43%?wmvb zV0Wse+#)C2x^RxL2r8irTD%8KLCtfNXyM8@x8ghj`ZTX>12F(10B)H1ato9+e>k0z z67xW-Jxu%bM~g9`KK)AS$G-vyzKMyg0mHHK#DL+w2F^`%RCHaPojzjBu{F{CUp8%T zw+49$m^2B=$e5!g%t_yZ?$@GXwp^(@Z$+XWo~w*^l&%@pkh=c8Kkz4!9@Lyn#i0IZ zypU%gtXRILIuUNY6=J&o{+>DzpCkTkHc99Q6Ubm9Iu6PeDIu@CROAhYNJs z{D)hc+-<>HuCAUJhk<{dpqspcgBCZDLZkXiF2|{7@gxAg!6W@Qr2@``gF01;zD4D6 z12L&CG|!e;%_XUqr>bIeMJ(B3<7RJC1NrklT+oC4tNgnpSXn#v)uBq(WaWfh#T26- z7OG0m)dTBneUXC*O&)uf0O6)q+v6 zs7gXDK)S?*M)Zw-(N$-ee)k$~x5=x~i*iz;hlke*!Wo!BG9i*70e?35x=gSrT7|A) zlIa$BoA7X_k}4ct)&k;Q38gy^eNgPYF=yVl=pZUUjmhQQR)3n%r2V#!w70hta7)tn zbPBOoSQQAauQ0!+IL<%d-HntcG=&edE_-5sK9(@0&WTHa^4C+F##uAXzxSNP z3NAU44{N%=kP_G#?-X4dqJSG=k&eger?zp(sZg=Gvp|wRwLKRPM{u$Jp&m%bZF$W+ zKHqx-;;Yd`#$ebL0~uT|OvhHFySw!I&@y`%xxc-zhqKinFKFlfjbVvFq-e)M`T#wrwioB91f4#+I zv0$tS2BxZ`GwX6PNe5%k)Q#+D+?tU>b-uuLJGU1mN2dg8&9Nt_mF*^_~0`*yk zb)?K^M|D1B*1H|4hcmHgsT>kNJxrcPkQNXC_@;4yA;Co&C89*w`Z?|F7R6&Dt%SORQ%D0#%#+Q8M<;?LHtyk zijtm2s9{|r#ZZ#rj)ifp236Q32i7Hi)IqSJ#_{^xAg&*^nS+iVf3@npD2iLb5ni;T z>QJ%@K3+SU1@*hN^sj2ooXZvW4$jl>QgWe6@wXkud~f}sgrujCjdw6|2%ox{3ew)VB65!Aw%LMNd#l{Xi#_J`am7-mt&>i-Ajnq^H|J>1& z-6kzMqasz#Ea?-lWn1hb1<=-fxhs0ubjYpx+ax*=*glJgBRX)u^;!J#9Be5f1OGks zN<2=T_p_PpLR6FOLhZ-37n)3t+SHCcnlB0y*o~jA_F9dk{aUz8_I8ADat`Ei!u9>G z(0vlXsUCGl_*n9zsms!YESC3$k=#nM8H>hm%WM%^_4ftbhicWGIHoJVGdGpb)3VQU z)An-HcHZu|G`6&`bPy3zNeVe@`*&o#)q%krJ z9^cB@L}0a7J{RDDjUH!5um#o!8+20|Yn7en$wE9DHO?gIe2L)W?yBU<#yk6*Y{o_? zG~$V?#K7N9+<15k3rnyxGBmeQPTb)~3?=yJ@HskG_$Od)w=^B!b{Lfv2*kI4Y2GDgq(G-qCTWm4_#cG+;1?J#%w!+dD-y`J@IljzyW-b=uV z32jq+>E~ph0-aZ^s_u_N2mI;_^@vzY4<&4l2P_^Gb?kRuh%>(n#cn-wznL&eBT*L zqpgq9Mt08X$Pp9ISIpkIRCK1+h36I5+wT{OwvU}oPd+{hKL!V=UyWEX;XmHjJk*NV zcbh(pOCTif=l|!Xla|apv3Vsgiga$%CJr0uESd-|pths6j;9|P*97^CI#4!0%Dk=% zQ7^r@TDT?A$4wI5ClK}EV}(ez+$1wNrs<@Q$}yLqe@*BcxUHe^9vxlY!M z{Au{Ukmf6!EOMkkAE?;g7KNTz|0HxncM=P*5frUmhq1*7wQJD%tOgO6Ide0GZ?_O$z7(f_=d@lyGUbdqEX z_zefABxM20Y};04m(1c2{FuSz^&>CR5-Z}oEy}dMzN^|jz#uH1NBqhG*M}Kq zKS~)bx_#6fEuctEZIT54H+P7 zSTC$8J^;g7#GxL5IGG%8xrd;IbFjkhk4b9om3ku7%G?fA+XqY)9;ef6a6HDgJKo$J z&dXMd5`ZZljN8Mu`iwK0u^8gHDx*;qB4E?2jU4SfeLZ>~zy`gGf)D$pWI0U}6GP(a z7k6jtkCCF)`BiiYtdh&>Td>KfEA68B=VZ%GW`XU<8T3pW`%G@PlU<(IB97nBt>?Ss z-NJ?y#cO&lO8>MGG}P+s;38Oy;4{C7ry`s6t~|y3WM0nwOW60O$GgYW&hG94MQ0zN z4^(2j2yVWl9RmQQFs8|?I@g{*FLT8~LW(J8DISiSLhE(Uk)h)X;EOjbYrjsH$r6^6 zM9Wy`n{PEZ+=Cz9J4|^$j2ZwSAF&zHFhcVV4i26nJM3M;PEK9|H=}4PE}fesvJ%+M zr}~*&EY|sn;6Q_7rp6UN$5WYr7j56rxHw;vCIpnpePdICLVXr)`F;`6(=W+NR~6D0 zQM|LW&cYI~-i_&gHEXP^s|&Y?;5}T#R7JDKsX8keH3C*V>er!znUl>LQEZ9QY0-M5 zKW@}NhRdH81=rF#1ZKWDSs9g_=T>%g&@qMB&|QXk#OyXh>3x<$R6_^WQJei&9!>qTP54C{*I;8ab!8`|bA#rgTiA@`bDujcqqc{S zgM&1MK^&Vb)67{Pu6I<^## zQsr=EX83e0D0{R%*wXC;_11Yh818_(9TUO9t4D)m29UpB45-R=w>KZl#Ph=}Nqd>T z3&P_T|1U&?4+~2JV)mu7PSUTMya8e{A8Uz)gt@;d9aVPzO|#sfS2Igtb&~H~5JP(B z?u>bV+b>Xz$Z=(RpYD2!8IADbelPV&apy>v>S~Z&Fyv)a_JJy7MMTrg|MNp@+cN-7 z`^&dK74_I`a*5ReN|4NaPdq|%tk#TJD`9b-i>YE7^s>e)N#+bgZ(aX^FSW#;kRa`)!x?kbxwD$@JEwW1Da$ z|FulV-CemFaI3#Hq{yC1uj4EhD0pY`EiU!v1vf!;A zAMdu?+E~a+0_+nZCwD?JAPO=L{l@hkevKrT1@zDgYnI1$VX})?4kLzC8m=U~xDWWZ z1M@1Cs&U zv{NP+>+cqi1RsHZTnUjbe87`JBZ4UGVtNVe7}&&_f^#p5mn2o?I1e)z2JkO&!{Th zFWTbS5-RrtcyIBK9#wqmm5c6{I||)KuN)jt<33y7$|6VJnWuW_vO%C<;67y9mkD& z?{xc@5WcGI|2}jAU9CPN;jLvq#{Y!txM3&LSsZY@R9#0Oe!G?uIh%#UZ8_!xRx>i4 z7bj7{z^rJfHJRHEl=bHgPOe$akTczl{Xcjb@N^e;xBx5>g^Z~syW~xZmS3;=_Vp|L z#kt4He8$*GRXo72q`VIqV)CcdN(x{5e^clj95JpfcYo%p2Bj$);L?5!xPhhUA zG<|(EED6Cr0c(w#8JoYACEs15zQ)Evc|&~ra{n>>?ygu}tJ`S|&@!Y2Zvb!Vy&^tm z`5$i(jtDK4>+*kw#m-v%p#Jr0O6BfA%04Xsua3))k7T6BUqz!A&0YwL-p=mT!NL0b z<|k)xO#Rf=oi=8Xds)n3$V17$H$QhG0+XK9lUn_BI+o! zK{t4+_iW;=w$3gdA@J zIQ)F4n!=Hj4HJ*uAO8TVJ9ojXL*q z#^X#vV_`BD_nI{#8WSLpKXMGyY|2}Ur?FO+$KoRlJ~wqc*A#!TRJYq3A1+3eqm&VB z*!A9;xSjFsy_2K@h@V%Qf!aS3sNpp6v@t#o;FFpLbN%qSs;3)eGq6N|r9^m=Mjl$_@h!)t4udAUp_u;Bxmv3G@uB#~Nj?;NfoP{ed1 z@Kr$Wr0%4jyM9TAipvhLgF!&Y>uknFzBji?@~L1A8(v|R^%OFZozXor9y? zaz?2!8Ur!e808^ujc)_Xhf|m)DqN>!-di>?5E+l5Ssp1f($ckDq5C|5r8*C$b4Ok}F*RyF^=<~_6TJkp2zZ*jjdZ{ny)k^^y$mDF13;Gd>rN|rDqsmoJovGSOmyDtifL79VfzBqZF z?|x*qJI0y7w!0R$DPoOvNd6UjkplMfzu+#e)%51t?YM+Ej7R~uPVb$qDer*%;V&#` z|4?1y$MNA!DMYjT@!@Vufm68Jvo1clHXQ{xykgcz8cloUWx6Gt+UAsLgE&F5J9cB1LS_TSgx(Iu5d8) zNO&PZ|3adxQ%O03%!Rfl46mO3@~liBHBw0(sCP+p>g4kx1PUo0auD_*qID{pu6l#Q zM;zb;;zOp!L@B%N`@H_J;6Z+7tM?IidR;=*-|Z8xbv(~C3@=)U;nI42tB1O4smeYs z$1jPPCbou@kdwlar|l@R8VMIt{0UrcQxTFCuP!21klTEXZ z044RdsRak9*t65votTZzr1bXrH$_E9_1ChUy)u*AOez_fZ*WF%%(lf-{5;#9&uh#i zR#pBRe@BwjEM8JvNYf4;>^(Ilc}=6gS2e0eCZ#H^XXF(?s8V7y$d~9RK_uBbXp$8} zS%6w@yP&+3;*=ygNZ7$XU;3Lr=ta;>-Yly?bFrrGS1XBn@v+t>X$@q{!L(n6?PeO2 z=KoJE=NZ=2vh{HkJc={}g3>*pphyu=dXo~AA~g^QML@vNr4!nbB0)L`NS7uhgcv%6 zDo8I<5_<1JdI_C3dhWT;`+A@I@tu!*uf6x|nLTUPUi16U-Zjo54LcGIg&HMAB04*U z7{0xkbEoMV;0ju(LuQ?-S=PUO#>=pMTarDAP9b?D z9729Xo>&TE=~LghQwY1+ELL^c1%Irf6!So!An4xHz(%}fVJ;*dq30gaoc}(oLtVA~ zMW-dow5j&!gkx!NkC>={O*^PQSoKl1I#4>ba@3DMZw!c4@wH zie!%abYHt9!Cx z8ty0!Gi+wo=Tm$o0{${eO2Vj{7>TP&`eJ7!lqAkE$7>Y{zuh|4pKoG%eB+w+{f>6C z$=Wm~F?s`^pjX<~HzziCsjW(rIL!?r;c8rTs){UKv;YMb^7Si<3nM&YSs*X3P_WVB zqljf*k8;kr+Xb~qnr$x@K*L*1qHdhtDwHtLxn)zmV)-bUSFy!_mj$rx8Q%W1{l@Bb z%B2A#1dHP)`Efv%J+A)H-F^alaJ79$U{3**}`gk0VLGq1?6X>Y;<66)D{?7k7F z9W1dG?86~cD&qeB^H*ME%srglz=N<-p)z3P8+1LbArJD8RIN;S!#o|!%S}4-O$9|O z0!s3#i-KU3pxjDFsT&3ffE5qvs=XED) ze4gu;qSJ>471Z2ev+xc=k1?rLl#3MYNGurwNoiG%C}Rp*x&Z)NJ#>|zeozcut3qFCmRuS`eD<44 z>AZ+5T@T)}uW*OrA>%TsHS5hbac<<}4UF|?DLjKb0-{g__Dw3w3tTGm8oPtERLl^1 zq4QWg)>8Nvb*J$qb0Ns7Bhl7FT7hC&M#h7oFupELt9fZn$*tp8yB;eh7WsnNEq z>HDVV4=*2ULgD0FhFkJlt&&2yqn~wv&3Oxag1Acvghe{Q{LIRtrXXmJJMmR(f~-W- z^0pYqI{+5egRo&qMYnXvfbm=mIUm(p;Kq05IReL8VybL(blFCp@ZdUnz9BQiBn&!e z1!-lcMeKhZ;eX`E1rx_RE}L}&gDhic`@8?fhMl3L%HAAdD%s^Qzedei79Hu}FKqPl z+Fnm)C<^Whi!LR^xJB2%FjqQr&oil4Z}C`r>0QrJO1ePSK*nFH4)S?RO4e&Ta%BdB zpS>DNrg~Q+QZp7+XC$3`yb}r!>dnk7+#T+0F!X)OYx}F~B-k=+D$ME&)3e_`x1NQ! z^m52B_Jp@%;`6$Ve7O5~LOCjgjqbh3*lrR8Hit1%SqSJtE+$R zd$`o6X}uP*QD0b@VH*0NL(a-v4MrH&?2o8>9bK=hi;6Nmq3)EAt9trZ5ZGR&r0J_m zKJKPRGZsNNXJpBjvs2u(;U&Ro`UB7Nsy&qzg>z{c{rKlY7~I}%!+kO>C0=ujO!rTX zHY|F~CA{$keu?Yuiwq`xyJKsuvos^Gu$OnKd?iKVz&jMQgOs_UnCBKkiT56hP=LjF zPys3OnFGEbs6UYcjPe^1CVZ!N6H&Ca!)l4}$Qed(?LAZb8g z;hLf7p2GrjYMN{52tI()(we_;ab9Hnw3ptV6k!kU>z6b5)Ga*feSBOot80_4pujFj z|KTpAh~`R#h<`#3E~>_EPk>|q$({I%AXP}T=Q6X{|)S~hwHj4jlQHGb_o`Z*S2 zJwVkX?NeuT=9yZr$>=YwpSjqcS>dS~yO}y%eKoj!LYn3FT)2(;QqPYe7GZ1ecT)Y$ zb`LYFdVBfmbq>jNGb>!zHf;J_E$eF%YyYXYZj8>Q1$7oI54jO5Lir zhuM%~y87U<6$MJ0UK^FZd^W#5E_>=LTWLp&5jt`gIwI?z@R3A=4kq{~m^_(1S3QSgv? zlIUMxHJ9_825A!I@kqm-K7Hx^Fp2>yI#MznsP~{ZRgbAEWJHfp^NG8FPSt|cGl2X% zk#C~m?5FKCVuFo&k*UBblc2Cup8F&sU=q2XsXiJqW%D**6SY*BAOUEV%ycW_(wC;o zzaTVEIki!!|GI)OiBu~yZyuji18U0@eB4lRhvbkjJIa?YxHA7QIn>p+AzXhay*zI_ z2@^$F04;NK4kGh5W6E&zW)Sc%Zsy{D@oje||IQ}NFudU~A?-!MxVLU9m_i4lX>9Osu?Ypy~{%^>ShZ}%J|Dy`51nkQc0MG9` zvR`dU-s)}i`smcwl-(wwjhKPk`ZNO~=1LV6TXt{PKJj}5Sk1B~Bo|$5pn}tWs6;7> z4&Q(1KwJHWbpCTo;34 z(J@|QvwkQW66G*E8PX>i4K0zV9Bbw(oom-e$qox=Sy%S1H;42}rrKwY6@F?Ek>xP& zJfQ^&NWm|)QY&*aZo?! zA4);{NoD4{Ukgx?#Q);jX-UZeH8&rd@;N>E^7{w9N5{@@_7YKjBmTSLNC{l2dQn*{ z);(YA>^4f9>2}Zfa?~%udIFkb0B(Bq*~_=9xXVYaUF^N!Lxdgr2mhb9$?#V!Rxh@foj&|eTI@=$2KdrZ@ zc}2*QUbQM-8LDmH&#Lq|b(?G1?y*ow&;W0F!&ivNeOo%;hzCH#x@Kw0 zQ0n@X$p|O2s69tp-S>PF!WVVMQ!a#z!?mqreO6~|yzm_}xePyQLI1le&s@$l19Y8s zos7t#^9RPzEpr-2KPcn*SSI|%$1w#&)F{9_$O(xzVzT`$ zlr1uGSprfpg8@e+eT_?iqXWjlbp&IZHsvhxuE^YwQeV1psgEQ_rhcOJ>AjXmY zIbFpqS5ZW{UV%9#J5fnGp)zqBF7;KJVCz}u+y`Ii+o!E_c}&#%_+UB~M0Ft-YP@A9 zXR&sTx{fjr*2ZhW;=~BK+DAYTFvF#rA zu5_1W$i?eEG752JpE`dfQDqMEa&d9k@*naWJc5@)Kke}(+;v^h#GoO)%Fk$auAY52 zQlY1q2S@27O`}H7?#TLo93M6`vVvDo!oUsp+j$qpmChV=6 zL?v_Ir{(%Q>F}!ez#BVBJY9668`Ei4tl~%v{@`?BS;(VD%qLZPQ|e`H)L-3%Dkh?r zYz|BzzeKW;@&JncUSDeA>m8XYL>6g6IbRr;b>vIB=a$=X#{qDTJVX4m|@!Z(Tpy)GtD1su{omw_(= z_EBO{eMx1&!1bR_Y=^E;qyH)xdbN!SE1PR>YoMtV#9fB7Mw=EVa3=L8|g`V)4ZsV8TS_$WA2 zS51oX=La~=%?R!)cLj^c+Wf#j_TIQs%uv@k+z&_{UzBDwQ3jrYn_0g9Z!(_vDLJ zdk4s2V?_a=aF@!XhppYmubc>d!9K0!3JZ9z*%xYjZ{9tI=Nn8N(lAY;!{V*VWE3zn z^~gah0^9aM4dIW}i*7^KwNb7=n~e6S_MA!vsB7_9_L(G)o`ZlYtAkts^@9<#!#*ZM zA9*wCmW;=dEqfp9giC#UrrB$SN<1qk72_dq>FTRPVVTPX%F`9y36L}I6Y`?+#^`Ph zLs~3n7oULXm$l0F8-NNYB3K^zluqsmONa+a^Wc3!K|lm?mKLD6eqElkF^=k|Gx}M6 zj}Nv!G?zj&S=0Xx8$9p)Z0BEvILLmL-+B380maWVzZ?9Yh~s}7{Qk&)-{Bu7|969b zuX6rw@E_3rKL-Dm>i>^g{-X={cP0KGUxTo8K_+H_v`AK+{n;MRm0l4Af3|PA>HuL`+eVkxL~i@ znBAE<=YF0$mT#)cvgjzpC;$MU%gafr0RX%&0KnlwV8MID4~x3N8yFWgSqY$GjPwB9 zKr~g9l>%P>`}xsclnCB~{8>)d1prWS{`-Xi(lZIcJCR)Fm86jt5pgjPVAu=fbpU`8 zke3qI@LW93^6f` zY62rJCjLRJfI;DmAudkFd(EyUV|0#j?{|vZ zF^N2)sA_X~=9v^Gxad?tDwTT(910GS9Hpy?Mpq0KKO78ksgtOyd8> z=GmJT-@8o1=^_=6))iEI7M)i2lfiguqOkZUL+p8yDKbVUYBd?)Q zhAa60-r5*-!0A)1*WpYtUxxiufxO46*B&Z+o7jh$uUOPc3bYbm3$4=&X(iJeju9 z^z0|vyr$!#dqNwhve|n`E+1+L8(B9Y@VRyKwQfhs6y0yuwsQviqZs5|hY}(oRBoaF zG(+^ffo|nEE*cyYF*r1YC2Uy9rcZZG^sSSuvg5(#%Pb1O-gcG1)2_Oy_tDk`W$2F0 z`@-YBGrHFcEqZ)h6B~5iH8dmyjhJWuwDrtgAj@OfN!H(AL>xLfIe9c!J~B8MH?kAp z5D@QO-8(B`II7e6ljWQ-1s~~yVmFmPEul~*t`#47tsWQv&m@*DNGC z2Mv z59rlzd2W_pL5ENkL=pS$Ar;xf0-lkLLPBq}LenwWAA~xo9(U?E^$h{dvs-O9AKn2U z#_)?CSyq72@#SBSc8=q+`}S4gyJvONBjw};efpsa!N=}Qk!~Id2$lUjGW(N8{NIa^ z)SI>yRH~$wyzg*MVQ!ts!yZoyrn5Is4#)Ot!vM&;1&gI z29e^UZPu+qeaVhK$$rgGMY*Rr^7#(|=Kx@QxizAWT@Rt+Vz{2$kv&5Ax7)Ueo*PJY zN#FMYGUVo5py6H{(?ZY5`JsrxCzn}X?F;+9=7#e8i_2=bk+0j;K3eX2WayVScuI{| zm#wAzo$rh_%L|NH&V3{eKYe9?k#F%e5^LZzQh);-6>;D`#r#+Ug_opG(DWc z{LQ}fv3zl!FCV!LU90we=J(s^r_a{C|EEsv@4vkvIL~H~JaW}+IrNPv2@yHC{V^%2Sc8 z^uHTpOTDNI*f}d2dQxf~jv}r{p0-=8|4==)H=a6>%lM#V+{yXcN8R^^T$n>}d#|tvSw+;-R)+VCk#k>@I zUwoG()dkD#CcVOIWq|a z$7|}AA6{-L3O^4Q>ut|g+clZ+!~t%Ygu+Tb7`IaDz6b~Lo@BOhs z)8~)w?l(WgzxY32&m*AXw>{r1+3%G8PpP6%xV6(+dbu5~x&8TyALP)lE$Z0R9oQ7Q zGPOw|&~&`Qjq~W^6_}UP`N-*P+Mk!TlT#EjgtbsaE7HWIz2q^rxiqcEmZy5-y|Es@ zPhjjH&@(o^ws?E5KMq8s-K;jKoE>EIU8*JcE4<7PH{+RC&80VYmf&2s_o>lbwY^Ru z``KteJY(VO1SB_ek<~d!JH3pgmTLxuK9eb=#_b1UJSwJ6DKW>M$2~lRdEiUuH-Dt> z`Q-PydaFqR2Z#->*v@q<86%qS;dG5zl=}3_BZ|cBxZ4}{Ilyaa20yS)|1s>;_ zI~#%y#?UiTFx8wqETaC-LszCRc5p@Cjb{ePyZr~@zef zmNgPn#&XsOQudBxWMWfys=JWeQq4+7c`;IPsJr~n#_{p&H7%b6o0@;r??KJVrVl`n zZ?Ip&$HK~0D=t810D!>S}A2Q=i)X2b$Z%7~wnUMq~^@O7H@@(Ip5X{mXadDbU|KXv3cnpLzgmHN+I8o#QY zxFx;T9QFA4OiE{*Wj)Gc%^&XlcKX=k;t}kyq$lCJX?a{OGIg$#owY zUh&cL*G125ToIL9G7aq4RkZO0%zWd}fM0P}Y25T^QO*SJnYA;Kv=um$2&1-%Uq3;A z1F)3hU9drrEakV>Q@u#!IzsNp3qU<~u43&ASVH9!e(~y+YgNuCZl95^PSGptRTQ+O z(`t5H_Sq2N;n}aWTxU3Tyz|_j_=ZD&bFy@MIWDTLrDYV&a8W;56u%ha+crF0Hw3ST z#03*LnJG0T`%iduM#pQr>f<)G}^Ucf*@<2|v0vG9qq3;20yiaf(FuX35_L-!Aa_O5DPS^U1ESITmAqCVlWWJIpF1$ zO$$LZ%NBzNOuA^~5s{E$iFqUP7$WK1xA1{dK<+06>1qhl8bIrZw4WNHC>X7oizBDyu~ldxT%>Ep}5$-bF31 z?d|iwe7*9Np>6WmqJn~g>gsC0Uf&S$sE)5t>?{+VGN=QdO6TmH(aD>$$i4`BD)|sB zs=23xw<=b6-1)zL4Q_n4TWR5CVq#)sJP`Pa@%LHl`1_`8k%u;`)wb^Ccj0U&zGF>WtXXgU``9QRstNnt3PrjEYtcL1Z zmNdmj0W0{QGKzxHEubw~DlyOf+^==^dEE`rk?O1;f_S@YcgO_IEC^Q>}yFX!AN+0TLLd4e~) z3G9(Q_+&zjOl0D?WKp%Il6L9Kt~S3m60pV35q$wsl%+a&DP($AE@bK{Rx@`}}=gUMB^V#WusB2>KLx(wm1|3n-+}zyG&Q3}y zM7hXz>kZC)!QYdc;o;%mzkhFTnwHKk(qU};qzRNu&?udCZq6*Uj@BXp2_i9Z$)ptn zIGH0TA)G447$;=LD4{|uEIjX;AG?C!WMfFv;{O|*zIXp#o*(IHX+h@1fwraC|1#t(V1>x$2XBpEEKyu|FrwjlMUlov*bQmzBmOwx;H@Mcha5(2~Y6 zMHLF6*CQr!fOH_6l$YOc_yU!@!Ff`sqT6r8jyOymrU`uDXd-$Q#T|B1PI+4a@ifkN%8ofoOO&hk&W?;(49u z(*LsvQ+d4UV0FH8$PEJ6FJAjs3}`RLzjYwb%of>bUP)#>984Rx?_AS#>WK zkxElz6eBS02aDb7t+jjl9F5aNt?$;auDTDlK8d^>q%HnVp)Y&a{I@bIsQlre9jP!@ znF8D!zuBVj>tB7k<~|yl#V9LR>NVl3(b~7P>78DV_lIA$f3%7SJ&dk6l~iClEZsj9 z>|c%$jHdgoMFcx=zt(PLZ4g@=u0$g4=ej-~jt=}uE&LakJ3UYK;_4)$efWE|vg6L% zIJadWWPtA^z~?)D_fMKN9lFlj&4phjn2ra6uIC-!t%u-evfk;+Hx5n~;bqa`g^RrsIDe6ba@;x$+OtrH> zTE0F%okv2vJI9q;x%!L}tali&^mX@+IGBL!wCF(`WTtqvr0^>QNoj$jxzki!%@Ad75goi~_h<0dK z8sSQN=~VWaz5^@%Mo)OT7IR%>suc&Jnz}j;Hnxz_*oOPT^~2Y%5ELu~1ccpm;uMRl zvHrZgyqX%W*nzFfs{37(1R86uv@mo<3jSXKQc_ZJoZmk%Sf?Yt_iSH9Y_0F;=qUJg zcXcogu1iKHChRXZbxGYPS=ODgNr#j@nVFf3>BNAi+g~mx7xVc8+C++{$@r`7x6J+Z zYetg4R$ELS0-Oa!>+X9PsS36h5);Ya6=Xkj_djYFuW}ysm{Pt8S3b3ckZ}k3dHF}i z;tVzJb|%s)rgPC7x|^;PPTO@_&UL1EA7>X=T7D6_Z;<^&ui@zJb}estoag5F^QGn? z^ld}(6tep6ifd2reIdXNA*&7rQXUhzfO*g?f}fy%k9JWCAXWil}eEx z^#}*E9yfv!Y!$`G;|0vc&StXx9tvqg29cr11P!nA^A%AAQ`-0eTKFP1FLm&@ll4cj zyuHiK7fvT0l{`5d4LL`nUL#9?y?T~Qii(5+e)FHcv_6zF``?|FJbT_L=CCQvzjpdH z{+v7@F4F=$H-4tNpDfY}-_rh{GG@+3z-2@lT&lrjtPz#6^+FfUzR9SDPcgsKPvZDz zWJJ*OALTfzQDv%yZb)1pg0TpVe8Yt*Rl)SZ)WdZKzmsWBhcy-!7GiV?jXX4t5&{G} zz9Sm1b!coYD`Vkg{CJY?M!;sU&Yx?cG-GpJXFdJt&-eI*guGcB#t3XwB_$;?0oQ#H z({GRe;*gRG*)P`p`}di1SJug?8kFd2cWXiY2Kd2H;&HaxxaRLOo-N!b{{ZF#ooXMCDgR{E7s-isN&(|$_O&}VLuXmkA77@hkNe(O77`|T zoeqmBMjkq2go3g%xuE-%k~;mszyLES|4{<*ka!(2pzGX794f?i`){KMeAjmW=Lhgm zviFu4@;Z4SG&g#-WI&XNsuMb#S5JXuwm*kFhTTvLKE>b7dY4-GedqB@OeN$XlBmDx zMnX5U=PkGZ07<{=TIMvgP_`LwNql6L*6J(I59@1dE!UW}U!JL6SsbV0LG#*g=o&B< zX?jRze_Y)X98P(ys<+3|ug&)CNu3QD!_%Y)sJ@wb`Mo&vd=K!5oo}$XtW8nlPsC8* zx%!{XF+m8=FMP&t=PCWwt_-r`cd2fv9A%W90rIo5V2dRLhx9b6)ca{D=%bF z2$K^Bq+ow{U4Sw+MtD{pL$bop$-x%)lM+=1(&&mFW$>z17qeMEF( zny~>KDoF6h?{QTVAH5A-?V^@WFfcJwM|L&~+CUiw1i-VU+uG4jTV6(NsUD};1Z&NS zOkzPW03=jm5=7v;yE{;+iG-~gGQ!1MV)XyBK!S)yS~m3y3%29$ffS%t0%sKrO)YUd zNPv=%l&TeXyuLiPvq z;l__9O5MlUw))>FK=X*BvhnIua&y=3K3D)A9svr7O`s5qt%ow}1#)2c?8o~R8S%G0 zB(tux0bKh{khyI|8Cf(k;x_l5Ns535^Xigr4~#zsZYFEpe#yx{h`0sjGBihFrEb4| zpaLl2THWU)j0``J?KK)Mbi;{l4Xx}EKVVKZ;1&fJb(?D6rafNt_6-{9f4&)Pc_Sv(bS}aFd0T*qB+G6J8AB;27yuKX2dg(l^2dayjiP zR<2CT(vNPY#QpDR>X1rxq|0S!(IaifRUi-i$EwSf_MV7{=$+TSeuLcy23#Jc$UW3x zu>>ifZWFFFnJ7X|ldeD@n)ni&9H@jG6}2;(;F#6W{_jR}|M~&E%8{u3dHMOWvE*6F z$(s7DJv+|d@)S#FZ;s~K-@Qxw_&vpm#WVZXyIlx>+t~{Q1_bdR@?f(W;X)Vj`~Z_%MCP~>SKK>`ts#31jol*4Pt3Qfp>$16;RQtmRrxyb)NlD2- z84?N#3KBAEQ2U_!dNi@^e1Gk}Hn6j7#-lUteUsS$* z5<`sky~|xGGJfaHfRiqj`ufJy!Yj2VK{}eybh*mwQZ^ZOmf38^%KJGxM`STjoY&ca zpuU0XYw%`?i*!te1kcyT?d`unuqhlP!_{;};1#{`;rU4q^G!$l>F(++ol#3z@cmw}lTD_E_N&E)wkqpNhs-rmj@unZ%wzj% zF4fG^!}+m=t*%LG0=66irPOS}=GREf26G1;xBXqCtuHjQwC6~1iHX!i;+vHg=bL@6 z>uEh|1c`BXmag|B((LeJ0PIN3Cl@y}Gq|1!2(J>?wpT}Pt~i_agRd+E3$(@FuKBv? z>ZZM?lY}4(QHpKyX%j-CG9>HZ5JP(VOvqCei#6UvAt&H^HZ)aP4a8&(#E>zwu~zG_ zv{AaAEdIw`xwzC^lW6JODfsMq&$s9!BO;{y@JLBn#+dAM-5Qs&j6a`)js|%wJuh#) zVk(tO(;NSS9@+Q%7v}*kqQ=kY$>eeM2U~uc{fx|Wqldo`>(mfcJv>JhdZHUg ziA^tD=o1U0WGR;Yj%?{Ow)F1UO?!mPYZOR0C#vq31YlY)l+aFtTjL*<&wsop$@n2r zvf8*}B6NQetFkt-wxDhJ)N>>BL)|flc>Evza~(4%ZDdoM7CAQCP7}P|f7%=-iXZhU z#S;HwiJ^P{6eOwmX>27MKflgc$iD@?i^IyZu1va>hj9=c$ecH07d*dwppFKJ+2>0( z)LZUrxsBV8`a+pql7%goUCb=8VGt*z!Wd}#XwSnZ>ut3`;}ZM7ITqK1Ke^*mw7Rfx zcU0NAYy4;S=<)u|E-xQnwt$;L{-oR4s$p?bBy3>eK~ho@qk5sarDY_E&o^>Tap)^b zQy*UsFE8&>qvHUf$Ki~8xt1;LjL^u*f7dol2`q%0ZM4pt)Jb6D$W z|MiQ3f#FwG)xSA?$CJ5o63I#U-!$@EXo>0Riy(rwwF$)zfI^Z66JF4-Pt-W11Mf&N z^d;N>+Oe@$vM@04``&#vFd&uwfQJXV6@7MmOD8ueRPTLRpB2wYb^nYymZ85KKaV$1 z2zxP5Fi847^xof&68)kE#L|3ksc{t67X8hiWi7rqWD9mYtdME1M#jdpc>U!XsJU(M zt=>da?LWoI@qEfYRq&~PtxX$CETSPZ%JA}hRLSZezhZ6y2G==bg{yq#j*8k-86~>; z^)2vHCyg&pJkU43B%mkpIq8sQ4kzD|XZ(T{gK4Mme%0*pp$KsKSE?jrf5>@WjdYd< zRX(RBtC!FIy!71DDB6COk0%=IZK2^Zi1p_4f(vub!@m``wH%Rqm+1KqmH$5rKn0Y? zl6X#MKJFHlKuwHWG5R}RCbD)$MDE>(vRZo{qsj4QkDI<{7%D1TeONkpIess#6mX(+ z8=z%(e=$mcKzVH_vvNBKSIl%^O;7c|uP|i8{{eeoYFL1 zqI~7NEssE!3@Y&V?_WGTO@*Io{T$BCLJSK@@tn?cWxD5Cg7NUkgo30M(K(KuJN2oN zk&%7xK}YLxH^UKwm6hvnxLw# zPoIoDPtz$fpObMZc{B~&cd%5qwUG=AD7nOy=!-K-n@o#BV%kgY`4nnyQIVqDrOJ#G( z%-U=LM`V$G3E;YPJ-Ec7R|X9;p>Z|bKIiN|fBxXmtL%bfdmBVtuXa+U>1gzu^mO8w ztsnL3N0_sE+7#`o5}S@8xb6QiY3b=@z0ujv=ea;q5^!@RQ5-2R_XOASlniHW5>;AK za&xvOYV|L0ecdF11}1RNE3&V~ac#W2yIUsz!!m|q5)x?5%Siq(U?cc)&3lrl2+gZ4 zwHRX=Nl8rwn-MH{+EbW9tx_a(;u&sUUM{YLkA!|da(!-l7Z!BZw6vq6(eDwwz%|o& zQuAxS*AWJF)3*~`aeQ9Y@CNGYE{DC<2?^&^RCK|V@X$a?gj{)hDzQ!F0ip58jV&xn zCt*!DH#aY@W)W*Z`%bN~N-AH4MkTmH)m%8+g6X01{Sa};Pe@^hjHAwtu8;reOB!@|P* z`uaXbjee>H{kx^cv)*rsI5;?@4x>t=}&a*Vb0Y1C(+#jIPw; zpua~LEt5dQihmjFb6aq=FJd)8X%UD5Kted^;53Nc0{LiYSU5ScsIV_33=QqqI{f_a zHp1bMFt)e1`Lo!fo{y_oz~$~_$O%rnSK52Qvo`9EnOBO-d%={K`HNed*2)Gof=A1> zjn_anuN6M=!;AU&{flmnKNGorv$jwE3Be|`2~84*~i7#_ZO@Ye72x9$nY;7)rFfio$aElzClK*ivi)3@QI z3<(0FsscILA$inaC#`#qKCd$RC&QKLALy3;`Kgxdv0;S27*o zgE$r;M}Hs$ASETn#>Zl7n764nYWm!5$A<(DA4l`G`T{vvm4PsX1GXzT5PNbySS$Y1?Ae|Ell4S-BNd99BR=Bcw?kVZo0yU^|=osR&vFVDxL415F}W z=a&InY(xYK8k%eC3RpV+Q*pek!*c&Oi3R@c6_s;^0s8qlI5>ECG>SD~1Dk5(mirXTI0R~)No^-Q!=$1Xd$mI72$|sWpb&#W7IUI_An;--663QMPR*38ihGGM7V)@ffPut&)Gle4ZNR%I~uQ^;dnE}q}Tpc(xOgXtq(r= z9z+`Nw5qOm`51Y6YFK3uU)GDZ-%Vs!zxp+IMzr6ZKY8Wg-@{7vuXJu~9c(C#FowF{ z*RhWfC}meJ`|d?GUi4W?KCR^Vb1=_LO%B<0A1$-% zoyaLRh=jHEL4~l-^usj;kJtbQnm9G6DSZQAVBi5LZaFtRaWq{*fmh5=M%eg}_hVl- z-pWZqf>jOeMVz-6?k{zNyQttHK~cFVs27*)y8>A~JjA@>P@~EvJ#EK&JcCyIu^cLhSRtJV`kfxv2j zP@u1u&h4loi5Vic=_-zm#T9}7)&ZZtqoJXpx;ow_COsC)MH~&nzL58o)u%ZxaUg_} zOW;kt$M_GEd@DjAgtMZwac6h;Ee{VQ7=S><36<#T^~z@tL7l6- z-a&(VW6{gOOA>7D@#tq4#FGX0tCL%{7;m5H8OYiFEkQ?>&H5tJ&XMu6$l<|w3ax;< zXuaqCc)JmaMN>hvU7G%y=f^$~#YHgvD&VlXn6n21xbOM;t}#bP_J_ucAnQ!<<<$LY zun?YW_uBj9F~nYx>v-&2t@~)Q6cnR^7#FId|9r3sUl{7i^3NrXd1-y1f#Ns=ZC&JX z(cV$I{duDtYlfE)zOms;PJaVO`u_n-?=^jkFPN$-njUfjSw$W;4t&rSe~JZrw1<}n zGxL9e9z#J7kkL~CSnx2cPh5Fha?8deuV2&l`T|36rK?#|Ml3cqazScT+^z<4=Zfir zUcWS~a1wj;nEw7<6Vj&Y>T^)rJwG=T@HizVB7y;MrGH+@fl(h2O?nAITLm#As_s1^ zAvNf;ONhmZO-j}7!-fVQ%$918EZlA?usStX6n>;+=g(5k-jf!j1VSY^UH2yr4-XAn zJ<>-%ed*>fAB@YDoYrLeSvhM|*$7%Gg7gFb=|JYrboCx*SkyAEiVA3uiC?paQ=56< z@w{AZiPZkh%*ioUQwxTsWoMIq~FoO$DmX)WWg9=;m1z9sFVTn(zu$> zLxz6WzW29|La*J(8OykiB{(gO?5EX9iOlJj0dHtu6A9K<^~Yez(yH&$?)}hD67_G{ zT6Rh^_$me`cx692y=}5K`z0;|8uB-`XB1%cqrU|VrF7xiiTt2_d zN{7nLb|J@8IE=W*5ED+6*(ta?j4A0oeZLeHt!GML{rj}L+Vm*;9$jn{-M4k=8{a(- zhLt}4UTuF>iwnZdZ_KmIy58I)!N=l(B6kOE&Qs!3^v;I%rxjOCM0xJU1~_e(JhS}& zvUGBCPwnEa06GRcb2;yQQ3N>GtQ(iuqr*7x;k!+%XXfTEgwB@g<_!!lw^k{pyl;sF z-OC97<}u0$P1gN%M4c!4It9u9REdt5P(V#h?bx(|820hl-A77FM^Epp`wJ3C;H>NQ z(hGy=AyLTLNmMe*BUmzo-=@jp`_uxC@6JgS6@F9q0033o(82fb3xk7JKoBKlYy`z9 zn^Im3f<+ie4}qYd%pBEgYT^N3^UT3AL`exyVe3gb;KFy$%VdnYzZ^IFTl;|lQ~3m3 zI14EmnK3X&xfa-=RjOG!+uz$80-qt5fJ;IL2_F3<=zgrDtc)?>M7U+5z7J*>9a|3Q zvcy%O@bU>2N!w#iPVpO9;g}Q+f9(*zQHQV&*3hk^~>)S3^<}?LwV)HiTQ(fKM zk{=Ig_&UAjQ)CP_{Xrn2j67jL`~db;ZJzt;iiOIYH~Wcm9_#<2u8e{-%(8P@{@S25 zmKi1}eCp(Wz0R^YaH;SQvk13F);#W_0)U0I%boO1@?kT}_86D) z$B?t@G3&S}a!h`azA^05`KA-`x7}{Xdb!JyCD< zGPg6uTj9sGkos6xM;~YAWbF->zQqv-8 z7J76x7xCJTadSv5s+*~8!;QjmL-J8=HkE?kPD@30mv-BU zTA+_XnBW!<_b=x}$!UJx7wdKA2+VBP&w;Smn-6aByxHS|MxRfVN#M7BB7-571F!^G?=C7}(g*N&TEjh~Iry3{7H=8gakbSUVfLUQMflY=HE|dQ)IvN!j z8B4$W($~r99L%kNahgiAJ_LZ_$M_fa;#l&#i^R>~OIce($u@IUo03~lni?ZX$rdqP zy&8LeeSTB?!y=Lc5o)iDctbp=84A4jn=%(v#@oZg3y&eem7vCb=PR*iIO`wLGlAPP zrS7_)n!rtPno$qB@2Mj?s!p6mO3r`;7H*-{v-!krr^n9Q#?(wvr>BK@R+Wl-=fhS< zmFsym>G!W=Oz;vWb^ZGPmii4-Rhao}-iloR*;rL!rznvnNOvsQ&G@I_g<2opF?i@a zl22tm?dYWX9kvVo<_plDMW=IF!U?%zqwqbp>*GOav_A&y zUG+((=Wc{j3*DC|{CxQ*dy}K#OrsEUreyJ=6Rz*M{!C1EP@2{!`4l9nQq3SeB?Hmz zWqqwnpD2CKv+`2AEB^L2^DW!wznbn5hf9X?u|l#IhX9?((4MK#7en^UHv%<|3IIZI&*nd9opNQ& zX}(%khi5}sd6T_`Z~|x8u-x1^lD_8QML_&Mwsa^Z1k7)5oqtV)leYfIL;U5-m)e>d z{+P?G<%NYN5IM<(y$R96CMG5de~~m&3`lWB+#~ zxVS8dL%qA6LZFVreEOZ`a2!;BqefafopTc{< z$gB|g%|d)W37@@?{}aQ}IV{jG5#4-PloN0a`W&}O=JjeyY7n?jgmeM|0$g0!m%&ZG zFX*GH#hP{LEr?RV7rHjwh_;@f zLFL@D_n()Hqq*QNupGG!FkW_B?lOVGi{ZXmKb(dQR1J7~mQC3~^LiCV(}=}vXVqACou^Dk&MxD8mp2Rn_jQa%30Q~9_Dd=l84K#N06?jroQt8- z;r+C9lfu_>?7;!K){mw;pO3~z_md4}I653|4&+FTx@uwO8gW(>eBr#|NAbrqwxf2F zFj!-|*mo$YF>}WU7-y=5gWpJO^FU4o^e3ASBM_NvtiXuv%hBnSUd%T#taR^$2mrA+ZC#iA)Rt@`dR_7Ax9zq2Z*5;iwud%fUE{dURKbgm&aCM@`Xb#51GPkI) zUl;hV2C5Py*xvAn#q!5)+Iw2N_$@l`;ibaq=&WYYVAr!|Q;xaouZ<{qTgHY8RpF$8 zv$Bfu`&k2ad>Jk(#%in&DCw5AE+br*2BddG-HTTOQba3n8HA){yjSpacbaMK*M5vd z^}Am;C+x(teA4mF)FZ+Tv=M6Z!yw&SBW6KNn_MU_n*2qhQ(8Z~^u(8#L?i*n<1Xv3 z6`o?wIU!?asu1@f6ntE&hoPE(7Y2#f$iipW*R|E<}Sn0Ho%;< zlgU4y+9AD_J=y`57XO!)o*d6y!Q+JWapJUJj|{KL_hvavTyA>>0E&<_5(?@e0-6|7 z`1KJEHO_q()4=l`uK_Dcv7%&4|V4yk>R#}S_uCxmw&nBW8`3_%7TFL zZT}*4)XL6I$aP;;K|ujzF3AeC{R>Ceb2Bp_cQ}X7!3M?Iq$IoLCM*CEjnIUHFn#Yh z*E{1H6#DM(?~N)60G;fSv9Z+}i^%tMib_hnglIfUIDww*OA`|&6~#{c$DquC8q{+&Ejk*~7yvqxX{@hw3+&fU`z_imwE_@AN(`(P}7 zj}auO_3>on!_~AsLCR{<59eL~#utXaE(%Zt(tj!Ht^V~tukxMU$+o>k@TT9f6x!?s z4S&S>@+aJ5r`7w85d$g??0u?cDKWMirC+M0Urr9G5ixqQyME4DecJ@q&u)tf8#D;A z8jmo#oZFAve;19vdnmFJ?4(j$M9CaElpsk%yiL1#p(l#kq=Kr4CtmMRKhKK#J&1p3 zG-@2==C)r%7I=7DfZ#aUPjAfLwhIIGVV=#L!6>?2j!J8dFFjITSPw;IEq(cSygh;H9Y|sai(dr~#-=*^et z$?n1stH)|=IXJW>cP|I=dfKZ6 zBnGY9T>jxxAj45~$Hf>n)>BdwRi_-9-PN^A5};E?q#P>W-ODm6r3LhDB5X2*Ka{ny z6j2p+%8=zIeal0|IlLR(Ce-^?F5tF!Xk+O)wm1P{iZ=TBgWrq;z3thQFFr1h3sJ4l zJTMI2uKKOBn3X`=U*B6VH|qT-<6hB636B}tC7m9nqvvdcDn4fG7)A)r;JtE&qF4w$P07hhgk0$p)i zTckj}%sNL<_l|2yN@|i;a&;bah#35vEAMu_pT(7xou8?SYHIXKXPJ@}Qm$v_=a)|0 zvv-1F;o8geyGZ+m@Zp2ufpr~mjVK{}pla4;rP@r^-rin9B3NWXRa@KR;_e%mw5nx^ zgu=ppGZF-#sn%e6r?SpsX>sufrsQBseo~CJ1P;z)%HOKm zw)g&fF@G*C^Yct4Bqe>WEc1v~ZFgwT$VE4J|za@q*_(s8dwsVh+1|WScES+QRI8maXwZ51hr(u}M zqG-Q8p6cVCSASSw5JsmwTWsLR#F}kqeD$F4d2vs0(JHNb}EgTg@xhw*ly2Wy1MzglucNB)wfjGB9`brKSKSb zVtwB^DOG&iGl%(54Q^|3d+S|ijlHhp^}AWui?{&crxBOfl{SRwp!PEzZ+({3dx_SF z=&5|g)pq=m#z06`!$l&pKSo9CTCDvy(JsoGrZsb(Z=L(-?PmlF*3EIcjs!o9RM8Le zxul2L=UOX%yA3-7V)3}Ji-fqj@D+JiS3@%Rs?F5yCJAo}anyBZ))hF2e-jpJl_Pcm z!zS<4dkBe46z!sg;DGgt@zLtP2(aNK=(%+Y80$@iT6N%&cwEmx8qeooD(_Zli0ZO6Psw?L1f$x3k3#9uhR`e`J8 z@8jS}vT&1nT(X*}VE9srZP>xuT&8uDE$kCRF07DHz)($!5k7rjXCypZ7m)frWUC3x zgeN3m2UCI`uUawgm%G0w?wsqn3~o?L{r3Ml7BEH!ddNJ4Xn`SMAp{22nF z((w6}LJ7f!!h*te*LNgxst_1BF$05TrMD}%Ym994?62Uf!QP_2zMgdN+EYl0m1gkY zzlk^~P9Ai?M?T^=>~C1GcPOm4fdEvT|NJa0e(IIeSr2cW^B+f(4t!JUA%yGST%8~^ zn~YA$$ysYdM8-u4mB@8&%?OV1Y&YUOZDWV#IsW|iT}V{)b+b<*K}z(=1pJ_AJ)qWC z)0VpDWtp#7C+1VRb7vPyITa6mqwr#etZ6}Rw zC-3?G@Z5W!_xy7{N9&xk_gZt!G3Jn zpyyH2&Al$(WmW)Pqspm{$-F95z8Iu{=-1qo)eDj^<8NBYJUpSd^xf5y7yb3DGdOKql?tRp{E?g<#=>op(o;FNn_qKN?q?T($UJfL2#TgN5PCu=I|?Il#&5ccx5erT;{yr zY;cRkOKUndT!;m}Dg1EwRs>}S9fCknnd)F)c{VXkSrj~ACPPx(WNihQ)n^reEwR&r zOJhXrE%nG6OczijhL#(_pd1{KCtxLwlEcf8F+R~k`w5B9$Twv*mp!L6Fs#U$&Qb8w z^HZGfeRe9Sp|+ed;S+^me1b$41RvP$bc4Oq(g~j@GQa>cK^Q;{#b$euNwmceFRO{! z4=-#YXj)m)LO}9kYN=`r;`F1CGR=bmfc?A@7>xrJw zKb4~;egOvToo$w4ekH6%w?;I@p0wS9}5sul~>T93ciz5f@EPKWj~0G1@TUxexMm_+)kZr&|8y|Oc=37 zCRf!qyOck#P8D8>6~d5x`ND9PpY!S%zO%?$4^kN>j~?6@H_fo0X@CX*Rp>zJZGV5? z46Al=l<$j+wH5v;Y(S3rXR|R7g+@-k-sVih!cxx~SDU2ifn{j!`}KmGHKCIcNAg+X zsrh@djUCNGKoL!5io7|S$q}O3ldu#zEgwzY$SEPyD+W1>Ff3$V5wv!21>Vd=>hA2Y zW|n0GBNsCjT2Df@A@xg;@yeX7L@}lwvn{VYHftX9t z5ZbTfl@?-ic(_qQsqoKOlj{LcC?&}nCuqsSM!^7w5SS<3nTtA?@I3$`HD56uEVQ|- zKipTi^5d8}QS|*@m{5~aQbzJ`maMdr!!=sw9Tnfi-E7kVkbS*}u z)ZRZF4Hv#af&t4*-Is}4^6L}2z7&Lp-jSsA`8XJ(adG%5>YZ zrw9fRL3w_Ehu1SL(D$gY+4)_TPNMT@p+>+FbkBD_3zq&$0msA+4ydX#nJPETBOkYP z)Yf<&NzjOd+Z}%Ng7)JU;9mY-51Mc*c>w{!)_E1B&2y95pOVbV{tN|);-LNl1dA08 z&4DRnOX;r9Dp&cDs?JT#(Z^EZB57?q4=!+*-IZCFyU#6$_BP$8jPW0MI5}^R z=gUEzK%g5*Y$(}qULMJbO5sf4*0NO-NZOP=Y04a)U8qirkttjJgBl4skS%Y@qETO8 z-}m)O+|8}UIaYu86iNLVGMe7gM!5U%pxg@_F(0H*ar)<0QC?01*r{33r$Yb^t!t~R zs+B6cob;bBTY1@qP_dv{MIpq{zh29}`CLzyCpT))1?#g$CXSIQa~0oapnXworqzBL zs;t96cP_>HhCUd}F#Y_*i1ju@gc_R0NJe&(O(C9!-=Xzr;E4gG%%?@9R22Mi;9!bm z?(K2BG2W%{7W*cacT9d_o}!4EzVj(uI1wi`UdJd?bY|c+(WJuih!k$`xy+=j~VA$=?ffF5DtQ# zaiviCuA{@c_Nm3{s&Rp`v5|Kc^fz$EJ#_U=Lt%?K$@;!5xHRd&J>|@^vo$)M?%a=Z zsU-DYm0WkBxx1(fYC%ghrSqdb{zftfO)%&N3GIYWnVA`e?xJhoac1K0n02ex*2U5* zOY}Mq&T%D2V!l?c8Cjj`5}^8Ny<7Wt@*Y}%?4{)8L;fO&c>Y;rGjgnA+K!uw9(G0m!Of#qrI|8H?ILZbIIw4*jeEZkaDXll=g>puo zPnzmx5cIg6j*SxUxg9pR37c+Fq}b;0cQ5fzvG++b70oHkrPoSS&{Vm1UA!9Ns4K0_ zt6xI3Uvyj4#*eqFXQ$W}$;&I@L90xlT1}@!{woOyNr56&NUy>_j!J6DpOUJI^1qHe zi&cQ6;f)&BkwK8){#x|S1dz6gNHUyZP?!d3Xkfu{Iqmd1pPhdS z6HjqQ`m2;rj!nqQN|Q;zXLR4bTy%?n`=(&5B)veLTRV%ZC%eXG z5@tTk5N}yq_ILw@V$K{bt0T|pdcavdQXHgI){ZMO^E^R#`umk*d9%p|JK0yK>BxjY z^K{MO_BE$!LRu2n-fsCi{>npo!D9xczKxrCGdgiIeFU~|wMZ(PyIPja#cFl6_BE*7 z_uD%zmu(n|=5t6P^wIg+gurv_3JK=Rqla4}lJ>4v-O37Ht5+jJ8zbWA*w`z`W$DkK zKNY5;j97@ZYGWq#m7duZgS=|ANuZ|#0>CZ7>z8RihZQK6s?j#dy2p~4v*LjMB`D{T z++=&*q{A^1BpBXQ!uX(#R5VBf`$oT3Q;Czkci0#E)ueX`P;)g5-># zF&!KnMntu4DeDxWs}mDeo^{3%EdtIM|V2)ur9n2lS|B z3dD>k4m4tXJ1e2f*!fQXN=iynQc`AiNnis&bhUh4+hm(% zx!07U#}+YAvut&;BT~Ptb>;V~8SNk;SlXg-A5`a*`*4>=g^Xfij9`q%2>h+blnO?K z<2P;s5=D0E$SHhLw%29;)v`gd;)x(`&EmNf)1V*r?tJSh2n>`Tk($(XY# zxAo?fb}%lv9U)3N@}7pJL%LbhlHv~ychp?Q3tC=E)iOK$Yq-o&LQLtf?TY#yva__TsScXf!cEz1`o4#iMK zWc^@O^VJc5pHK(42g+Nm^9<&KrtN-fFw4_u(XwHU+r7W8+%Wm&rLko)#Pfte%S%K4 zP_txg8lKk*WYj2Kn~^&*t3U+Wna0cy9z+|oob4jo>8%d-#gAH{V5k>-7ipy~C#S7_ zpRG&M&lj#Q?sXW;eIqX(Z*qlfl1x`AeFzm@H>HUX91DBzy*EQck)J%yf^M7m@@Yz~ z%G4x-Yh~$>kIh1lB|)m8#yBN3sSi#KRE133l7Yr+@hQ501xf+0Ddl@b+FxbNSTrAC zWoZm$PJ853n~JwkfF2*xryh?k9&VJ&b$?~fl-MGv4K$e{9M{`jL%>BQy>^06I!sE| z{&af4KTN+W)8k2J1WbC$X#jqEeRc4@fGl`(e6g8jx`z%qGXBFS?wQl#ct{%%Jifc` z8~d3exjedac<{OWC!eSLc?)V)>0}4$(Tby$zS;MFNceEdfg1~DDL*tI;Q0;b;Jhn- zbK&SzV4`zS>K7*BPLuWlv?zVHz~8U4`Od|`vUn6a6}F}O#)Q~SkvOUQV<}jRwcali zhA{2Mwk1CbPyc3ob8jxSvFceqOFp;3X);QX>aIGs>REc{YYW3z8)f9^Zy})be!`Mx zc8#S{D3*h(EMTrTpz8X%BmBd3l% zG}+J5N-elz#xPhyCM#<1cP~gJvqO|gAwy<2+8vEgX@}@H)?R0|Mh}a(l&cWEa+(r3 za2;~3Bz?i)yTLV=3f7>IT77W!Sw(pSnL(D;u&%GOvpE|D!BDLIF3kkOm6}qI_YW4W zN$r;`-)xMLaaEfYG57mS3#B%RaMtZc@ei+SO?I(z+e!TRLz2X4FYTr@NEq<3GOzGR zx^6Ni3Nudc&FL%@P|K9J{&LEVNZ{W@$uCA$)bkU06GQT8qvB6>y``(0D1E^nK z&dmPt!-zR#7zr1w59_q5=DK{is?U<6fdVba$X(6OJZrx7*ZR2!&1(6CZfWwFf z6aHJeeoez||C|eV-O@{&_BuJIpKJDB)>@OwlWLN3qq(7#F(=v9?vnj7lUsYJi8pzx z9bVGT%d<%d%6inY-l?gCHiPBcMaG?_;H6xrF*YU&#R87v;J`!`cGIcDtyy;4$`lLL zd8T&@xq0NYCPm0l5;O|_Yz4tL+M&CEaUSZu6X8W-)aj50z!CPGf^sMa3T&fAp9G zPy-B^>wOV$YIO!LkDKjc1ym1tJVyjZ1G+eSV%*v!QUJVe|2N}s(}*_uWX`#}hk<(d zh!)LGz+=CFDsLV4U@ZJqe>qeLgD68F{YFG7HlZS+^NZF>uA%`LG*QK7z3eo^@eM#6 zY&c2o=ZT(Zcd;hxrM_HVm9r-5)AM*Wfk1(>Ayuo@=G)%T2}x-sHin?p+4P{QZXl{8Wg1)SxaZ@ds!xpkqJw4YiQ@T{!x|?dZ*E=TPq9FM+eYttZyRkrp*;ewrzGP zjWPNgoKGsnF9!qoLC^ZOTfF^cvQ}z0-y3!3_cT8MwVF{e0hj4?Jf7!4a2l0-%Vnnw zj_38TDj~z~9ls3D+9%f68}q8}#NOPDekd#39WuL`2CXR5e`>bIY$(;1Uk;*YX?96f zrxyyJr_}sXd|H18003{b!=B>5mjn?ioB3gcwBI9H?iW>)``7D|-jr>hLp}I^)(a6T zY@a7`o7uO>JdHa_AJ7n^a*oQoSqx`w+t-b~;oKwB_g^7*y!F8MzPRlpesBt(g4e`c z?5O{G*1x<=0s}+Ban5#Tp4X~BRm!LNkf2hg%Vi-hMgCH@nOyD7ilkj{q4NfsX74hA z^gs_P8{}NzC)jV1Jq`pjA7Go`%updZ;$!SD&M@^OR#-tT4Y^t zrUXNoGTI_9qj;|xO7>HV#Z=$p70Ym<`f`b|9)Aim40lVd)ceBPC5 zTz1(>ez5Wk2LW<2U^IF?MmQ?^rB3RT&SKT$*99?H*&3%@t22<`SU!M!x>(VEy_RwPG(bujWqq37%JMg=&B`;K z>Z<2B+y>zxo3qt9#$Yix{a4-8NQQ^iGC^;s^`Ptr`{L8gbN(=!;V@Hu(_qgP3B|E< zC=N0oi3&%`fsay9H^~?I?z*hG2_%e(7&1?gj z_e1qr?rnwHcG8@og5-=53f?##^#N zk7+{f7*l;^k$@@Vldgh~sjgEN>`ta(E2RCV-yc z9ylDcd6L~6t|%Q9#)~$vzdoWbq~M1{f7m-Y@0Rpp&PF~hF4#_!2FOcTzX2{zs3lvBGVD9CiyRGgXv({K*2EN8~ldhjC zuBMLK+p02r*qfM}V+HoI^DkeH`&GMQx8Ig4vAdd3jm^Pl{GX&WQQA2bAr3*^&arm3RJ0+&oj8#E|+si z^}T)Mc*IzdOzm>5baL+R$N}q$z0cYBnWE+*@$d9CxOJ`5$+O>9w95L-8Yoz=Onx=k z+T~aKUdMf8+R^-}=!lJ7A9@&#@naac{ILx!CGlypkPPgrHdwpJ_j(1=$APjvQq&7n z>d!la{--w5h)D7zeAx1l|9QO7KrBgL6}C<@@v9O+$Mhj#HxF zj}bMlyJu0V`b>i;OoGJtc%1BR9{nT9zy1-b(RvxjH(37IZH*IzMPtpZ%@c5bS;~t1 z?Rl|((n#Swb7pj~fuE6rHQMMX@G+xmfUP8~r2XvdYzi^)a4{|O#hF2Mtt9qnDk2Oa zK<=O!k7p>^`YdN>A1!J8hJE^T;Ai>WIdO&PmXj{Ol6uALz%Ws0eWS){Q=6=xO6Bs?mIj(Jp?x+47ldp}@3)t?fEHL2+o2zbLp$ovr%4QI!6}LFW^9 zEw5+DD^quSM~2YeIUP)dje7f~&;w~HhEb0kUeRDq>s$4NhHEPuoFYJdF%oCrPGqMS zBF1M?Z^(}05B=3U87sKk%$)%VD+&QecGI=tAP{6mpsU8dZ*{R9*Z1P?Y=_vZM$q{@ zjSxM+di+eJg~QmwS(C$R?@F+XwCZ+audqIWbh*Vzz8nhDI5JvgJhfS|!68GYC_iD~ zYWhg%{YG7p(l%Z{35NqecC{VQ$<853zlH-@mTt0mPI!L@`~Gaj$6Rb4 zLf=E*5$590<4+hUYE@0Hm&j^Ww>f`I@t+I6Y`%l|{P2ws#`O4)5q)0}1iA9TkQtb8 z2C8=X9rCS@Aw+i~9|JiuLKqs*J}DPN0s&#kLmo(=!>lRl2BhJ+4H|E;%M=r~adevC zl>B=~^&kPyZg@DfB$W9-QNGn(hda*&yVhODl?(W?{=7LB#c$n-w>oEYIp2qlNnnH% zzGzvC-|o|VJT{virk)#yjmdD4y)+&KC*O3;;B@n791_Jl3NItgcX!epIh&Wx@?R|g zc_HZ^RpXAfl0gpQ?w`hQcZ13Iki;mj_Jfg^xc`i#o@>uE81>3pl8fXatcy=z>4uU$ zTfcSKYJ>))FrN?p@{oNtxilRwJc?#PA{)Q5I!JZizOX__ssG?cv;2y^Rlly;TJ&Mh zw~pArem|q!XscxYC#kwv*MRMW&txHDqX|m%uKTk4lh^#sDis*sSkYa8Ghe++5Si;! z9)ER#kotTndK(GBy>?U&vv36|4|Ju^Dd{opJK`-{`wpq=WJ ziAr3h2iv|AGmUJg1HzfXZYL$o?(tw;Uyv>AV&$e3DJXez1GiP!?v)=KPd}4K$&imDqAbdGiNGtl)m>pZLAYi(V9@LEFCp#Hy5=&_L5>&wE`ziZ>B&D?K}yR|tj#E6g)mvJ~Pzqa9NcpDEd#X}(5p6~M+=xo*P zkwPB}%~`#U&^K{bKDR%Xuk2#DYBpNT5z&o1e0WVf-03<;kWARwjLiKF$FsH*;v>Fk znP|rF?|gFB$nRieXcw~6o*aSFbID%5;rRCElHA{jzSOR7f!=R5dUuvI6jT;yPK zqrd*D(h>eEtA_-?+6bg?lF|m*&)&?kC|IOz+HYQTJJDpB)O@q~c&*o+wLnT;m4Zb5 zP@0m%yfz!aE(BMBjdPjD~;*d$&H1hVnF$>h%EvFaY3`+S)ie<{*sUHnzT8 z=tTv-nN`1V?`40`c){wZS$zGuPrF&?)M8x2b}m|6O!-ZDeY@NT_x-ueAdld#=pXQ7 z8*O|4F?y2no!u=OB>T~?tVOEa#w*{r2EAr?+&x`N*3JW1hYl%mRI<4+<1UuGYb6-d z^s8_5w^~G2YFb*gt}KtVRm3H}v=@BQQTpRWC`PM4Q)(XY_=664j#`NK)8;{PJg z%0;J)M)75!v~fC0+Cv)ANL5mzQ||GNh0^^6U+!OGsG%Ic zCnu{lhD*Z=eevi*OD2$h>}oub9wyDJK>fYGh)kB@N%{QFivtA^VK*4uj4fp4Nrq7= z8@V9^evW)PGxM{)SzLDqsr4uGd6|J~Hig2UCe>)RUWzjh-<6 zrELUzd9^*OrNAw9i(~9N1_c?We|5~f$J_LsKJ_XFeuly*@qa;cD9&#h_NBvo$yt+sb85+#b!FBq2_3?IT8jRf&=lz<_(5>$Hq)?Vr<9t(nMm=4`$LVt|wotCK zbr*~s{tlmoam|l{7ERVD_CqxI`96af;oN-Fp6Xwr7*y3f7DgX^GD{m^bP^UF-#TMgS;lpw z`HY4ZpPh~XlrL7K3bOueW9Dwy|s)PqDcRZLekSc1Y@kSPhL*f976s;NMcm zOUNY>A8k&43*pAf4;-|y)9E&jfw)4J5=syu!zfp#1|IeMe}UTQi32;d!66`GKcNz4 zL@zbQh7+R_hK#bv*As6b@1LG%s#vwA8be3fZZ3z5u{990{BOk8t|GKY(|GsbLWj~7 zrw?6Z*_t=GS6HG};DOZ|vEUU&- z9Ih>KDg6J(hO+i&#pFDMCEtG?P22vSiFJM?|5Ua;e0=3eW{o#vh=vBwOIew{sFN4Y zTW4`r%E>2F=M_S$!`gtMO6Y2FmS#BZk)(7Y+qZtJ&tsCND$R-UIEsRNuvp21ZCXIr zL4lS8IP5Uw!3Dfp_EO_})Zq8?S*v$5!G(3Yv%>&~ zf^g1twQ?JHkgx4h%Jb7CO#8|-c5zoGx|i?4+@Cw1>t}1O8Koq5r{k8tWg{g2i*~=) zMVD)yUb);=RWo$Kvms^TeK|UD#+>DGYODPhGBaFr*5~067-3u}Res%okP>o|Hqj|( zr%9H}v620_cF4Y%RucPPqSWCDH$$J@VcfD#(_2GvS!*0Ek0)(md z9!^v8mAtr!7jq6oBP#6QGFV6ukrOJ3rT7ozp#wplfqM|-Y1q;+uu`dWyg-(fMsz$| zoBVi0W29KGa_WzHeTbhMR8SH>2L}|ZlPOIo`U*Jj&31eG7#C$by%_d>C}0fSnH`M| z`o*<|ONVin-L~Z%MWFv_Z;n2bkU;ECueLQMvBG6z9#3Ursxo}J&m2U)r}Lc6U^1{>#-ST39=DEXHad|vQ6s2j+75B{&RD)9Rk>S-UksWe;6I7<7p8Ijsti#D4es)A~axam9SGZ*Cn zj%Vum!quj)<0Z19BooLKcnnJbI`{iukdW$dmu1jY8!{9IbCJ@NzPxvH}TW&M- z`7adyw9GhJwfN9LZ{X7SQ7!Yp--+Y6oCVhO?3CEE8XFGg$3)sO)f$VIbVVkvA{UeY z<&o2#3CLj`hJ$V;)l~XQP_sQGCt{4E5}oQw@oYk_a39=&7uB2&z~nTR)`7u@p$SYp2~$dD}eySF}8FV8ex2W?$rA0HXA}vV6kkD;`qRSv*$v z3rl{<4Hh&G$9lL5a6$AWJdtTGEJdagruJD1l&Gcys&AXc2G-zOPX?~))Ac3baSMZ) zYaW5HcBYsVeKQ+A=T$p-C7kF`3Di~>U26-0ZwOIi867Oam&cqg3$O>bl%M2i!gj8$ zf<$ZKsDDvEAN1Ud2Z&YC$|xpGuj>u~Qa(oT%~3pr5K0RI>N9|hjtviU$4mC$W9?N{u!k z#;l7Ml|`)Z7sG6hj<#$g7fajD;V(k+eD@^B11XAZ75!BrJ*@a@K5*0Bp8 z{rKTQx!!hWo zLVhKlhk8?R<(MzV*I1PGM*j>=HCe4C0`AP!M>$IAT(|86$VZSu|II+gQBA{7MyT4HvHu`wfU~(^JDI;6{D3q>b4!-owc_lPx55(By9Ne zLU3a}?QN#sez#*ZqY}B6rsk0DTH-M=G41(2LO?*k++TbQYvhX*%NCNh;#h0`h` zx^|gOViWy@`R^hCA8c08pm3yw4A#Maff-@o%?Q&B&_|5VMMT4z#gdAv)qxgdqu zAly5NjEDYHHUOnzZmU9ze}`9hcg}S?T0ka;e!um&INeZin{F?+jfWKf6JJ1gSGs{! z!|~1Q9qvRVR~SxUX3#Mx>dCvi6B`$c7pt?yi=>AClXt=P`NFsD>=1k-TMy_5gaFXR z2Hhajjz&Bme6o4nSCi;KR}k#WjQUn-CARTVG!bEG(R0Qx@b07_(py=5JlS3;bpJY} z-s!={3=Z!9xTVJ{u}dFifQWb%=1PJ@Uj2QLT*iXUrN~iu_s>o4nrWGriv2FA`*{Q( z#)4^Y&{S;TXsr4L8CiHEPsNWf)t-l6LtTgYJ>YYBKE569iweHcWM^1_N^n2lNJ9>C zuwd0ME%Ynoz^X7b4&Al}2yO^3gdXbF6)Zjazeti2a?!#A!fMfeBB0a_g*(b{X`vp| z5CE*;!u{etAxrU$vI+MMj6I+m%KY8O!0EaE;yfX^(Y~VIoNOPMNPYZEK}AEJC^q zyJI``;P4X!uEdXr2U)B}8fh6$s3`3&Gd*!aFtJh++4D4(iysL|V%CA6SrB@lzq0bg zwN*6c)mW)Dv1@PFmB8FR*u8(t4#m19!}mb92)dZb&Ls?ha%bDWMJ zYsOg7m2);JgtOsVp-3b{wy9ePWe61r3{0uSvW%RNl*xa(fdmXaT`;7U_is(r!$ev2tR^QK z+b^9zCCAlA`qc*AaksB*i^ifz>(CkFX)AOEQDS~-kd41&BvqD}Q7)C}4Evwu{!0im ziUkXi;;%q5844rIfZh);8M?qrz(iQ=AKmt@YWT^t;21MHKa#4w#?T%+9zWWz2mol2AA%u5X!I!ib8x){ z&QA#Ob;gVqYk&ySg_(g1I%kq0Z~0z}0%#d3R4@B$8f4+QXmaP4B9x9%NZhZ8!Z9fJ zb8n)TL}Tax0)XH!+3%|h(v_qxQ;X?-wDvLk$G!P@)EU*5dCX;Fw>`8Ejd!V zZ4U_cO!19`PPfn5e|ls$E#Mo6SLK47+i7>Nna##=d$)1sg%y<+>qsKLX>`19bRC!V zz;vkzP#`D>xKQajAcu^fJeC6kqHbfBmFjpi3jt}5ZD>o2~{$?2vFGN3d+ z=qDr9^U5YR)Q|)~jlX;=JYZy}OC4`!)F8WU-G?ik?8XPq5@;Y)pqc40bjLn0wsKYN z93Ot;b}8zdTQ9aaGJmOE1rr50Bxor1tTl_qki`=eQe`6n;c#rQtLqxPPJvHXIz5j% zQ8v8H4?-^f{uq|uYxKk1+?ry+ds-amAp|om^MKuC@2Z$gnq4L^!JWCPW~E_K4gAc+ z(ta`*T41WbkT-NjN!q8=h8Gdh-nY2O!dwrFv*{f9o?SS2f#hIEkS4oGv@p$FYtsJG z0qycB0=%Q)TT;(za(F62FQF05SPv@#UfQ))zjP!Wf}aZisPu5r&n8yD%#dYgMj8*) z4N@Of02n&J0U0zV_3-my&3X9}Y0!4wFD_qk7vX`N@A-5(rQc2xO=?WxTR2TVKf2ZT zl>Cxmw#TUf%=QFQhnG!o>I*!>y?^rZXWQxqr6G==l==ND7=r8zaGaoEqOsYn%wJn; z^30(aadYqmk$b@W=(bK)#}=9lL}J1Q7p!Xu3a0Zo52>%hcHV^rx&>n>=nAFeB@mDR zEIllL2*Kk8>|LMHCL6c{BzLob`ZkbT+`mG4&n{=y(q=S7!Y9|7hh~@}RG6NhUWf?* z7I6X#*cg0b5dBB%PNuZpC^+EVB&lCu{;Lj4=6Eg7D=}!G^)oTF-gl`5C4e)T(f}w) zbYD0a!3s76{Ph3%IB>h%y0FQJI0*JNu3%KM&s~ccOmK$sEinsHsonp8GSq5^J!qZ(YJ6adct4IRYMe?Fa!>7kjN3z z%nWm5!7;>pP>0V7AS>O{%1N+oNWgnisvD*Nzg^msKbZ8@@NDLacOKM4G36wxqE*jF z(At3A<7J)CTUV#+#Y0DKDMl7ty&mt(z+$s{xe5fk)#J5tp7b#0$4D0UlIfp>Feu;c zY@|Erp$;dWqPSl4{@DvdEScKlzDP&rHj|I|ljVDN#cL-q!-DslmVM z)bW@w1<+oxir5u+jiJ~bUVVL;QEX?wENoIMqe;DGIiAQ9-8{?fWbLumuHZjbIj>CX6AL~|G3^1{OU!onRJ z38*r>o^P!?u2+?|=(r$v){}tSWc&0wAL|j~r@FVoy<<5Wx7-QCpuvdP6NB$=QV8C^ zG9N#l&Ou5_G$Imr2$1)A{%OO`R599o(o}er*Ceu?UXEfgVPWJO-(6+|e&8U%wbX$2 z`dox$1{1Ku2GOFCKlWhRsx~4}l~g11D;}_C#~ftW5mqn^#9#w|LxmAGbp!!mu{o8L zCM3y2YFTe0R59JXZss#pB)2BbL(FK#nHf6qctrIi2^wrSTx^cndn>FZcNXZV|Dr8aMTIbdo+P?> zog@)w?-Ik4neLLc8wk9~QyW7%o~_GkYb!oGxE

    %400?5q+TKMvDE1d~gBJk(*y;efS!JS$-KKJ1>v6X#ILCUd~On(GlNi=PR6K zv5Zefs+wTE)<_~qX>eY!i*%NG!p#Lw!CW*x*4q!h*sb*V6D&VGhq13 z4G*vuZW1;)*_QJY%6|V!%AZ`|?#IzAY)3IN9$i+m`X*` zLM+;$k}+iFXSjz%NPU`Q6chs>PW5)Or>DpN?Zvll;c%)nejewd7a38fa)O`=PwBd{ z4-#GZIzWT=c6|;c^D)!*bY(+YCBBmISjhZiKP0jvmsga1ESTF z)oXixFapnb07rsp)WKak><3SI`6L*?*njyf+q;lG-L{T)zoe^9RH#>_G4W_nxsm>4 zdu$`SbALP`Mf;X+{{F<^EO*r1@NF7t`at|vJj5XcZ(4UrLrZSFlDh28;XPjwwy#(u znctaTsIvO~(-ZM&&$Zdbg~GXQ2>Uy-tsK<964oJQg(>AR5RQlt!;fNd<5jS`G&7?3 zCgGdQ{}Qm`{j>DgAVCiRm?+gM_(hRcXJBxqI+|6Ht5fg&bdCLuhFK? zV-eB@)+~&sKdQcfUkyi@$KY>Ja1NGt78k>^>>mUP45_Lwaxp=H`P(?uQ=dwj5U+HH z{X2ul_!mGK>|gJ8vv0p6LV|(0(CX!YdXkzbyMxXud7X zkyk-RgYNA=?4!HcL8FsfCO|a*3L&M!3p9L7`ds|l)ffSfWr zV*nvKU-{rZT!pa2i@v6_@x_{?O_k6VX{t4_yU}qzuEZ?laB}NTALa>QIYx2J@9oy^Q!aXk2!Cvbv7_G-N{0zzgu_BG7NMn zR^xsXtmHf{EqFL8i+2;Fn?f>tQug@bF->}o-Sqy*7B4-iwjgdtz0A1}b=PN`^CluXoFd;GbRE2T4rNQ632fz#*F+dPQE{Vw7(D-Ic86% zvT$WIkr`c!htR0<%=4{eJ#CAx*1|r@XyB>YJ?BI_#$^DCH6m*&mIvh_(+r~-wY#Ud zP;(#vzn|q11)dR~)>}a`WyW^3G193xU%%LcKZ=dB9ASC``%}p`bot)YLx!yBO-EHtRA%`E+ZvCry;J+uYksTD$#V5hBQ^B64jqDH;JilUk~fkbWg$ zxytMhVpY?3SiRmQvcbtG=ZVRfUBW-q<;?Tfje0xkLef!3J5s})y{|qAysiwWxUkB` zfw`zvVED@N)1QGX+MJAy734&SB)@jo;x2>v3u=E^22&svpDN6zkX^$gB!tcc>!J&5 z0dkmnw1$)l3W~**$5Nav-tJ>En;kQ8Q8a7M{>}jUrjnZs=|f{ zDxrApObVz|b%}K^MpByTJD#=T9v|T!MW6+A={nAoTVdibA_9JT$rXdU=^;&na+${X z?vlusqTAKgOE-CvLtLbXXnnH8B^VHQool)jfqK)AQhVLsOGg%`)2#ke7>o|8OQp|O zC|AK0Wbu=1w_XVdvQ;TZN2iZ7KX7^VkU`Q!@p6A2_||PNuL6^C`YB|J_)0#NT=#pqU+c9e%q|v&1a~oj&sqV^jp#c(T2ctxT!D1M<;)lS<(pZ_)9RmlP zSI)eN9T$r4%TtQ(m@nW2r0*i(yY4*;ewheVOQjhq8}m(jJA@Fy(u!zqTQ)h)-Q5W! ztgil@&Kg@8@?rnBh-zqsM?9ug@?08On4UoB5S7N6glGma%FdErJ=J+X?6XwQ#!l=o zUafng!4W_iZ%-~^Xfh@}yOBBcm$c&VIW!8I{WGnKDe-U_R`fi96*bnx=y9I9&CM^y zlf=9VHi^!0R1}p-JQ+oaHT+Wf3+HG4-;*RZgUI;LJy+7C zN?N_WEbnu2HpWfqtelMwHxH7}vF*$vs`odJu?O=M4V@A^k0T971JMvt&O0%rU%ifB z&6CA#suQ3)q%411vtGGt^KWV@tBX`o^S73SSMl^^%x2H;$B6!5C;ztUQ>1?PgR#E7 z54^!sK*r7zr#IJKm6~#WO&&Umj;;fBz%;UCF$=BGGJ4Ka2Hkl>j>WERcP$UP%1udw z)d|b{+0W&-Kme0=i{Mlj1goXS^wJqT*RoDg6|>84)bLAXVqF@NyzF?nFy3;N<0a_M#j&~*XRgEZpMnXsAZI^071u&V_1QCdo&Tl= z0(Uu5L@}IP^CPVpka7S{k`_6b4-|p%?$*{@o<6zut3Z-z^t~(q$HQ(bkoJqO?rhP! zW6@Nb8|2af76)ooGg6Af-rB;*IQtkMwfN#|V|e+ge;1lmv$=MS=S#;?E&jT54~ZPE zc&%$CJpnMqXD0Ws(?CF%u~Z!H2xZF|cQ$&l=-H6|gT4o;G&&Cf@N&jRsS4tshF}Dh zlU^7?A}jW&>p`u$xq&?y_cg*e!KUWqWpjm{gF?>e#-DN?Z<9zJj?HZc%tyK9Uh-DY zz<@ToAFi5ext=Xv>RA6DYi}78SJ$))Zy-SMkYGVW2*KT*puyc8g1b8d!GaSA?(Xgm z!QI{6-5KOe?%dCF-mgx5b^e^H`7mS;F7q?k6@QogZA5$M; zSMP&t}=GgBnGD5 z)4Ni~s^zs@9x6}&@_07t`ITX1Ko(BH?#dRpJzr}M{ym&^+xmgSka`Mcj^$vwiG}TB zZJ&}fkRKf-AL5cizya*+P6%;N#84F>YFUvbLCZ#b=>Q-Av|{=c8k=zyrlDcr6)3ByOTCQ+}Sm&tJ~r~~`P3H@VQNrxh@ z$uT5Yg}DEVhgGIpxM3$NW|}Y0Ra}m^HI9ebyNsMy3A|PZYy`=bgoMd_R&xvF1SpN7 zi>-;a4;eYz#c4aEdnSmBb&o~UKZAQ0MM9TBB*Vucq?gJxhr3x{N2FRRoFs&Z*O~Os z<5IRnHqB?1ZcPqY;?ZN zc-_)j?ynK+SW%~1D8AWUAgheoxF-kt0>Or5;0&g6-}ib5JiA@&E!8)_409z(csfu{ z3{WApMjU~zIyT8NG;ou_PS+$?f`R;{>MhW;-JM#|ZZe!PW?y(UCiS>Mrh~C!bb}d< zyi*m|NGB@~^(5x-L~0u6g%roXXDYC3W7~d}duV2Dj~dEw;_SJU-(3#hI}%8I%sM7% zsl^6K^R!(jU20GnQN>Z|>9F;B=E%)Pr}?TsX~tUV%^o79QB~b-Px+U8;Uwd82v!qa$3qGO<4uc!dY&k&tD4`9@cX+ipZ=!y}< z!zL_5oIJVKkF{w;01QyoJNw}%=fk3`SeXH$A5=<2${#;lqNPq1Iv@~V%-`agladN+ zH90*m++UKR;j`OrwDnNIVVs?0X`NIu@-m<)zReXOYv7>OUQ2@o)rDx>Xtu#q%v% za?ea+@dOJ)P$AwBK*`BTUO5WFSOHl$-?!}$le_-C0|4AwzM#S7tVI5bxne(%gV1k7 zS|QM2aW!jxHlyrH5=KVVLSFfKcy8B>@2fsJld6n!n$XRzsuwY8fS4>`d4N|?VQLL7q zHd&dJL^if4t-dikKX@n)@I3#RPrhC~PtC5{@#t)FPvX-@7c}mmD|_R(|G{)tgxiuY zWAYum@q)tvx#_av#XPliVypg*FtQfu{U;ME1THizX&kG`vZm}?O=T>ESjm}8pN;U8 zE&^!b#Z>*WANr~9R2>B8Oe6E1nM`k$kOl9#a-wY4&^gR9zud5qUFkHVZ8m(ToMH;K z%@mp(x?(HfgZ9Zs7Ca!H$fXhkfas5l)Q%V)q2THM$HUcIBKw@_+D(ZC@v(RS0!G{) zk%f2Y{s;~h9}tHIXyZDeP;Ke~S#FkecpjW~T>wV)WtvJUW482or3HMx$u;BugM3e* znc#JUs}RA)oK`aQ*mGJ77}ey!>;-skQCp&a_Z~X<%^OTj{$=H*1y1#D^@1!%v}EfV zz9Pq|+S-~DrDB+x(ngKj^zdM=V3_0&&~?0eIzMl zazur2CNr9}W!EiCaUQ|~+UD(41m5m7xlyRUK254~z2=H(u7--KWlt?70y9FMyb{^#MRCaW!koNM|c&48qO+YbMVr;fK zD_hDe#B3eF>G(!@rIXzMU|(A5L8RT|dQ)nyHyi5@CH+XFmy8?0kHPyEu9|vY{1WOn zdr3i`=62t}>?Pbo%#o?wvya)K%9XWO|lGm}eKq|#F83kxu)q6}6F0J!`xVT!n)&!b#i@8u_i4+5m{ zq!x1o_DTGfr_pqDJ=`+7P539%lVJ?5x%s2}Daz@8F#DfG-06XA3BZN!#;U2jG%i~O zmS!8fZ58Kat|Z~})SR06f-wo&VWWg;med+S{a#&Sje5L#C@U}Z6^I_BCd*^<&L^N7jC#Z|XGsyAR`fSEgpS)_9LW2O z(Yj1~+s?@JUUe##?h@&=Iy@l|lg`FlEDS9X@Lsa42!==>>MP8SlS%E#de*32FQLyz zhtIcZh>Hh{XMF1x;LlQ|{FsA2(MJVoLzgv9jtQX{A~ur94OXNiHtGl#)+Y-R`uJsk zO)y5pbFKZkK!Nn*&psipoHxn=655f2U0Qw9S6OjBC$matTI^LtG!a0IjXPZehd-rdpMiR zYSum86#Bu14NU#lmYxf?9nk_fm3I<7>Q=m6*eEo2t0u>bH_d0gV|eX~nk{Dc-zfuo zmJL~nh?t#lmCIdz)_PHr73JOoVKwA$fXhAhqO;K!2d2ZGuwp#)fwj_P&!+m(%O7Ed zc#K^TX_D8e%o|kz3A`rui)4!v$@(cOT#zwN4(Ssrg8P|=5EnwPzl$f5td$T!JnNu1!j$ln_qd-}}wg-2@(7AFa$`ioO_%(ZT$ zv6!mYH#z*umlkw?%VkvSk{)fKw6(i|Z`OBu8W$@#sHW+;j8x;Jsi{f$L=p zY?^eH7u%HXtn6&&`=|*H{D_MpyrS4q%~HkO4)g2<&qT+FV-dpMIZG?6OI$J)X1TR) zN+e7>G=guBZPbM-9woj8N1O#V`-klzR75Kv5o_9Mvz8yxZqdN-a*j zK-QdDcm5ZKzkIL3-9LT0O0{;vp(1!=17|fr>tr5wZ%1`%t$R?KR2W7~aPRYsrO!-vEvMJ73N@a(v z7Ea&Lw>TQ&U+%(yGIVCd*P{S;eT5G`DW@5hX*ke0XkZ&Y^30W7(<LEiz->dMh+1S>J9=slB>?3>paf-#YxfatD$)7nq3DEau6vg9aOpu8fB z08F5m7gd=Y@)}S8)icL7%Dcgv+L_`1ZaOqL$#=<1F$z7@0)w4Qq}7F%~mu&uH`sI(H|H?5QegfmA=hrjWduF-%Qju8WC z9WNO4kcT8cVLNZ#NxnCD3U$y?v2i{g<{RBU?64=?Vsh{!Nc!pGBIjUHi|%pgl;)an z`ir#p@RquYkZW{47zu#0)XVTLF&@-wk9YkF&1+oc2a8T6jt!n-JjnZjbX(Kt!L!)f zO_ny_T}JC_?s6-dB*MUKys~lWuIcz;S9iqb0#jUYeXY4K_9R_0V zFD;OT902U)s}0rG8g|-csjT-j--N2`_ccd9qWrR^+4eA~43qRrZR$@Zrt2}U*TPBt zjz_PTbYvF0x7Toyt8)eOl#Bzhr`(O%+9Bew6l=5pb! zM=!8kSuQ^NBG8;-&L#>M1jKvn9~(qy^&Xp<7!0*>f-fpGo>SEKWmf2}>g2k!Z7;0I zkpeCW#T-xGQ4g?jc}7`S5~$OeUAxyQx;hary z2G3Z2siJ5ipkYZ6?O6_2WIj@Uh@Bo5#dIEzBviN3V7m-%7W&L}&hC`?&`ef9=_~QA zn;>HE`HSPC=fv5u8FZ11nCdDU6wq!-p99|F4|yE;vd8cypqcrSxQVL~A;FHsH_UbY zq6@CnH)$U7+O!hW+NX#`;5%*Fh42M}T}t5q33y_Nc@uaZ6oO1(AEjap6D@#iqU(kc zmHTP)ZG}ED#BoDOAI}d_aj>&1WDbEdaUphnE*=~*DBl}>+P{AeFX&}FY}vK!6eb4t zCg_ZfgbG>1L!otL4@o+G^9~F`kPUAXoRYDY1&SL(c|s5kM);RXf*8nKjpPd zMi6UIj^0HRY7W$#TOXx3@*L>v=Vt%hyj-dM_NtS^Zz${l6VoVB zONp-`kG0&wJrn+@(GIQUn|qCb{G@!WNp72CE;U1ZsS)b$418P{p)&<2NtGu@xzYF$ zKErS$4pq{l{cQAO`&WifBB5h5Z>rYAuVNd=bg9Kho1LZ1C*!vnxZxCnA6XQ7HCV4s%qx4_Y7=v%)Ngl9LlLhsg#R;WfEey9(*lq_8$H>&pL> z93+GLqEAIVd#pb_%=Y1-CsmFNd2R0I{ArJdx06A9g>_J@!XDk;^6}`EMx{pe+U$nc zm!8Hr>z6&eB)(wSIE|N-I^Hv;ca1I!bd8UbPywBKNm5pa8;Wa2&COnk^VKz}H{8IR zYK!ypJ1+RR+V<^CQv6i4sj zR(U!uRAiHn`DcJ(Q)CkrxLnpRbid-02pAFTZlGupt*FtI1LF18Yo1IMD9jeC3Br?7 z$dt`u*P$eK2LGeqraynGu$HKm$Q4cDV!XhUzE|zvY+~*PVOCO8y-J7h58w_@f{nWL z^Q^rd`e$cnzpb-qJMMBs@YGoQV0&j$O$K^2(+Q$^Yw(?|JzFo@0ZaE+E#AxT1F%<^ z41S9LjI%D27#)8Pb%rmR!d{-uQTg)vfO}K9V-Wl6Gu&kpVdjTO{nb<=p)VgGGthU~ zF*{qQ5J5OmPS2tUi3rLKT~)*obK!s*^(~3q+VXi3>#D3K=Y_4O)jf1tgQK_kF80-7_*Z|$SeM7S)?sH% z`Gk$T30CSF_P_q~N_+F*?5y+66bF=GPf&^wwQ4!F#v}eW<-YI!k(h{T#337e(r@!j zCv&|nhhX;GbBxleF*>e$0Q`2>(`+q{t%vo;MLTykkiyFKC9Jui|MmqaO@L*o!u=EZl;xSe z`{O1H^qHr)1PMJsLpOVPl8}@MiHbrt81Wov%CG=TEa#CQ_$iQX$&Zj9xg4XueyX9U zQWjHr-fqEu5^0+>e=KhJ9_}Gz&E1hkUwuAl<_co3qC&z*#^s~lbBR?83F?)JaI(ka zY?{51%c+~8VwLiAmiy*ozJ|UZYE(EEvwC!*cV{EWtZu2YnXkVFD)~BN;P?qZ_jV5^Z0@C^T*hBa&3>gtsp)#t9_v}q zCaU@LUYId#YL|#h^VT>&_4JZPAiY(NW=@>r?9CNZ&;VS@pKkAYm&bTf^BwBZ}WyNP{|{Xnj=qm`xVWMF3K>m$FW=c&Z^aa zX%^{AT50KgWg_538itepyJ$#7$3nQm5i+zIjypp^S$r>cWO)Kn`hBi8bg%&T%JGLC z;CD|77Z;>f8d8wYt0Vu7y4B#Ti8+n#LG`zH$Q%eVc2Mc(Yb(;w0S5a={I?G1f2I2X zzpNGsS23**?P%fYB4EN3w&#P@encO@7&D~;T6S7 z3Q<=6PN+k~>80bfdpn9fE7zZ!e~QnUI>R;gWzMm?6@DfJxF)MzNjRyL>NLk~3^h0>%r}#hfisqyJ6vNO%)i%$I zWIU#meZlsgWKTc!@5+t@m%UU)5~cq0#~ydzTdlpz?b>0U_@iGAEQf348$ch*vMcK_ z|L{Mq7bFB%S=r)BxEjdM-@?xd%gW07ku~@$O`~ng_x&yt`R>QRV@;*7xw@<$dIw;? z{VRvTSsdVhRt&N!TYq~InHoc0%~yzr0B0 zp6(+eB95COvE!#UG&n*k{}A8(ds#D1u6Sb&FYArEhlh}r)#nH@8;2{Ch3d{!9tm=J zYv{(jkiR!-WBW4(2gqyIa~}5U)dKYUb_&(cLGIVM?t5c)h*BL8<_>SS zvCH4g!B9ghLp4S2W+9{T;bUMx10Hc=yE%-)IebZh95W{?^oO)f81nPS9J~Smy*Z(S z9C3_TuPdUNhF0pgJ28JnEY@PV7~7wrIil^eR4;BeV!4q$e2)gV5l|(?htJ_P*!fI< z-(*WlIkrhMwRvZ&A7u|}2rktK!>e608+1m_z<>)}aj-#3+--M|lCEE}<8XrQlWxhd zus$!3f_@UdP1*PzEG&Llytv;Ow}R&7`*V?%Zog}`TkFnJRJ_IN6b*-Cm*4#G8K$ns z=fm9oWgi!%Fv3Xjzj4vOuO?5lWphxm?~te@YvXjRU4G1Jp|f`*SsZh zTWVTrq>99Jq;!~Ta#->Pe&YqYOj%nON=^=WiVPoh1s+Uw`CPU>8f_2V8e1rNPufit z)6$+Eag!&YSvI;*Y7@th`V&5pwhw-%*cS=8dnlr5 zIw2+Cv%8j`#-y5Uup^GuVoUu=F=FjG8?Ziy%IhMQ%T91)4dv6Dr;@|iy@QTo()ji7 z`>#x8hjgx=yLfz4RQp6AiQc(+Zcf>g?&@(8QIG+Igjn%m(fFgH z2+;Fbub^@PWT@a(B_+}A`X1xY4FniWvW^8t3>hq*9IA(|R$Ra1o8PH;B7-YYVSUjZ zDGahA?ax&h{lfdbdT%jsR;#d4-|C(-iM)%~?S+Qsj>f$KvAYDmts9D(6UCvf+VbQS zQ7EM`-YlI}^dGh9!^Wm#B&b5|;A+9v}@-9t`o;GUo_Y+6CIHKJ2^2U&Z2b_fJCKMLjEF?Wkib z(ME?1B|7zNale^g2g{H*p`8<*#ZnoYkU_nDS@IMmyRXi}^X6r$y`oSXDru>jCkFT( z&b)cui4FmYHY58ds?z)b`vy)lX1O0uy+N^RV!BZ6t5ivo3nL1Uo&k4nRgq*yYj197r?G!qzPWo!CE`8k zUs*YSDrAXc27rKdJ7CZ8AyGc_trGSQdlGpS=R@e?xEfIAC_{t?2MjH{JC?ti8lP1_ zyv%eW81Qp#I#+<^s`#;rj$1@CzqILC_27hhG|y3PyxYE#CbFeMIo%cf`9QNEa;aD& z&>HH5M?}hk#KY#BXW#Je0G?itrC{NUpvivWsSSv&!!%;XIrQjPDZlXBkbHeV8$}Da z-%#(*+gB@i7_87k;rg16susVwufI8&W6)M!>Qb*XE3XzFeVqhjpp)Z6PgpopV*8p>oj&JNL7gk3Yax z+*jA%8x-PB(FhNbH(QQ7;w}NHsS&<+9koQ;of{qDzB0kG@o(IC5lon!F5)4bX|BvK zS3*bQWbD|AG|i?l2a9swc+SKKqCxeyJN*^~X@|AiQOfJI#kHZx)295A8xekef6DYt z)aqX6MLC?hlOEY%XRLX2D)NlllfYW|-F*U8C%xqC(*P)Uq3034ZPeAcoz8ZYTVd9# ze|LG_jvBZ2{%lb2{9xg!xy&+VK=c~+V{yw%4w-@YuGYTeufV-Di-RFUoB%x!uhMF^ znu6w=5$qG>PWmwHWvV90M9*Th2t6F>QIdJOB&+FrQJL4SOwqJJASw+Jx66&UbMEaG z!E)ZFUxzQ>;fm)}qoaZub@tx+3`^5#5dln_YE!ncXG$qEI^ufe?BkNsW%)8}zL^XvRe>tnh)QdD@9Vs10%-UsX1UGj%-XgjD84q{_eOP;Jd_1{-Kg~PDdvE_pN#vk zOl%~jS(hzp*G;0ec?jG+bR|HX-M0R(uZdX(o0vyXflr&Dre}64GOG29gN|CD{kTyp zqpTClHTS2l7R*vz+2 z%6+(i@?JD8Y3@^1QKBT8o(Ow59W1ouVY$U(g9G9curbuO$?cI)9N)gSoMIauXpP1l zYF5_$DEx$!^%T&SCiTpuzIOsr{v++MwDiRqKmyFCPAYvja^V&{))jvt-EVC0ZfB2 zZ?!&7?)}RX_xNZcL}<>(svq9b#VyGf$t+C;LBup`o^5&|pUBOH&H9Ap;2SHj$5d8~ zJ6Z5EQV&ODZIA0}u}xlFluWU%VL?UX)d}SXiWh|2|;-hErYsRDv(UVpg_~9=Y@EIO#Uc zonPDY7DdH`1Z%dc=yYS+(c)~~QOm_!yKx82RSv~n9XtS&k1=frA`9@4e_gZf_b zJcq01sBgA&u}Kv&3_8m`i{qx0Qh#z$tsY0r;Iz=o%^{5U;(ig`A_M+CINQk~XweO86`-;?WpH~gY}qCHrd)qeCc(p8$qRTp z+SP0V-rZbDZ-YZc3`TSWX$Dhl=T4}+PI};V`A!5%$4!_I2>q+!fm>K-X3QqW3u{_4 zrRJ#g)D+kA)n;rFtf~4xvldtH$=?~Z=mxv-FifV?7yK{+IS*uS<6Nfm$d5kQ(OVf@ zNS)y1`ke=;f&v-gg^6S_ybNqT0PH*w`Jp0O8?`Ic0Q-Z(@%HKj7O-}IKm6UytufI9 zGl!;VI;*A?Xg{=9RTI=T*KrlBNfE9o$?H$0&7o}jwB&z|_n@x)tZ&MD8#SQ2F?rg+ zm19R_^c+(l4ZLb`A|pN7>wwqgxHHLJ7j|o0;%!R4s187AYO)=;b(mHhH4~I09WgdA z!)6wE&43#=Jxqp!fx&3CH2UsyaUzF3RnEs>kf@1_!VJDgTO9e95DxctILJuWU$JJQQ0odalM*tHMq$@F8f zgw$|0!7m`Tm0uUQKfDIw@5_QI>2X*p%$v@jYP*xq9urw_xu;8+?Ih!FW;gVfJyvnA zMJq>4rg&Dy2!W*?k;0--hQTt5{Q=%{LL~kR(0T)0mRy>b+4`K=W$bBFoI$?ugc2tu zYO6WXz8SK`F_n(giu|Hm>}M^^1T@|l17=*51kpfMD1P4v=Wa|Khxc)9u1T=KsdZ-c zXI@XoZS)`xW4;R54@aO6%er4zS9Mfa&3c3~zFXnc#nD$3%f`P$SV1>A0**ImU_>-T}t@G#icQ6=tpK8_y$k;`T#EbHBJqA4m+;D{mJvToOVNRUFM0cWbI~;Xf0i-Jzq+Q4sxJ0D1 zoq+@sH5h6%()57#d^Vixn$=0{{KvtWcR(b_ymw#IdvY6W*vajOJXGIZ-et0xt2^a7V#Z zhvW0($YdOAe>2Hs9*?re8*DY3i@6FVU%27iuaGd60c$(^v(Rc|8p(Tl&w1;-lWa4l z(-TxPxDUH$b?)234;I1@+g^4$oBgL8O;aP3L*vy49@?kG9dy2OdIfC1iy7(4 z)A1oDvAC|#1RB8L*-d(6(IsmgT3~Z}+I=Gp^xa9}o$0UqhSVCgZVAtN2&TZj9~{;K z3BRF`e0bVF>SC57x$+L2oppZsLYyOUj^7xn`QD9OoEQR<-C7?nool59JYtvms9mOq zAQnh+jwqZ79ELQWDt$F11<|fFS3fM;mUJy0#MPZtN)X2^r*4uIE5*1(t&?HOtId^* zaGTLU?wN9N7fAcoT4y!FDaL{C?4ag#JZCck@NIz6QB%TQ0#8`)^)7*LoDX>FWXG)vxTjg-b(Nd4pT)Dn?-Cn7> zNVsvA4F7m>S0-Wn$|>f|I~gft7hl}fDK3GS>hUWEz>+A9Ugsjx!I@d5eA9)Vt^z|M3=L_e_7C~rkWi>lTjVOGOOp6P1-8GjND&5b> zJq&DR>WA&3ZIuZ}x+;=j-@)0k1s8@AADuNeVtA}NuY~6(&ce6Ee$7@w$D&T_Fg-;~ zfu5~o+`JrWgtm80@A-IMwVN)@2)CE4Rv422rgHOyEvrOVUd~NLe4qyEZ!ZKbYx}b( zofM!3nK`o~vbP^DyKcz6kC|%93}U?A*q{1X!UD8(;BC9y6;+#;+4PD@GGtHdaYrc{ zR+S_(n6QKKha7DCqn)fmpUWN(U=YDZE0X1%I0YM;WZXA~h0(_9wO%%BQ7T{7MXsLH57 zETF#c>JJlKg8dv^a`!GBtPQ~g>MH|TfOwXtI~jk)+&Sh6fAdUn*09ehpaq)GVJizO z;#1GystmMXouK)qvN_Y$VX zfJ)3>h5=}|>iWt2G+BTlL^8S^OD6|j(>UE=EDpdQWsHw!>4&bDFhKV3m&GrTuc%&ch7`puc>753=udbR;iWDQ9YkiCcnJ%I&ZVk#0>&k zfA~%WF;_ECab7}sZyp`4)_58q0lMxM`Hx#A6DcJEWBY+kq*d$F)p<3g9xV29Q?;o% z?@Sf(wN-DNrVv537dgu_NToZ!l3L0y@^rWd`#Xu$p}nQn3XU$yur<@B-vC`CF9W5W zyMH_lTtPYFkv6U82IVg#ev#tuOl{b?2_qsxDR&NhXyh1l9PQ+e)U6%`44Ge~Svd_a zm#6z^vo|X(so+P|-q=&=)i+-Ei{Z-1fOh9k_fo60`Nc_26)!$z)8qNwNuG%?n4=;P zWp+D>&4mUaYb`srs2`n$LA|2xakDX+wZMS{kh}`FMggA;B?!0f)T}O56`~x{hO`l` zeTF!Zc5iC6?buyZk?@X72>6OyKH#)I1ZoR>xIO4+m&zfPGg)tY9IFJZ%|CF3tUBGC zw6&g7E!KQy3J+xS-jo2YS7HsJIkSP6lW;a>*wqe$9pzE z8WFEOXnt3l>G`3?L%za-RF;5Dfn->#$%Bt3kc~rlKEY;Zhz6VXD2SRph{s!IzsOCO zfm%5j4(-{ZH=HiGis`3JTl#z7mFrt8*~jG=XFc)2?g+9IZr;uYkMW1gw3?*dMp4dT zCeC_|w3Y}*@0?XgixV`4Ci)DOJyE^Y+rBx5wc{GJlt3anwH@zn|TPlpIkie(x9}w6?MtVu~JhlkB_y2nP`iQT2Y8z?vLjTR==As zd)R#4%OjvpYTX_J{oV##nyqI12q?N6g5;u8t#{P*%w6T0Pz_PvPg*@<9L`X0IQ#V| z*K@?`)#C$hTKiY!BJJNQrcWcECdU}Y>4xMTdbmrtP3D&>lgL>-KG$!+h^8rp`E})FJFUg&YDPS1Qt0@R z$#$skCd-8aBEQqs_x#+-#`?zzg#g@dFEr8eKrJqtJoB9Xxdxlz*{^l=4 zG!Mxzj=zH<$<_mlu*@kL>^!V@UbRQkG&PtT^vSddW8*+6C{Yf`sJ0yhj3%=h_=0fn zz*8%$W%JAS9il!u#n(-Ca-z|gf0G3Q2RJ z7#Nw;SP(O5Z-2P9HjeT6FRc68l|eGb!Q9j0sOApRoF|AGf_Nsf9+w-Bz73vA@;=%B zo2J-0m1>00W{#w-I_fw<@%Ys8!`UFlifw4O^bm4ZCw~_f!~S5w?E&>nO+SyC*5h4mCG4Qgx6C1YB*t=3VrseQ-(6TvRp6BNl58UGgSux8Rq3C?ulC-Y4P~mkfP|H_u_Of9!jAnCmI(i1he5JL~Y*E!OIY z=QuCU|4s*7VbJ~^tBT0l-Y^JX;-#Zz`k4y(#}5j*AZvVE>P@oM@?SV2R5QZnq{To6c8O$IvI!#kv?@>5w4*sraSyZU zxrAFkn4OXoM&lTdqwH=CXs0+#C0!fOKN$dD!vv36s7tcjBv$K(ezex1Y#9i4_D3i$ z5IC(~D>ON|R+&-KLtUPU=SrTE;T zn9saC#|M=YO&Mo`xt>unFOZ;$PKbsX+uX@E9$Hx@nm8UL|AV$S?i|EO9_cb8eu8mt zgxX^=kVuQVU0RsWeR)V1kmL3neB?vLpYQ6P=^*ru$->0`13~+3%1>mim8~Uej`t#dp{9DjcbOC z&r%*%wz>#)yo$PK&1ciTtgbA*wzJyCp!M9(#4fu|UzvU>y&HbyOQuEC2A& zbay_&DcA4r9i=R!U+>DnNI%amh~~p(H(aMj`*6Rd)N$v}q`s>)-{86*2PTBl9fln< zDuRMvg@WO7w6HSx$guIpJPzgQ(DAr1_TjnQNu0^~a}r;9yd5(++G04ZHQS%vX?dSU zm$qePM6UBo_2|Eb(j?(=beK$9yklaE| zAEf=jhmTv)YLC`@r!V`XTC(v@n{{Qop3Zb_GO?X==YzeK-n366`_|YN^(h$;`1pSq zx)UZ6B+9t0yOkjXZ(>$$?*_1^ z!C;mqciKDhvyd7u71{%n%Kua?w?$;UiOkzu@B912QyhsRDSu^Bfc#&x2EmR$r}nG( zeix`-fJO~8wz49?=T7`B^zQ~o7f=voRT2@_f<;6m$Ne)PD@$}Tm*D0|P2i{7yD^@B zYpow!#8hNu`|dBBP6lby$;Pny0O<}!v8AHfIl;hKh4TO2jJpi+)}gycyN#~R+rOPq z7rh*D&Yzpe(B*DDJv}nYUugLjV%NH2c#0t&^w_Dl0A)02wYzd=vHHI;_2aFblthGg znacC^8l<8n40P9fLIk6(Hv4pJX*BFhOPS5M&n^R10|DUvNY#q0;jM$z|HzYmM)+@@ z^x=Bm^#79nwBzFJ_QwqQHKSHHxu2l-cOIJy1{$5K7ao(7C97%TY%$&OI72d%lZ%b9 zd*a35UaWU(=o27MPP9~2UFK(~JEPkkwN{0e!w5<5I-f<9!42`&7Q+C6-Y|@&S2H)U zp~4{uI%%OX0~p4%+PtI+UrAK%RV4z_AjbaRXo`$0j~UY9u(I8Jg99?i!m5w&1!xzu z46n)-yuWFSxZinpD(a3jh?d2C`<|uh9Z{jeJNOasMN$mYYFdR7l=PrnRbzc~)n&3I zdTmA-3ry}xdkh1j>DB>j>>Af?O7;FmfeR%1!f?Ba1L%+fR9XYXdLuMj6di@lla!-d z6I)HpB+OBDGv5rB;*IuU?AEoKciLFM6E%NQNJhP>^KRdqERs*qzx)BmT=(g(GMC9s zbg!SeaPu-2Qb(cY{#ZZ1!_?lY1+$1iwuwzEx-$w-sXZ=N@InT`a2Z`%!__X5(Qyy$ zv!%9uM21dj;#}=YSfpvz&a?G=!#A&$g8xrEVZyXMTju75sDI-{RC#D5?O(!vjlV_m zHN^GTE>DGczxF6@a=g3cazt8Li?zvCn@KkiS4qoLniAp6!w&*W`VG8pk0)|I5GVX_ zWoF=Hf4S{Zo&uvWZ)33l_i*;p)nd_8E99K3)@y@ok-PS#EB$(4hiFKRWn4lCz)FE@)L3A>b%xE9){Th2vvgY3@u{&Ltl8}I?dL@@ec`X>!K!3u z&-%{OQOl@P@zJ0BiFoZ!&UO5sK0i*CsJT0@6GhllHCx+F{5U#$BKx%*c!%aP7{gQ@ z4FrKsTC<`(7A8^n)81=5{G#5OS(!hQy~l-)^%~xJP|=hNtJGSTfJ-D7YT6X?D>djv zwQS*Hb`80`o1*FEU2d%9-ko?ANzjm){w3&N?U5E!u{n!xZ|}B+&~QkPb{X?yf54nW zDE7}Y)^K-^LpQo}>MqhpDv(ur?9vWZiGH}O+nW=2oD9L(a~Lkq#!^Rf5y z4GjEJi8`8A#&g0lC#Hl~BfJN#!Z9VXPM;l=Wk#t5foIS09yN36Y-3);@HW!CNPN@n zfi6ORR#_g#4#X}0#qmuNlEN+4sQM>`YslSz4@ArkCzg}su%j`WOyHS}`Cv@;^2yTtw z4Rfs6&5gCJDJfX_)GJ*N9D3aTMSCvo770RV@MDS4V>^YE0Fv(QTJ;hjK4En!X9~To zh1VH@c~f&a+QmS!7;ynbc*K5}{rbUg>{3i2kOh~^RdUCtr~tZuvE{L9HgrYZ)IE`s zDi+NuqhCOT*2`vlOt9LNv7CVL$%ExV+$h`2mJak`drNGdD$(BZHG=DKxKB_tSz9bv zIKO7hO5TEk8d7i8#|Qer$F&b_vp05;p6=M+Z`+L~TKiAHQ6Ss{3&5firwwg@z^*Sc zpp);vtMS@Ac}|<$?>#H=d%|^iV__Zkouxrdx}C?!PY+6L9y`_V@g67JSM!|G2p;$0 znyh>42zjg4TNd2_&XSe8qo}Nl8Zo}&<2KIx`yc*9tL$kxutS4oBvuRuMJFT5!a>Gt z!zu1+YOO>i<%4|{QS{x9UV-Sr!zoGD#i6kpAjj(YunzGsWP7nxy==Pk}`T;Uo<%l{F}9{ z`&S!K<3VkR&~Y7A57VPx<#4;K!*IbqbodcN%wh=kl$+);{OleQw>V{?XM{J!|&c z&nt6`@r>d8y>YX(Lb76ZcVqBvSga{T5BH0xTZv&n^pH@T$Xv(h=a9GIzP`@SSlgY; zT89mWZ2{^lZ`Bk>H^H_UzmEh6aClDPKT@Nb#OVb{4dFK^Cfy0>^4ztaQNwc4{HjR4 zd&rVbZHS70Q}cT4#6m!T$@wJ&k^fzh=v_n44HBFxh(IP;0EH;ixE0AqwEv2F+pt<1 zd3S2tZhho&aXb@R)d3APYF)VWB2VhBou6_3?-u~6PGSEaG+1Bo^B7)7f&awJb%olo z+^b#Qw>Hj-mN~>WdE`7h*Tl$jKfYa%c+>FN5_qXjZ}F_qZ~t1j2wnIP2lQY1t)&pm zqcEDOcl8!Ll1|${&uUn7#%9xn_E2f$vt!CbpZI`tBHh-8S{6!ymhTk$Z|UZW=KAy0 zt`F$uSYnTUeiKbnu18Cth=n->CFkil;hKf9qX@)E$HSg;nMMw8!@{yo}5>>WsQPzwOfnSKG`r`C#%p zH#fd{#wuEra^t^_Z|#HmCEr%35smdgy37(XF1z*YCC#&IXWiA#_J4;*qzAAdwCaUqV`a*9H{G{0H>4m0R@Bp zqk0lf+$R}YT8>wdq8J{Vsh`h1pRWJcu@R{Y5T{|Av$eg3}f z(};U-!Q?%4c@^eSzx>aG_5XsSZaQn{QFI~qCZ4jRKv0Oq$~!TBc2C}uC8C}L4#b8( z)nXHGk%=|=ky6W#f3}tkEy7atxLKU8EX)cI!d#1$(pV}^9zN<4EIL6{Hre`xHC5e9 zc=TU_4XF|R9z9)Zh1<_5jfrypiUZsEYi+Rwtp0`-?Mq9Vmtz|8b}=N7ysc)8wkH)$ z{lapGB7GvwTIPoH9lrxvWD-oNYid~zk&vR4r?1ln4H*$3fIq)ESefDl`88qQ?$V!TD^~BtLbs2;s90^$yP8;DnKs6~2g%}^hqyQ!T$iG#x!pB zRYY;OC;zD$=4~w`cw>2a_KS$3S>@W?aKDJn1sCH4zyjG=6DVUt+hL#`VJW;?GkR2=T^9Dh%{!cWoyctF4K3P8>&pw9BD> z@ZId8Y`T9-d#}wu2L;DHEQ?<^k?r_a2tT~{0;JC2uBcF@*=Qy1bW$N=+V3ql;>nHPF%OD~4_au)a;fZl33<-8y$GJS{&&mob zqu}}+m>xGrs|~wu%A&Q-7^1BH;1wdmWmZi#RR|!Enw3Yl#HZs#0Y#ILjk_4d4+;O$ zN%;z}9za>uP5aX5go8Vje`&4G?s#Lsa9Dr3Z(y&NjFpj?+~a1mvDEevbickaQ_DIr zE*)P4ZqG0Wz>5hxkUG}YwJ=B}Dfl~?js3~*=SEk%ZxhMbS=4??y>pFM7q#eGsk9Fs zpzC=L+CiB*9G?fZjIpH#D_i4&k%Uz_pLp1-N0c!F)?r6P6Se7MIU?p8{hVLj)s;2+ zm5|+6L|Z!6)~u-C%&1F|`UHN4D#bAq`{9wqD04~<>1+>9C9ZwBM*bhoJ<12pL^1O*;FkZt{h3LZ8M97*|Q1o05n6_dJC#*Ft58Z6MeIA~buz9HK& zbY8S8z27WF&f6vgPo_$#tV9fdsluuBmETOSYi(4~VmG89)uun4b`KSMNKr($#224i zoSzjjxp(VK)9ap=N!ZO%2pjr#2w>6WDraujMwHoWDWq9TXKtiq1&26|bk660DA6`8 z`K6YY6&DxM;GexoZZBf47#|m>8WyH(7AqtK9wl6u3<{#YS*nco(9en`%zztx>s%zQ zcd3WGRJK1|a;{%~MgfQ*Q3fIFXx`>sNBJ5rvY6jGGcywpDN{NET1{_Bg>=>3-#%vS zmT=W>HAo0DZ>65b$=9WSE7_(T`l)P~M<~X0!m{2}deB?sH{nR^tMkqk?7i*`j1y}I zBS$fN^O_Ac6c>RT!V}!Q6ac9WxP~)cnu%%SRdwVe_8>HOx$+Xh*$Vaxrq#8YS(cgcusXX6yQ$md^$S>TsoxDx%-`HaS!ST4T&WguQ(>;S3Z zBxfroXD4&^cp{*@qCg`fE#drrQ%)`iVtr@(3vLQ-33knKx}Q(EzaB*^<@~dNmpL(R z-!05J`*N33Y%KW?1lUgvM%j4&0ptsz5hqW4+A7DQNH#vOp{umT!P)e|6&ws>Q;3Mm ztpAw2EWq@79=3u}{Q#%EQ9ko=Zl{Ti6yCX({{F*XiB`W70WzlhX#6xLCDy6AuI#Uz zysWxNDMl_d#>9v`%~${cT~KX*#iazqY6TwVd~fFF&yiMxl>JzFa;Qp>hG=q z+$r)@#fP-wxh<)(A|Gq+F?65+LG8{!P6$jV{fe+^DSwN12_PnWt5g<#*O~4!)c{uBrk1*Pl$U^H_MizM3+^R z;n8U8?Sj}71tYNNmbx!b`sx=Ltl~D zicyb5z(*nNIC6G21uwdq8zx8=BWXgbWT6C~j7JeOSpO<@>`ES^Sd=4O)Vv3|5)n#q z#}L_-zQ=r}<);?Q&ggORy4bLz5Xr!mAl|V5waQd%|8ZI#w@_PE)Lz?>ik}_*crWAJ zWd@f@Z;2$`^5DWv#xtUKH!`nkRF2^uS0EPU{sb& z@>Rki_O`?Lz#x}}x$&WI`UVPiv;7~E%tf!;N~}TT5#_uj{$_7+uGup6&dzy71)_FO z5#vPd#*z^@{j}@C7}zaR1hyWK&80a`+?cM~1aXJK?4Rgr!q6RO+57KD`3p8@>%Mxc zE~I_VX-f&P4*>)G@o&Qv_CY}$pO}-dc<`ataS`8*0h_8+$Y2RHGbkgC`^@n)Q_Ao) z%kUxme`evwCH#3UWKxl*_08me?#H(Xm&}hgp$b{#&hnScvm&slKm04(8OYj7)-D_Y z4IC1e!@3sR$>k}%$JYWvH#^dQCdOm&8fI7~nFk_5$QXt`#SD31i>rM!0*or5;eeEU zj^9xJmML@zNWur5mI%FH+-0;HB>7d9khQ)vvM3iAlA#Y1;K>-PdNds}qswTvVM%|# z|4bwjqL-XJ`x7ros#odT$0#q7x<^&xpV;M1Iz5`ta z81IRTNKjpG$WG`SjHxA%>&c;FG!$eQ-+|nK${cAAvzHO-=!AD?v{8nbb$|^Y1u&}C zfCZXlVF8T@Uvh_?Ky#%44M;(?kKS8+G4^#mpk%%HCW)=epjij?`ycF=Va*(n7b2ld zJi5z+gFFHp$ZHS_KACn9Tu`NKav~xlOT0KiM1IlK(6SY?O$U5+5CAPs4^cRv6h+MA z$&Kli_hs19btDI+-$4i6|Iz76}mf=d;L!y%Z5szwb=czS2*zDR^ZB< zmIbFe1?L>FWh%-}T<9)e!#O)S4+Xf0xR7Iw9Kj3LvTE|XduCM!E@NhR#eIA|407MF z-&br3rtc0Y%@v_42GabvCs^)V93)#fB-`Kv;_ROrYLzJ*FYy#)N=Zlhj*98ka6-uCjDrSC2Zzg7 za=!BavR;|~o87Kwrbx1v6v-glC#Z`osHHU?H+O`Wg-p?twrPM&WnNwGNa=6*jhnXb zbv9jAk)53y5)fDaQl1i6kfQb1(dgkOL$DySo8|}rUa7WhKC7ydYm-5+Wk|!KU6^or zA1PshjehI1wsUNg+_d1J%q*!(SsaHyhcZP7dcWn4fc;vE_8#NCK*P8=Jz&5G;t~Q9 z09lzbUXC)IU z_R~jnDZ=({c%xX~x=_ES5U++QdA6x}19f3@9M z$@yY9n9!1>`DtECo8$Tk(IWU7;ZBap!aJ&LpJ)z6v+7^sO`U`s|2B^v_%&*`sVYQK z`n(%eUES#{i0EkaVie=!r?^&|Ck2Z~+3-M?9*jeIegCaQbp@h^5CDoSsDEp3ut5Z9wtBLi7KmL^q zFa9iYorqr;ng{tQL$g5jSWyy1xKIDP+> zwRk$M-YPIDc2-+OfJ=m(RgHl8Ez%fmR?C@#vz))Byp5xgt$S{+tb?CE!a=7?D$=@e zRlYA5lwkK!Uo@Z7TD)H{7Iy-Dri^-gL=_{186!bV4fZ{hb|frpq)C?8Ys9Q#Oy)hL z$uXp!6i-v7K9M*gh)Y~ab#n3}6q96B85)$oM*a+|6>Q}!#l|n<@B}Jb25v;t>`Nb) zM;xa~Bo`McGE-Cjs1h`F-P#*Zq`%6n)048pOff@!C~^8PttertS}2b$%FesEqzUDV z9dMbt;RhO}K=4(~PbHll{Wi`Qy@LXX`B;p7m&(|P3BKppe<}~ximwI@t8Q}ALF;Np zjS>~14zirLaJR_RoWIFj;fp{gT|@{*fq;7=++VBl=jJWly^5_oiPAN{RH#V)Ejt_XR2)XTq@m>=u-zYm^A7QAIh<>c)xY1IGRW+3gH6_O&(qm znXJp*P2=*O#J?hln=R&ZNuZ12TS-UC6g6AHvCE_&SYoX4d4$v?%*6PgGaaVMd_qBQ6Q21wNK&Xh4AksbRCW&A>VP=^YRR1*TF)Q3H zt-)~hHc?|j-77R}mtokm=CEE}EOtR;W2SjqrO6ciG$_9p7Eo?oSt3-@xKLqVPy1lK z+~`Jx>u8j&PocPpuU>bnx(&ZX$TqC}%LFUTrgj^QRhw4e zs1Yk-?+gyLt{0)z6*o3l^k++zl=NlHdpgp^9i!OFgbwtGf$~giMH1$>RAaRIh|g(h zDld3nih|j&ZgQokQn9wUl2Xu#l~pugo%vei*<+KHGjG`#`8#vW)8i#_YX)P>R=&$i z`vyRBED`K0`*jbJe_@}ru`A6f62VA_r{DXOum`U)TFx;`^_<>_ zKdOZPTsB{*&)DvLckv={ zHWpJNGpMc3!@RtVD+&V|x6`UW!JID<1zYcuFcbLapZD}sSVy%p$C9kR4WIhm>bghRcemgaOqDpSoqa zTVjKJrg2|K$G*_LeAGbhCn)$~A%-JQPm3~oJt{v{IZDQPe66UGB zkmcx!lE=eEOud1%o$^SdkZc1$yMTALSwK$GdMf^hAmC?MM7wYRX%5^%g zknds}UGcua6cFo-s{4{}+xn)hN;=|H8CxyQ)Z9*FjY|4qBD4Pe za4CHcpL#ZLyb|!bOk2+K5sZFjsdOxXDR<0OCm-}2llHhl)J=O`z5a0}ook=Ou}kt} zM#7;4xmE3?EdLsD9KI`SXA1u3bKO)Y7$)>!pQ;Br^m2jigek7Qe@U{(ymhRt=k+k5 zMG^<|lody#@97TYt$z6B$DG;8m9n0=<|`ES1I;rcD(E2}g~V$WUNYbL^@pne zXaU?#NlS1cJrI>J7jaSwVpyZCYV87%4hqW6V~+iN#x%AG;d@Er#^Tj!I|WxI*pGM} zJKAwR2&$!$>^Az{_%s}Mx~H;Kti|=?L$tL!Gs5^3;r}~iJ>XvC9wwB@u%`3q``a0f zLaeKyv;W`@LBX z%DyFxphoHmi2YZtq9B>?_sN|T9}eyZQ>(+Y;UEqqWJc}AQ*^94bWH?oUHJbB8i0r1 z)DB~^Kci5{(hD{f_oon4_b2!Nmc3_zO0ey6wM$~cpyCYCbu~CBAp71b_w*X$`O!DM zapCO%yyOkTg@6$MJz6sw{XKkV9;Qag%BP5)o12=G5poU!8sZ)}hHC<_U61$owb#~W zM-D8In|h5{YVVL|-UoFbwR7BvWxC<6z|X;r<5ACmHTmq#{O-)B4J8ks`=x#7cKJJX zShoqE4|cWR@~ErU&z7;`hP4<5*f`dn(%ZeBeJEF|Dm?qUC=F z-@?L?-Gcs|0RKE*285d6#zOe<@x6egB~+uaxht|-;N{QOfxIb7&)dqLuVy#=PPfV> z#qXwM^nWIPS14X~eEXpT^Pj&*&>Q*l7q_3nOuv3A&FvZhd>W5r#h(RM50bT7HPeJ+ z@nz?>2KRDgakfr;d6C0Dd}M&V8l!%17~waC8MhWwkC(ON3quey+PU z?p)`K^KF~T4`haB>FMX0Fu)uolmNh$^>iS*VhKtXQ8tQX2=&A0#?4FG zdPS_tF(B~H?ja=i-iLru1b`ac^J*w~(f9Dpk0y(PKbR%`z)=RlSAU*&BU}jCt>OnZ zjahB*{lf{pe4C5XmIqmDUB4Rnpg346-v1i1&TUG#0JX`vxPDvtG}_cSYdyM;PBFm9 zf0Ie^G|cZGJ07g8Aa}60{dpKxeye0tI!n5x-7Dwm z{JnJ+4)sQ(Zcpe}_HCb}8i}9n(aNMe6!9Y}!DqLl*#$>@e=^s`r@C7F1PtlC7wXpW z7n%m)xoZ+maSYcPE4DW>r>OXaGsuGYdpF%dwe7eJ=k0Nf@9f8A zi79U`*D&**(e4LSxZkgVu)HYmH16P}!}ZtBVBr#mv)yzEpKT0zQn;cC0w;Z2f?bw= z0lj;lnxC=Ps(-lZ3`S`iWAbP!TGD$(wxac$_sP-T4L9&Zu~M-hsrw}<9V`4WS~Da~ zSTw`q@2pR(LR0?1PrOR|k2Tv&?V;<_<{xXzY#3L+9A~Z8FP2dtQiC*FD844?ptrm_ zvjHli_Gu5DnqC;)zH1-!CFrF$3r-}M+IDAnq@4y7UE_cBhQEU+2(inYx8IE(MtWM+ zV0(v0iM*e6WUcKE&~JF^=V78n^}Ui2uXm|&7{?f7OEeXwsHR#57_sIk(Y1I%TVCn5 zs>+q8@LW$nB6NykWT=1N@<*I62YsDfu-JS!4GU~x=9wOs@A7n3Agf5&f(Auag^IeU zVE3O&1*0Fdk{t8AmA}mNEv0wm!C3p!{#I*V*R8`|+V z;6|iT0Ykp=MHPcN>{1|^XYg#Qo-p93oWhkj3Ntu4V3{Pjg4^xnaif}w<~cKQ@O_)F zp649B^XV=1q!iR7Y~D!yqvo)i5K#n!8Ku+g;Tz;Im^o5!X5CSR`WZsAmEA-JRI5Ni zo+5)iz_(F9S8k_;dq9U-Lc1tlfTMt>C`1+C3_O9 zc-bR1SG9AT)VFswFR~CI8#OR#JT?@D1svwabc>81{GQ!ojrbo%w}Q>l(PMHX1}>Rw zTGJK1x-zhv_-O0L_jP04+)iqo$oEc?L`Vd@zgy-(9i0ywe{kHBO!swoxLp3U^=$Vw z6u!0IjbUdubSH_;8hV85Eb_Jfv*G&fs z`=h2%ZMSLBn%ts2Oi#I*`Qkfdy_%H0>h<4Ir2O8`wA-6TfizDOG@(cm#6Fh{+}8#PYB+ny$v@LTGqqXUA!{s z4xkX#ZMZzGg~n=gc-PX=JkFh*m%SF@8~MMZE5k_^c(4gF!D!ApZ_Us59)Gv?gbW*t z>I>(%PY>xAr&8AmIJ=O@=)2rbI_UT83qoE;^e75{+Sq?KGO@l(F*_>MItlIj4sr4e zRPfX^N1?MMdpSFf{q2-=N=kb6VzE+@fm)2THkoC7$tQibX6qUQ;$&e)&77SzTB&lZ zD#bnV6KuFk#m@CKha&MuBc<)qxiAqaHUO>oG{wfDXnn6j&tFkh>o)sRDd@5fb5R8> zPxUtG)A1}&fd2r}SdyQ1ae1eel&d+ur0X; zD4bv)4>1BWtlKrbcURE_05mC0OuAeP$w-kRA-jin}!i`;bKVnE{t<;t@8Cpq|`m@5Z&=^^HG_98}&@F`CA5q zEEw-=tB`+!B*C}oM?Gu6an%8pT8nwPBSFj&hHk8CZ_}z(HiJ{3$D6fNWj6Fph6lpG zc+qU;vQwhIt1pW$ugb$T9>H5{w6(04b|-7w zwyCrvl0$a#>rD2}!C0cHN%pcmiyk%_OZ-@Wf8+FYm(z6B#cbC0i>1SMq!eZYA0t+6 zUKC>*%Xp8^O1|lUeqylW8EH#uyyv31mwWY}lT(^k#Ff?laL2U!yA(H5w&E>!qa9yb z`nFD!{r8x_GG8JI8~4640pJ4`0u!3Fn9j!lahzU#1UpW{En@BA7N`9|iZ#1!$#A7WZa-;P31c$>MvBJUH==*RPf9WQW~7#rtz4Wy^-*yE zEcOH^=mp$>o6cN?6%z~rbgGp<3%m zU*y)igOK@Z<6B*CT74#sLN~lGNbLoFmrnDYmV69a6Ea(^z&+n%_;mcq|I4DaK&YPK|9B+4miu#;1bnmj>Xt|vF7ZGE1UGy;zOKP8P>hyZIpG?2bQJW!C`Vd9OxRnRj5 zWW}1-hs#=l02lMca7y@XCFe|y4%4b+95f&}H$|0zb7^9NIB`?<^`x$Jpc{kAcAKH7 zs=l11JseWu%LLxhF^{FOBzlmrg(Im|q4=S5;bE!b?PV|WWs_yb3@ad;*LYSE;lq*t zKE-TchTK(+W&b1*EJtbiKKEDnhq$k|P*h_hmo7HJ(H-k4qBZPkY!v&;Bu$jpY$eTh z*nU%IcBsb4+Ym;vT!_qGR_Cnq@Y(Iqsn7rG5%WV;6(T>KJVR<3QvR{|$_`v8zwB_| zD?gBabXVJZlZEK0cD}ekqRp&>z*FDLb|e>hzMLU{fg~LUZY#QLP=hhFiUuP-QIRCh zyiGl$&}=Bu%7Dk45~nCBk~(Q(n9tf2of1t8q|WFW9_vaq*TeF`1I9liJYBe?q&Jn% z)iNTiGPQG4U9AzNs>Lw69_Vn@8TnJ@T-&s#KQT3v5J>Us`iQ3ZGVC9oj4bmE!0`>@ zB#Awo$?Vr|JtvgWn8%)G`1Ex{?6nD0AEMb`X1;NM%Y8;MK>@pMOeJ&?`v{y~_~o(M zylPmFICRiI9nBmDmY-jsyv4t^GU>JY+L?|>q}_HJn$J15U1RO|O8zBmI1|#)aG#yn zS|&VJJyK9Eb)A;DPynqA@3SnY5c%?(N1Jcw4@lYl}n_p^&s)?0YybffPuh`zL^N%maW0s{@Xo(jP*SyV5w@M)qGQpuB4I7hY zgIU-m!ZvL<1mG=k_ zA9^4s@Ab;nc2`E&L>zpnK1Ey@Vmjn>AlgE}7J@19Sw<1mlRpo;B?z>c>4~Mc5-TJF zUh^bI04Tm8Jo|2C%`sA4vTD&Q{l{5c{K~K$c?i#pr>pN1W#@*JBuw55OmgWgnU^lP zkeOK-g;YWrgen#fvz@t6rKu_;slVA;eZeR3csvU$?fNhIfa0p4YV4=@SwAM)Hu((E zk)%<%)3bG!zay)mw-nFs260%`dv7yAA?D9|ch6uuMCmC%vYue}aXFW-Z~&gzaw&M! zB~MPWMoW1GYg&=MpHHXhNbe3ND`WG@P5<7$QC{acTM{;V$!k8sc6GM27Dv2zs}K(s z;DXmg|A0d1EsJ`861HE}AwPB%#K$qKq*C>+^K9x8zC%9>*D!b8WAQ;~=E_mnP9hjb2k=JDIQy;$Eqlwcp zzh_JTUy)38Q&uOFLyXJ0gz=HvTNuUevf4B?+oxWaPm_14%>!YuWOUEx-;b7z&%*L0 z?mBCpvf7j0?B++V;GTCnkCRX5Qiy37I3KR{5CDuEEPEYCBK722_O6HQ#*K!)f2aG?&btm)EUh#x#zgO8VvN#aMA0Y!WSl zpYJ+oi@Mbu8T>taYh*tEtqJViu`Kx>e?;$X!gd6Wgs;42se%4^Qc}a(!e3iniud#* zIRyNoNk^%M$RO*;ocn8dDH!xOz?J~LB*j+IN~#BeG-VDucbIT8!+AgEmtNcVZO9>o zCUZd!&eWzJ74_Haxr&NvvmS;e_RTC&&NzqDZicG_FP zxj;&CM9jK-5Ygdb2R)KVX7g>H2Yqm9w6ehGV4roc0*O%y zdlgmA@Re|*2h^&o*LT*fBu-uP&*0a0q7F5Xm={HUo>Gvr)$@rhpaPwVqCG`bl|47W zi|0<(r7lGDm*-1E-=}Ma{QTAaD_?KkqF@R~kWzKc7*q=~+LO{)Wk&=wZcYI_@q2PV zpk;NlHYvlHoPy%aKAq(2O+0vT7wnM;vDeQos%yp>XQJnY^~Utxa2(epN*lYD+JYtj z#C?SM`WH!rGs}B-hC$|`@!+WAgQy*=Yv2CBi0kj$SYdYHM`1nV3j;;a{Ia}i!Svw~ zdf7$I03yv#ZxV$VXWNPoclvw5JoTli=vX^#^3j?o^Hh6^W7H3$r}o|IK76Zg10rd* zBG+XK6n76wd)S}5CCfU0*Zh&-ua)nLW_{s7ww96mN(N1o0pg_YSQYazCxc+t8{^c4G>fGr$wDuIn zK_}^`bCJZXW527)&Ck|@>o2&1e}KAJvlWzz!|HNX&2OIMqT*{U`^VFhqTwr_PwLf2 zfG%ocYh~hlS;OkQi;@Fovwuv^QC)t*r+{p$y_JlrE0LW1)jb&G!!Wz!f!{2-ukR_1 zkdV8d>m&{^qjuY^ew33fRCt{4U07ZgWSl0s&RO|cFAr)j9}c=V+AB`@e4hS%xZ+kc z$7e87*D8B!1@|WkeQ~8ON+eZ3I*ipcHs|`GP&0#bHcx3P=!1L`+fhm0cfH+JM*34b z#f4~Bq1UEZEk7KA+myv2xCbP=N%gK4Q089O9HUBSoG1r`@VJaB@?Krasqj~28WvpZ z2q^I`Az`d7w%XG_Kc>epbS`aJ6O_Y7uW+eBjeFb{_!$V8a+hd}&H?S_`>uX_B@l_Z zWY9pPBNMZ($+rdGC;JF}^QHCX#bNGc=Y6Jl z=QUso|9XgMuChP5ypqYnpRa*x*TBY}^R8&EJb5aRV6R?kNy92z3BqXFZI6JcJqQ5a z=uTE(Vb?#amT5yE^H*^qW+JaZsuO?hg9(t`aG7kxKkZhwnCiil5l*{v06P6T3Z0O8 z7p`JM<6$ult^ym?-udHj2#McsAb*)zL2Xdq;p>*f!T^MNd|yBPw&Y(vby@hF>#<`X zEywRpcpI6XBtM0IP(HV8qXhYwoJfl`p=lL-K1*9Z2!pPgys8^~P*f;isWl1z1>oVE6zDm=o zD;(-`WD-`rU%yBm^@Cx%rKlQ#59gBWuq*L#&QIB*7}`=3%M_Ji`oTRbyu-=(2cnXP zsy@4<_B*hv0NYx4d6RDX%e|e@UBnkxq`%a_;DRAN&2eW6*8|9v!@`s1NKT*N(G)i5 zBeRpPI9}LvW+!&4*O#Xs8qXg-hFetaKQ0<@)OJwpEnCL`CIttEzEA34^E@hb-<@0; zH-ppTc{8W}!?JyZ8&y-R+`5}&qYM|ExUArMWDi@0099GCT~5;Mbgm|^-5ds{Xcw(Jhliu{-to^HqMU?{J~TVo zZ?THn(xx+Jb0}MlXC%f#=zVidq2hZUWpJ|E^$%u6KE`rsW-eAota&bTj498e>dSu{ zBB%mX8K(B@Z4O!^M*88TSUm?y2-B}o^kIRTgJRQhw@juEZAwSG(5fNXZMiyZg}XLiD)QHafc==)p5MJ!+tXT#+voJMKN&ev($Xq;qkF zqN9ZK)vQO1w9xz9<5n-bR0?!BTXO^|qN8^$yswwt5W{??SJt~t)TOB4B{7j&&5cd*} zT8n+Uy%FkXqfiWgdiMp&`WMp`br`&70LH|a+B@5;eW&IM4kmhRXl}{4HAi|gl+g*U zbxExJ=tVkK`(TJlaCf@gsKmSt1T$abJPGhQbwCoqz({iqG7Q28TYWOR%_1MwnGwTI zfxHk4D974yYiVnbf)Dx%8IWIEx^X6etcmt9>Iy>y=-Ha}TXU;jeGH7^T9(2EGR3?f zb0jYA!e1k&aNt}dDt`#RlVZL0QQ5)f0);fCO=w_Pshky_)b!`e?!deRL`ysZElj)Oi|0prvJUHiNW|@f=`Ia3Xf8-+7+Od*3+c<% zq&)GL|Dp%~!y`flSbVBqlg0b$-ruuFW60P=?ms-i(*~h|G^VT_LGse+@86u@lIp8J zF`;EYS72MaXu_er=mJ9{^O@-Q*HlU;E{|= z*)H2YCK(SIi07OJt)xwlWZ!SZ$b9*O8A{9ec-=*k>DwRcZHt@l?dt2PvQYNLA?Sy` z>!<=}CXu`AvLyS>@!%D1UTr-uD$zDeWhdqKWvkJF8{gkfs|aL5&g|%ajn+T(YzOma zdToRt9h4%x7RVXRr6yF#nO-9K5AR{9hn%Yw02$5WV{_TS9f=mdudu>A`w%%mMu?o^ z#Ky*v&}Xm<4dz~-Q$wcEl!Z5BO}6KJ$m}?$+gm#+HkVafsQyTRXE3I7M^%GokGf=S z;hMbF`q$jo3mFoKjz~8n^}DHrI4VU> zyJIA2-Y3ODTT;-Qns>G^Xv$T(J~NGI5#E1^6fmpFd}Wu#q}UJdxCkHe+&{~P#TqeH zM4~>L5jBRqwER!CY~nJuz(c=#y#FE(q7L#8VZ|u94Ew;fXKX?QB)5>7|D>kTZ})Vb z1L~&`=>9H^eSMpl86Q>F8?SATs<9n-wSPOtQQgu0FFNV|t`S9G3$mio98T_snYscm z8y1)kQ=7?+R_nKBfO#Qu4MS6l>E04!&I7etVMcuLY+aVypZUqqSa-{(7(@_vy{EQE z`M*f1|4DN#ao!#bUnc$jlQgFK-&%kQq~L@U?)&@Z>aj=p4 zY#{xonD6V0=@|B%Z7C>@p8s;V!x^Z*=+kxKe@n0=Ab9tx6iFxIvcy~Vnk+U%;p}(L z8>c9AC9(K;Sj|5@TMYwnG4hYDTC9#4Jq z0^1?~J*j-1CwI;SR;QhOhTGM55W_a?{X2jMAOL_cFr8IZn0Qy%!ynr6q?oLM4_(~o z(LUQqkbcmc3tVdnL+1{Y1pG&PWXv)q%7`Aeeh4$ow#`j`+GJW`AYpG`9`zs~wv6`V z=hCF@#P11)2BM`)_2d3P#0N7fXF$G-;NPW<6@eqABL!O0aUnN;OIZI_H~tG=e!9o7 zm@UuC%hqwUH6XfS%2wVfdZzy0l3%zF)u>`>j0p;N>*#E?}jwKScr|1WV| zf>@GkCsm`*KC}HV391^_+DuL?|J>wrBsGTH@Gd-swT>YrwB8_YO?QgCYO9Cz^ zG{J~%S$sZ9cSEkghdq0f5bK+F!h-p$S4Yv#lnehRZ?)1@z<3>EYeMjiFNS{g!k}Y! zDRSKx_eK;fz*o2Z|B3>}(21#;0tQ>VCqX+)TzK<~QlOryYRJ?$byP@|pQ}+jvo;&; ze0y(3`~8_BkzmR!udb<60na-0^{nv+0T2Md6ubko1j9dNwX)7P%`g_UTU`Ug+Z8XE zL&4JXsE)uO;s?Xr)c;=F@9V>ufF=*8sK&dEm6iD6D2k(ll=qjKx(-10|0%DsM^Q-s zc=@nA>vLkI00`-=WH3^NqPBjB`@viw`&fo8nFk8mp3%LJy1N||WxnWF&FgI`is(5Ty2y7--Kk0k@TaB`0L((mt8ko=s=l? z@DNr$dOH6niZY(q8AUzLFJKDmxAVv26sj%Jpli$rAFt|tPlFC{EN}<+y^l$-(6Hwv zEq-}LHVXrX#JAD*8 zv7c3SdZK@b?fdB!4x@JQ`a%zGcm)0Em=5~Sobq;)>@+Wt{2%%xPOS#i|ExfQ&!Ibr zY!I95`i@2PR8%{t(=DoHYJT~8!K8bPw|<_x|5N!B4?eiO z?_E5Un2HsHOFseGQ#}NYtogUrH2sr}qwnXEe_iKI@Bi;i(hEWjpx*_1F*aWiNH-6` z;w6zmqe0?NM|m3G169`;QuMlx%+@Bf|K5#yCkXR~-(u2U_=ku8qn%;&|zZyM+OV8?z=eh#$9hT`i}}c5FZ}l~%Y>zH5|5t8$;GW)JRW2`(te zWU{^cJcfpXDc>c2yvM3EvzM$V7j&x^7poXLjB75l3m1eFW0#{ z;o{$CJO%=rnjjOX*^3{%7~Oz$|W_rowXMPB+^Br0$?P1xIORgi@66s8MD& zy@~)h=7O?XJ-yFzcD87)o%!MzAc306Xl@z`5oJvzGx_rjC_CF(miPKY!wGyai#t0& z%yK+kjr8m{2_h!U4S<*%bwp#hZtbl?}P>~ zSQ+W0dm#(w?B)uiO-@3&A@%MY>b2Rbz0yl_H2E2zSgnp`egEFNT3Py@|Hz=xx9`QFL1g~gm3Ew?#f zy265GOj-57skR^?i#mwHS)Zr4b!eQxAtM${<_yj0Wq;`U;}oB}-786I`s4d=a!4#E zhOJu~;f-*urEluE`MsATVNra+2e~PWBj5kVJM^sTmfPAo*E_3*!7Q=WL{?VTS44Ds zTK`|FoBUz(gYW-z3;`@I0^{&<@*SQx`{fW;1=vd2^=Tfj zStS`MMQ!QkUZkeWkuTuaMaI(M#`k8VV_KMtGkap+ei$wL36xamvC2$W6WR_h>IR+D zZ*?fOkQ1_mCYs+}M#k&4g|FnwWqIk)%Pa-XeuDC_sV=5&DRA|itZo`EHA3*QIcq0i zn(kC07Hi!{*5IwafNspy>s2j#3I1ts(!xihXLmZkaAK`!M$~v8&1`XIAIArzKncsq zvon|5f^K#D{2#9W^e|at64;Vqn*7pV$IRYKW)A z)Udiao!3^^#|6%nNIn*+^lnId6HwN}mWhI1DCE;VyPN)ANx4H7h&S>T8`F z9DF+~!cGXte^;#~)7fFOfS5=Z8`VK1FGcqQ@k1RosM&Sw$nZ&KSuXXZxfMr>g?RZv z8_WH7N3*T!ppR=s#?9_f5zQg5Jxdz1>yHYBby5wsf{2!L0>(Pe7-Di$W#mTkV9gEe z;o6@+3L_T3amTJ0WU->OKk;0OFd|j1M6Bc*A2!t- z1VG=0t3>+@G%TI+xb5UFQr77C^+2HiXP1h<;jKjU{xyi`(;5^xNwRpfkh2(Q`;XBnXWjjz3Mv58clLXyRRnV$z%+SeH86zpt0T&SqBG;T za8O44apAvgPDZx)8aZ8#r{V;$Z&COgUBiF2Ov1Xoh`*-cbhNlB`Ss|mYqJlwyk2KP z>rXfG#Kvh1$p6v2ywT{2p#LMND$a^4u8sm4SIb2I#NHqHGYaA~J$4{DiXkpP`LyecauRAo)TiemVDy$ zCUsCh!{0<~{ke4?EH$jYY0*l0pmlIqCgz9x6UG#v{M24A!=JoWMPN*2O=`{^TxE15 zDD!-8ZV>K)QMY(eJbh}SH2<#XJX=1W7V_slmGM9`TN`_{QZi_ z=5OzW<88dee<>73^7#}CYpHJD73Np9FuKVL5AFgcR0O7F&xO2ZFGyqbGo~HK={x#xsZhm3{ ztGT{%nqSB*H8D_kDoccAHP1GRIFPPO@h)Nf=K45+w&!*2^QSmnx^8&1u^P}xPn7DC z%Z=wN#*#Z}dn2T2`}izrw%Zip*MP(T-GKN7Mh36}Kmlkgs=d65lP(w6U06mtx)-g@ zw$*(vAugj)K>o{|WGo5_Fd@)qDQqq=wpSS5$_`5D++E_<7TXBU7|dO>J>r|zXOrY7 z%+#$A@1Y5nBwt+S#uEx(X-+aC&mSs@xh=Xh{O3qsYg>Se}hd5!Z zw1eUcNn;&^sPXirFz+z$+*ZzZ*Y^zTZ>QP7gIwpdfJU7TS2u)1y!$kxtI>HJ<9v%Fg4ZlnC~x^7f5Q%Sgv*AZ_mWN;A<4R;-j`t4D4Q%&{xc-4=Y(IbeF&!H-f4P@f+dhSTAkIW{>L~HZc zaZ8JRK7w^oVuPx-kY#?JY`VF$%kw_=mozCb`K*s)`G0d}i(2$x$*_s-0ZG%>I*uR! zd6wrJE!Mdx&{S=i_M<*Lj+{q2<+B|tnc=WW$QB*4jW<;glA}k~fB4I`jx*I0w*R4E zGIb2Cv1$2KPx$O(iNkYOeFs+o3arWr?~16qt~{h_Xn#{SZfoybR01QN3@DQ*u3gVp zi$d@BSaWC`h!ojX9S9Vn#s@%=g@dL4K;UQ~_NSWrUTN0oFX$CWO}o+7$5ZVOMJV}t zJv-m6d=!?t1-^iG0)!GB4{MM=+uX0t+TOB#dP`?K$t7ySZU~snzAmKpb>DeCY@63#XXR&oLb%r@#+2d??yXS0mW|`r8Y0-dZ=C~N1mV$iZd#$?Tw_SK8 zW8pY-w?zjBHJXbtlm*?UP-Kn?N6&{A z!Cd-QoCQ7*gmLllOV|}`#qM&2XnWd>t^Bmzj)J_OT<-5PlvnltFRrbPhn*5`z<+9B z#PgZ1`p+;i+UE&NDYa6RJI22pt^O9{0GSCJAn3NnxK2fd57zNK`_Ab7yQ4=Uv{q{ z8_0bg-cwgB4!47Ry+|*lT#^evo5|V^fvQ1e`5@5l-42V?GZYXmOUtyw^5RL$$3vx~ z#R@`@hYPq|k~>YwxQAoUzft!mmz!w&rx>qbCYGnIeUhEPdp#RMseev3OTK2zA=uQ^(eek$?|?;VlSEy4eP?u#LzK z$DUU31A+B7dal_;7LHdrf-2|zww;J3bRVN7j^YNsi_LzLNbZy@jH)^GSfif=OCjdL$A3?}EjL&HxqtkeZEE4d$b#nlq+X(}+J>i^IVdCSKXDPA!}-@rEj}B)O4R0-_1koK3$x5M zv(`gYLhV2Hy*ZwF%MMP7VusdN?K$JsEj5|9Nd2r$Hdh_I6!b1uqNb=7D_|D~%q19@ ztuSxLx#~W+XQ;iZAU~ybo2ke8w@Sm>vLCdY!0}Enlws%&o`3m=jSE}s!h2f`8}El~ z0~`RR22$*E!F#?ARJ`>{(pZn?m(`jcgi4)()15IFPEfQuAkLW$pc>Ng!NC$<~6 zQhDu^f-xhpb@P8%01?4?aX5gq;u7Mj9sf~~LKukM_)Fdej$*cNvDPO=5qgwZ*{F6+ zWF?08)#m_*03P)kJlq{388AD>S}b@iSYf(PFqb4XAHLnr9jTfuolF;IZXh&X>m?H% zvOZ!zShRQ7GW>ITULpU=o~iw_n|>C3y9^N!Fpd_Css=9$uNt1ac6z=4UfiTXIX4k9 zf%nq`J1*}2*JN?jD5t?-ho58Rdw;ik_u=w3m&^~qTp(r1_WFba$JZEA;_}bfH?qZ* z$=TE>B^@|i>-ihrk+BC**{UiT1z!^3$^3$@7D+om+03g=91CuF!-Z{C2r_$ggR3oX zhT`wi)l3Fz%%!w>rOUQmw}`0y|@iCFe}FjmGB8JH+Mf zw03v1&`#ONE&f@{ogU)(n4&wU_z1FqSsldMMIw~&AHkcRqlgKUd?Bgv%s?&j_EHVx zr7Q-WOf_^1lM>@q?qh}VF`03K>_cH&IM;`Olu8s8Q&02TBXNTi9x8Qt<}+FY^Bc!k zX_Z3B>}EHuH`Mz3zAyF&(@F#NH1zF>hfJ_2fnr-c(e=A_`E%_j+Mu%RV(Ybhm3L3F zrF+`NBEla+(z;elLTh~3V@iXyQ$I`d%dbR%ZMKT}Vz0_r7sJ5$B3UVwhGc3?s5@zX zJ=67$GmWcRDf$>1O>xhl3HG4u$}(Dw-fT2IT%abg6g+?1;eU?;f(79*87a*r7&`uS zb2_1Qa$vULl*++gF{~LzX25Hb*4{0gD=s5J?a1v5 z7z;$8q4Lar<9fAVn=JuP6UU+ojE+$nl8lI@c4qN5fg`R@9F8S6P79w9sV_x}pL3vI zw4LGGI?a5JIr6qAC+JSOxE?0tHs5Sq);GWOeOmL*arP3~hfvF0SA<82URcTE_>4|a zHA`nqqmu{SNnEHMJUM6MO=6@T6U@vN&~d*=dH*s!Hd0RQqK0QrB0(Y2eJdi`Tx=rn#E{BwTQdt6ff zy-K3=6h)?_}+I6UuM~bp}L9?rIZg! zrBxfE^0}YJ)~?=7#2dk&J#TnvWmdLSw_uiyzvvGh#ZL}5JmM!o=fBw4H){qyUgx#itO!YL$lh?P88E^?wwX<=IDRa zHy5WT&XAC@vPXpNt_>5a2#`@w3>jpXFWN7E)@QNq1H&#^^)G1LEf4>;W2nGgj$Kq-kToaEfmx!XF0y@5M@yJNOyofhr>c*{wfI*&m$-%B4vJ`44WUHI=- zW&H(6?2<-Wb6z;5iKxl)-QE1EP(t2F11vzo*|o{dS5McnmpoX}sUH19szx=LI*RCG zd;dJ8ZH)aFUCz$5*rQFG`8M3-At>eXX=9-kz!2vwLmyX89PZ5Yhdg!~OgM(V+Sc+1 z&WAS|j4n#v?lN*P=x6FLYh_VvjgsSOAFQOLBWuo~=kEDsr^ZXfKq}v+vY4~)=~NY~ zKesCf8Xzk30kY}@i_^1zC=h%vw>o)v>G6lD^vu)$E*k6KMhT!%(B#pS6h|hZ?Tga# zM+DFyE5Y-NO&`D88g$)O z64uSviIy&qprc46GE@{6)|3MSoI%qUYWXQLKf^;Mx_^Oa4M}>>Z4s1{0Nyr3s0IlB zO~)>%Eh*U-jQ)yG9zqzcVVL#^1C!cX@~{%5d6E{Al$Wn%(7oDw0J@rq08*mDs^WFi zH*eQ5<7+x}24lxaJZjPsbw#Y?l~Ll|F@pT;Np!SDL^3~pSHE#^s6qjj=!N9~OvrL* zs?Y!pO?xxA0)$AbF zDwf^A*QBy8R-JyXfF8TIo^3GceaPD*3Qt=q?1abbJvHpJ13o(J=}<#HF_^Bt{RG8p zb!y1kdSmW2851hK@0a^F_@>zD+Sd72bt>}(6Jmk8^O~zeEm;GNELab1Jxg9i!UAG+ z(vJ_Jyy&gnTdTpS-7i&7e%n_!67gF7n~uju2*n^)_MEyqmtM)0>wk zdcV>3UBpDzTfaaUHpNz;<(7YX-K_B!4AbkQa^3XN*;)Ys+cCFxjt&EE^A{4(EP1`J}=70+>|;w^G+G5B}k$%GgBLE zua3z1L;sllLX|i@J?$>J&16-&7-x(yOS6#0pG{SFY|YG{QzppRxwR@IQ6(2cj~5zS ztkP-TZq*Olj%c$uMa=RMJf zlH!JdfBU0f;u~(SQKuJJ+G^$xbSi9DdeN5CEDv)AmsjS#yf2??@IwAalMLGii&xcP zKbkx3?_@@yShU~$n*Gj*1{S7jB4!Av-_htKJiMCYQRg0_qKYV_C?Q~}pgv$8a46h$ zq~a|Qkc2{0XAd8rMng6e1M*JOWKQ<>q+1}Y@G_3ph!W4oGpP}JX* zQ<XcGSz(6HjLsdx@ zw-f$fK^7l^h$^_>M*qsk#v^+gw1(7%k#-`Sfd?P}Kmr?9DI|dVA19-(py$oVTG>3r zdX|ha7(mVTpl@C&2KdpVGT(_q@x3C9(Jn1xfQOvxol`%2M_S#FP1%C5y^IRcAW}0o z^Asjh|M964-9*a4zJ)FL=~Nnu;>qAgmA#Jn~;& ze?2`2rhJ}DV=nvGZjKwqu`Nhu#W&tBj%T&Mq&;bHfDee6;%RsTepxF=+(b%m1P zC%4#p;x*3^QaHerY8d05F{9Y>>6NI4-neSF)BRxLe@ju8rJZ`@; z{KND1ak|0v&gbD4cb~)?0uS7qW5hH-HaA*h*re7LR>NS#+^5Oa*k~iR%>8y+S4mx9 ztFJfOEsytoT>0URiLHv>83hj6bz^{}*wwyp&A5)Hj>b~Sgme%%=3gFoj7Y&+pW|N1 zIFM?cshaE6t#>Zky}y?_?rhM@4L1+iC9@4MlIm<%z+F*v=Pel~nd)MrD_bMGXjhjk z{n2fT&WP2U30P;!`6!q%{NP;8HMiH77<5F_x00-}CS!qs$WY9gk?|Nx0a57x#Grin zTjIn^OOBM6xla3kU~hq9vEv)<>EbZuKU_o!ps|C+D=nDd*)q;+pA`{({WX{;QILh8@Gsv;%x zY_HdFe*ysnbX1^15K7xAF(@{aOxl(HJ+h#c3#gIkpX`yq3#bKzOKO`n=Qz}d8AGI0ni#&pXg52XfB8>rLRPpwNlbP9M0Xz zmvV-f(4kvQLBPhmF8P<9GX$*Z03I75kEEbNVQVb^(f|Nm4n+GJFVRlX@#Z_F(u1XK z{tzXxQxgD2#`**9R&z6!@L`YC^h2{ZkVb2mpIgBNq(Y|B#cQ zz+n>(z-PMm`{KWU&9EgVj)N;$n5g?wKqd;90~%gsbN<1?vKvh1vj9$U%D(YR0gQ!Fxusf1b1A#L@O@lMbMHv^bC7D%CP2XH7> zALvtx_wK4prmKeC)4?CI+lI>Fxu7RlrE`pA-Bw+>W#3QrH(VnjLL4D@|IQTy0`cQk zURRwK6D1`!%LO#OfDH+y)9r^bKu=HaB>Fge?MdG=>7YOXzV&HSb&#YY_8U=I+o|kh zMm;2n#6>NZ57C9gNYQaw;q_|c(CFV+#V*;Z_srG#I^$Yop1upw4+g%Nh@bL84Uw-i zc{j@)EClQk2ADrbnn&nIprJ%5?M5ys^#DkCK0T9{ns#^Lk8fF2ox z?QV3=UZ;;l5Hx_k*4lhew2pi?6=fLbaPhRM_m}Z;gJlq_knPrRY}Q~PBlEAKt?u}% zx;ES!BYVEF-P^1xc`kv?qg zk0~&w>s_^*TfiE8(R*`64Kft8eP{9Y^4#6G(&+GlpXbSdv;Q>1yeopr45Y#1ce_mu zw!XSmeC9>%JORP9OnQxiqZ8v~nqHSvct(BNW?weoBLBgCI?&*#@ijdp;y6?zM|E4! ze`2ZMcVE9CH1dD`zQVfW1f9E*i}%4S-c@#DH)F~5Ut&q4oQ?bp`w z1#Cqf3|HTG^6__{&;y?2$^niUK(_^sp;0e zTNaz+$Dh%0u&tzhK?^V__G0)$z9UzSNMZ~O5jwgM&365XJ%B!*^MG%BXaApYhVz?WA0FbUby4oCU!kM zwmk)dB{44X*o0YEB{lj!QLcgchtBvJOME1Mq)B1YPA=QYx~s9`tJT}_mrSm|*Xq7m~7`~L2QBiakFg){HvQVvwF!!$S{#<^B>8Rs*ri~HW`kFTs z-okpJKmZPA`~&Ybp6ft&P{M{v6|xbh$d3KMvDk@tqVR{x`}Ey@2r7q`(+=G3ilFuS zU|5@j)L~gC3;GDX?3k;oNzA_6s^Uy?Z-L2T|?bf;% zlfR9R$KesSWS@f#Ug0ImQpVGEX+7QWolsOr)qQyd4%Tm~nnZJ`^vjv@ruzl~o zB|z}|9jD`3RvCUa!Vv%5e(fQCr?vv@(aIEUW5T0jXB23Y9}il#*XQO)L204oI_9_{2SdP9^9%dk+kTO61Iyt z(wSZ1Xv-3z*k=3?Pq&Dgu>gRv0B#VPq2JlZIT=9R zURbGFr?o)?ljawioCx_+R^3|Q0M}Lz`{p2XUCSz0G%7LmBB?Aap{@eEZd23SFOSWU zS|WX+A=!M#4JwDG{NrWGrbj^$bZRmtxBFEkdWoxA_~80c~mnhL?!u(OV7exV{_nu6EY=*vS4;>nNj*aTa-epP+xt`g@HivN5S~~Vm zE5kBe_nCVWA8N!Bx5n+-!9k%8V_#%{WI&JMP!V;;Wbys-UJY0ceN7Qf>Da8K5Xv=& z-=!CKF&V_+q{GT3Mkw8=KIl-|LapX#g8=xL7XR8Kg4ei_Uf=}5A26MsmU*5*e zXG;XgQXGzPj!Ar>NIlMDJPkVDIR3L4YRV(zJdh#!D84gSGMbovRCbruHr>OQz;=4+ zwA;ygrO`OQV+@WD-zB+2+wncoJ0)g0xsFT;NWPd3Ds-ib zyy;iE#-|Cv&B&LShstnU%Md)ql17BQqs`tTP71snw7sp3smA^cPR{|tSeMq;*OH<^ zexDPAXA*dHbUomHFNM)wB>Sg}TiDqI&WvKBl?^Hy@}(gOpa;bbwSocV-|f5xD#+CP zP&f3``NN2=Sg6Py%8&C-cc+2&ttG4lG$mPtld=RF(q-Q-O^X%#5NRW1!}ijwDA)ih z=;=|`rHoUmG5LwVUy3?f@#8feqi`Ju7zM3N%^6(idUFQQoCG~kqN3@4XCy%Bzz`sF zQSOHOo5^NcA0^Cn0YWIt9Jt!cJOX;WDXZSaza>}4X~L`LfaZqsDA1yYTAUIqUVAM> zHO6hWmtr~%AqX5u%RuvSnC`?`GE%n*z3;XFvz=PAeY#16^_kL~Z7lh?0H-LkEA$zS zpD_DJH*F8iIqxw+Zm0A3R~a$qIR>`BIER0IIlY@3ZCDkZN`z3yGUe-IX=11N>OUC# z(|b0vpbZIkr3(Dn_{XO^dgZB(vsCJCHz?bnlpYpG>k)pO3drz6y~5P` z)6RM7E6;-Z1d|ch6_tA{dc?f)<=DL1v>PJl>v4#+K6KBl zdCnM?brV+F*{x&_4HTzB=5AK6xr^rgM05a7X;?DYTGvR-aP^C+1(1&EzY$1dJKgM| zO`eThT2FO5q117=2t%u*F28W93T%%V;0@8z#ySghFQ#Vhak^gzb)e2Cb3tG_aMaW% z`nwV`N$o>-%0mvK$6(QCdtFD*wXDKST8Cf?tnX*od7i`2jAiUt~(Z>pX55#U-Al_nc>i0Nwb|@I6&_9pF2Eq<0Bzq3P*c1{=#Ekhp_33mzEkk0n)=-5Ww8Pl>4o3d%i0j8jR`Ga*;6c}Z%WIlir|5sqv=Rh=?I7qd>*kbKK}T}nxnU7S6lQH7fa-dN96gg{|?F(CXj`lLpPVmCeHZX@b!D9bN!C;?5vgYLb> z_w%4)q1sGc+{%Rp8*rjs%wvR=RzXK!v8)peVIpP9YzF~*ElE!mF*J11NQ+UVrLC#i z&T~5JFYq41OEd)P1>SLq}|DF0TR*7ENL*HA(AZ58J}uGbi+UnBQVjq>#%Ps5D^#;v9P zVJ~tuoWwGPZ#_|fml*iq=DR)nVzz_RXZYEg$tJgbV$dV^h(1WHOWKB+7P54^{KG94 zA7p~J_WnG#|NY1vaUGV6-HM&Fc8T-@SjQcqrcHh)BxB?}aHDX5=|bH6xB-I-lrD&0 zSy{KU8^+BDol{H^c@7d}F~*umEo}ac>AqU70#X<(I3U+z3icNh@O+s_*I7l>+kH-MM|?*oH!aV73}X37>zt@UAJCwoY3u!tkZHWw zGP&mB;%-OTUSa>@npqozf;4aMXhQOzphR(Jp=6pB(0EA`dv&;i(E5W%ilb~OVOSQe zcvsvbsmjRN1P$%EnWUng#NQ~;*2>)uajF=7W zQ51G66Qk-fLt1U^v01d5nybnr&BWzR5_~QZS7%J3$hz<5LWX*2{qXTVni>o<1C>rZ z{C*Y(UWVBjk<|g`%l|G!yoSXk&>MsCP5*dW_)y~5->^F-yGm=k(+8Qd*BmzE1Ggd; z4;iu`j+3%g06^DZrv2>w!Ha7+dutWdT{&@0t^cYD}((~swy1*VfQ7C#r%<}Sj^H>C#1No#+|?F0^j zgzGkobth5dGk@NK$q?{Gw<^yuokw9xIEnQ#k-%ZY>upFK??~X`bMDpIY;+uXX?5td(}g8-146Ybw;D?-+Z< zGTAIchDeak^{BzOlb!z5Ft z^_FqtI`RCZX_)(p-=$?fr%7npQLqRt%qU2F`uCicu?J*qR7L;tk{bDXXe$J^82aG~ z$woZB1_v^f$R>BslAY1WBdkt4Uz0cqv76C=$@5anHCH^4L3N-^>O!{Q0E9-307taM zB`|D2X@DWXX@(qNl`K4aEZCBM!I-jq{mEzc-L>jVCgqx7_bK}iGe)J{L0?!_vN+k5 zvzMAp7+cIa=jLQ&EJ+(^Ix*H#SNelrg;f_UAyKtjMHdD-#Pt3Yh5Q@i8Z{S!g{k@63Dbih)DUO5@LHA$pT`gwynAqKHutdHsokhjb2qhD!aaDI_T7>?|jz z#U=?)-N39zrm$JxsNXL!kLjR|L`8U6?H|aK3wBfG=x!0gX+?tXpuuYosVMz^V`zqr zM6PpJ!Id-Xf9~#VB%QERc9Q+u2w&oDOe!UbWyb1_KM?i&brd7_EBgj+TAaxBUfqTb z&=zOM@hzS|u6z!mmS?#1=&epz#NM9NyUh*C9eXNRMG);f$*eBg7 zM1~RXd&SttfV#^kw3=O$^j%()1IQ69NKB7CzFE5ymQVWJ@WsGpcAUg6Xv2v%fd7R` z?VUdp3JrJ>8A_A;JyA}>$Ox(e(3XiK?m<6JOu<|R&o1m0;s=a`cY5x6y&Ovu;N2^0 z3^_yx6MI8|1jP5tg@B#Uj)Bnr+OML2dShV&P<`XLZrNY{rh89jIr4tng3GE(t971f z@h;-W;In&a4_GtZ2x+V+Nw<;$RXyw$=ysiYa{sx7Lu$CoAc%*vSHpMenY^~6U+1A-_?&5bEU=WzIYO+BU@ZFXed3^C1~K^xOmK-08RIx# zK(hq2l@GC$rC=ULT1r<`H2sqRMa3;;CE@?vE&qYXWC?r8+&Q}94MJ==${;${NS|1y zZ{_5A8AHSI%QHpsTY+-Q89FNeSGB;DR3K~VVq#6hVf+{NT{h+>3hs9DS0wg`U3ea+ z-p$&6Hm5ny^2b7g0k_iE!u(@#{~;-d$a1CEd8^wUd?3xX{ZrHFnhUaP47t>LGI#c% z6p7w&_4=Fw9!{`n?Nb)(&!_j}3~->ltNm4f@Y!Ul0!Cf8mR@&hvw>8JIpDrC7xAoD zn>!A{SyIm>x9RqX0js5a8Pf>%r5$F3FyFvYlj##AO+1RAeX*!_aBLKtgq=`joA;Gs zYn*pFxy50)KDCGm60l|Gee`;{&haRT3@O?U=b~9}SxCZNamcDC zr0mM)W~LkY>r}w>HaC`kSU+lwTQw}C_(^0jv%{P5(qJ3?iC4xEx%CP|#r-;`P|lc= zebc&77Q?t$aX zB|M6m#1W^?Hxyb-#<;tTS%>h3%Fr=B25fUG;#@_?fwm&*b#-n7&^oW{+|K`D0VJ1a zaXFq6;&-d`Z@lJz4g-|H>B*UKqGi=+uae56~5s7 z=*8v$=b`X~T*s&L06^~9WD9!hO74}a=~c!Ixw)@T zTv?+-k<==$II^1cMu?cPYDCab`%5G*dy3~|)`yVf++zUCq@Y)hq1ejpBS=o!8pYC@ zqYh^mmE}aNNo6(JH5+}nzor#W#1~nu!;8!5h$OTO?CXTDh#3JRo92W@b=Lk8NvJpT zmDeGo@6xs*j{NqnnWXo%G4~UWmoOjU$^HnG=z4ApDd|@N($6k2J7M{kf?bns-B#xH zN&htA_Za#;Q~T;48rpL8io_}18;vLN|F%KyE3N5cUZRwUh-=)6L+BEVlNg|(wb&c4 zUSkCNbpQn+1$+U{L?JR2r6)_nTH3keCn(LPPX+XFZIY#FQ8}hYRg(k_yR#XIB!JGd z%$J$eW_AEtn5pK3i`!{6aZMor z3{?N^LuU85onM^Vl7*f2_x4$iG?GL)qXEwzFaC&(V+t3T=hPZkDdpNmPIUZ4XCv3^ z>T=G^YA)RNR{Pfg^#y_j6N`uh<$(6^Ad8O9e)dyBd0UhJ{CD%Yo;{HawmhGo{~}R1 z!RG>13`<4@xx3?*uctksy3I`WCZTAtw;j&XTQMC>;~{47O&@Qzyy`Kw+h?>~2p1gQ z9+Ud))Sd;(cYVIc?tYI1uG~;Oi2@%To6b|`p^>T*4|c%dQT}8wRAT9W%Y`QZvQ}h) z7zvjhNE7l3|GzqoTx1?UZZ-aY2u%wnp;ayW)X124Uw10d#oQ|a5I6J!d!6Bg7xgYn(l`zcKYCHf)fc6+!Ck;In7yt~CQ-f}~Cts!$ec{S3Bc+UN z0}-ypf7m86rvO7Qioym4z!>>ho7w;=oo1?S22U?6ZPDsV^@M$?DbwtVFB&*D11Yt5 zaz^^UC6I61IdA~5z}?vRdw5j5?2D(Rq$H!HR6q4p`sg+O+aiumC- zS`Hr~oj_~J{JS;8t>4<;uA1B12F#x0WcoSfT(>+H4#d!!cUQ~GF<8`T`=1H$-)k~< zA0}B1#L(&i0|3CyU8NEEvL~eODPW`TS)qe7%Y*Wxir&LjpdJU1p6t$DW*(fR6Z(G$ zJTuR5NRt-Ehzy{;!~elOgow6Dnu#cZ*v0j6jYqxU|5j;a)@e@;v2AZSyCuQ?msR}t zi`pxc6L4Y|0LYk(K!>@VE>+=vu-h|=M(-^LBi+$E!8(D0&W7XOZD+VR3PMJ!!vAy( z_#gK_(bqGSLL2bAsn=X2LlKje9b0zia6jp%lP(?*;Rk&m-`i7oA)v! z%Uyi^J;njWfB{JFIIO3$i_r+h_Y){{W{E-Qqt_#JUdp)SYW zj)6Ed+C<1R%1sE|HKcH_3;^%}PWJR$n*#t)f--<78ie)mzq0n`;(l(HZ(<{~vNL19 z7ji*H1k`wV`)oluJJ;u-a)y|2b`d6P+&COkHHS$9EYyk#h|R6BmzU+Stjst@oQmyz zu)l&h0+5VXa(pDt&)E&#IU<)s$Zai-Ommv8XXfUoa(Y4Ru6(DZFpfm%>F}2}Kl;u} z?bm6}&sU}PeFXmL)TAJlsEIS+@d$R*MWeK8{ho*9(qrra3CMCaPY6mYODnoupeNSa zUibbymba+N#9#+6{kzGvuLz@)i;?wYruunWH%wN)^|tB#z~a2&z~32y<@3IY1=eF($f@(%)7a4%Im^%<6!_ScSW z_ER9bM1YL^LiWbiPn0*qA9CSx{0+2Dp6vt);H7SUGttVs)Ae=_ePX zTGHc6whD`Yi#(KWAumzvVk2SKgPD(p&O-a?H$HL^K5k^gC3$ZiQBK0TOZv@^H|LpR z|7~?t0HC8I7Je&798y#Ov9l@At8f3#z03Hl+pX_;0S#a^-_O#HIG=yJm_Oe)38#Io zao{3wWo&gf9&p?Go{}Sf48_0F#o+dK>Um!sHpPv3Pkgu^$y#qAe18Pav@&^D+7jfv zMDh1l@X(A*>Wy0M+uqfNU7*Z%c=35apOkHLkXXvn(y~kn*{{jqdE)+W#Y-0z8tWs> z2D4&i!Mz^7-Ol!A&zSPw{J7&@JI@COh92LG<$;Yc^hG!AtzZjz<7|O4+oid*?t`z= z)4K~My|(866{D(2=p}K<)r!$6WE9;>Yc)31OtwHi8$&2 zKmc1Z9Sxc(5fI7#8gqk2^_DK&o+uEhVbx9jZ3KEnO#gNLbYM|Yfm{X&rGOFz0rjG6 z#MdoF!4h?d2}ea{ga^LwjVPcKEMf_5IvSPDG9dgMtE|pR+A4Kci4T@n1UjnH>aZ}& z>0N}p7MpXL{Bg5m{%2FQr9{VQ7-P{amov^T1EJSm>B=D+Iy}bG83Bdno6uRC(qY^Jnlie;_%+Do!$0Bq^GXg zwhY^p1tj^|of{V%8mmzO02dwZvWx!;9z>brUF@y1E+Flt$4ECTDd@F!SXQUzjK-c= zA(JQ5S(c0j_GApwqXoN%p*J?XGbrGj8|v^loW5?|@pI5Ioxr(QX9*W8A4FfRXFc0f z*5+{1>q7FOpe0e>rg)#}P8p-2`B$pT%Q=Uq6Xp184J)gEV2{LaUUCp*Dx)}D$f<4W z)$DD~zS0_1{mpdaOeXovE&q_@gl+e9Hk(T0_w1|eR1)I@Y&DmH)!WclRnkdJabrD3 zLng)9Ey<5#$~!kSH#Xe2j30caxZ3awtVdKx3)kzM^J@ppqP+_2jp0?$s^`OsH_(-a`Y z+e{{}!%q|T=*INraCdrLzp}V6f38D@b#)-!caud+EH*s-s@aBLzkGGQX z_Bwi5UvmA&FYd(bvTnQH{v4a{ehI|CJ3$FtPd3vXue-IV#$;Tw_;k9>VbRuSd#%=( z^63h;BSrR#cTDv3yMR+Iji+B11I()#JMwWh;O_d*oSOSZZNzv20fGMK+1KGl#8o-Z zUFu8_pl93=uMvKWiO!?H_SZCb4$RC}UpJf`UjTAg7#n_G%nwPxe~U;WSCESOcmE|K z#h=)j&da33iK^|blENiW`<@nQp~vIYsRAb^Rx%8B(^?I`5lmYLRP1TpEvTn5$(YGJ7x|@(v#(>lZhi;_q-8o$l zep4~f^jj~(D4oeg92VQ*$&+IZ%$={RcN8l?LD-zutqvs|zJDycofqNlUpW)lME%~5 z>;H$dcMguU-T#C;n%J7ynK%>Mwrxyon-gnd+qRvFZQHiF{hU4L{OWyo|JrY*sykg> zt-AW^`~L7Gp6|lJ&22c#UxkeJX!Yf6fDUPwIj?8;|Ilg9kP0Pc}Cst!^CI_`6pd%ppCT=l!7pT-|2tA(IWT&zoKyqwf zDR<5E&^FU&5)UK3Cj`_mO>x{;Z9SW?Uz&MDc)PCm^`rg#ULat3TGEDa0YIyvnBV35iux{<2W)B~5i4Syd@OMK;^v z)&4mj3|JWaXssSz>gLERY<%)CJ)9D{7r2$#_7DUH%4?oQg3+)*&H7*^IJSEy8KLN) zjLSOCJe_^fZka&bx`CjVR|-4g_WcKEgW3n7ZjW;ht)a`x^Z5iA;@`SI@LX|X**+L} z4|d7A!>4%F#^j61W`SF_?x(-pC7vJg!bsi;M1F7SN_2i+O~9UM!CY68HyPw!zeS5L z<#MLFZY6!uS?b8B#SZ+RKvDHKG~Tos+tqQE&!HIIwrfI2KnmqR<)zfNcc<&_I?)vT zV}7(R=yY1QRe;A`M5F{Z+&q|H#&SAlRW`Sq@x{D(;KgL&4mPaNBOEAzEF?WLWxpPb z?ANzTg!XL3W*eZ&vNh^()X4{{H4C;cl_B!YC{di>@{ujn39EU6jfMrt3K$e@wH>ag zJ4q5|Oad%9nw3s+YrAY?;w<1>V2R(Od@?@k$)CSKQKU$@?)3;$_o^=}5_3xZE z6}UmYE!!yQNxi5(NPg=gLl?E3uD}2JTS~>3WZv)Uh7AC0w;L@yfP!yl=Z?ZP{ODUM z?AdV+XxmK;D_&p!JN|gtpwg43f(`&!HeOk36}@N#43$1VM;>^QpC1HPrnxSjM^?Va zSeaorhGe>%{&clUZj%zlP+FA7AZa!+9V3D7GaUTN;ASpwCQYs4aM%=A)ZSNP1^A_$ zEOOP_7ejKZw=#8=?R8n}s#=Sxi(-cC=#-=Y0f^{t*0wu@c0{s&`#qy^Dul_yET2?% zy(}d~EO|4QANw4}GA@bX4`LICE>_L1t!fijhS76jz?cV0NC8L-3|Dl~v4so9=x{Rg zB7QWLRK1n@EEk@@x{Vr@Z}7&M8657lYVw~ozv|(IffQG`o=99}WPt^Q#Y~v3D9qnQ z9dBg3hLuwKi@Bfyl45+BQgq-4Sj2K>VyKBH;kwZl26eAokhJFv_ZQ&jw|YJlM*fDd z>~nqVp*{T8B`eoL6~3FJ5RCF&UU_0%WOKlnlrQ29x81j`vhiV<;|F7zqhhx`2GqzOnT- zZOKuuv2MEOIVPV?-7irzWwkmxqIgCx`2z`wf4p>M$H%N zHw>7osZto70a&H(54!AQYsO^ZJb4P`XL3SCsPB;Z@+DERJ%2~is>JlVx|~eHL=W!r z%jJ9wz$8Z9c1NRZEZN#BH(KmIp*{zwFi40=@c>T2r>wTr!tRFiVt{EoPOLk2+KX4` zuNT$b!o>5wH?7G65dL51RqmFD1kcJ*OC~{jYh7-gYjt~1a@qFS$APB4pA>C0=ib+yRDz0vYnnqYfB&?~cCbB-c0qHfPYP_lp5?-Wy1`eN z%sp2W1HAnt!Qyz$JOeWj#k>8+37`KG@a~|CTx4Bhn0Q>jul6#M=8RfMsvagefB=@s zUU0F{q5U-s1paX_BJu#IQy?9+1=RGH@d8>K0>M8H#<3?HKH-GexR(}*uEP*3GxrF( zu% zkYx}?wCK<3Bn^e)yDaq~$RBew#j#ki4fyBJ4OjaR^2amlQ!mQ9hgXLBYK9dW#j8Th zJl%m{T}@kM73wXOU{LkHQy@R3mpfjc*mKpbdVm&2(`YfeDwMzK zCLic6f)>4J8J(~(?!N6ujnaaeo;}N{^ANQOp?6GN)PPxgBrEr0s7 z89RR1I8u2j?gnUk+)LgVCM`$IVD`+wfcFFVWHY;RiPiY;J%rH%dVkr6zH&CvM34#x zS0^Mv!*u`DGo_YRZ#nx}2c;4U7WUeY`#T#H)HY*oC+U|7Ms3)m&sthgcBO>Kd#DWZFC_55(>0MEtvN5WRiq)@RVIKX`MM%Z5n!sC!m>p_&f1UR;xZHxN6S4n| z&q8~T416rnss-Yxf3pkKXX8m44w4@zLZ=}7wEL;x{yrD>#9(W=%-%~$^NKVScS%k~ zv`?5B+3~O>gbg>6M#F;!gA5BK14=Z$iDZ9mThz^hVc1Hi zquF^#_?n{t03rikP?YB`h7glR!8#Mucymg2uB|zdbq}p_z_USfbNXf+DVU7JnL2D~ zVLs_n;?UEEYc=hU&DAEE+dMvFp)pOb=yBynR;%^>p7P1BG`$Jbc@i>V$IhRkRIqnz z?qrR@+ujjf-^neEN`sjKxOu9XETB8?Co>ZxSeM9gj>n@H5 z{w|37tTP!L(x$B>6l!s2Cf?0O__{=wk&`tH4@*Z8S72l|P?Jkh*ZzI-%#1Kv+;+3x zJQGtWEaOo@|Bb6`@igPeus2ZUkgR&ylCBU0raREm?D#9Ee_Z6FAHH4{s|X{var6S|_(WiuP{2@9ZNyyl2|uGe{r zpsp^DcbL6Whhxnk)X>CAHL6B!mDRPqK_9F*d=#lQ8-@d%nrYYkBSP?HEDt&s*$!27 zkevk=R2;MqG}F2sU#zg*Si$74d^`NojB}+^m$v(L5WMg&#p?|eFh}RuR}=_2=Q*4_ zH9PuyYMkrcVe6_jzNeGXCoN5`<^tAN$J(~?ffB{js^@l^NamHDGp?Z(cd6G-HFT+n zZ34gCe}&(Q3CK+;10l_>TyaWjJDA)0%^*_&?Gr-Rq1w8S*Am5izO1ZL$F@{MtU?U6 z^74UY8^CjMO~auh2C0O%(L)8~W|eEFKg}=po97ReKvIu;rF`yDs~Pro_N%UrW<+XM zv!zQ!;yE5Rc+1Y>LU}FT@kq?g8>c+ZshTreE5?3y4=+q?WM!0Yno5d9G*oildR4grx^gvc=Wd=6k14{P~DC@JXBMz25TNjAmAWC4JU!+jNVCHyh{guB=4KtG#kqA0DxmcJ{IWfVE3>M8 zgUM$Fw>+LZF0!_K%VsZnq7(YrUl6Agn7ba1_4lLn)?@YdLO9Y)52TpqivI)!OtreL z-7S2;K)c`!yjrLUrXcMq#o~|c7Ia-6)t&RYsfJ#TqBu+vzE3dU%e#uT$1^4mPGz?x zShIS!^Cxw)ttNw)91&!HLvWkbmT5Jy1ct+(_d*^W<%^ZiVzW(MCde!Vl5 z7AH)DAIv(cC%u+Wk@(GSGTS|{^$n$Xsw(jz3Z<=|bgWubr94F!YinB4t9Hdt?JCsP zArAg|zpgGU$%A;l68}M<%!P0AfnSXuEvvnzE?gJMnfBw8*WZESQHw8V{NhgrWH4!~ z1bvy2-TN5G!}gkTszTAr%{48wJP`hQyP}A^x5$~NMD+P6&y#ou7!B}7BY5pdo+%ag z+`rq7;je$cxm4CCb<;2;>ByfNmKm}8S9Oa6wjrG#r$9MT5SfNH1MA>GzuPP@`7IEV z3zw^@r7IKeCY*DP`q`;G zEAP)K*OGypL%mw3A~R+d^ix78EQ0S#Dfk>Nx21<(sjaKdxa+`n(a zL@D}S%$7z`lB;|1kaeHOT9L*ENABF*WPhc)tDHy>gL_|Cc?7)Dc@Ljf%nce~##lln@lqA5}QVp3CU4a}29QkkOF9Z{ljgjx}U@5y!WERiJq{ z)KWz?>G(*SuCyssSo`HJgaPvvu4Q)(!q#lm+IoVHE`kOl^Dm<1?~tiUJ|Ovp^|gK` zGz=9I3_$(05adUzTdnz}?mzl`a0hbDICz#5KlGF>XY*nY|NltJ7(jHC1w}55UU!NO zeV&=Hd*6+#ArH_17=Umtce(nA^CpNIC!qQI1NBPg$8TUM{Pn4q2@8N(X3So33WmBF zVMUjzqhg+vW5uADhdV1dM~>~|#p*l8dpvdr$$2c!zhYXS3+?J{ZPx8*%U=#vkOK#J zacGSthB>;wl|_$Pe5f3uidKIpZQe|LcpM2%L`Ip+c2ChzdH^lmxn^?hn0r;p=a%N4 zWD#qnWx=X#NTE#v)HplOUF%J{n_`}Gaj~VV$L=gEKve*R zsNd-X?;!g7CLn(eJdW`N3G>cLf(P)U^{Ii0-+*przv1!8>Xu9yiiJo#J}M<;htl_g zT$dF@SZ3nkl1ry`A5qrdFmZI)?r51>d1G-fY%?ZwZs7yM)X-v|D8rx<0}?bJVPc zpnp2;Tls;Nfv`|c<8~eX=0Z^?gX=%ik>2JHTlogI}btMGd&(rv31`SgRANobwue?jG-5-K1%KtBZAF@xpWVxTStCrnR;F7?#G`WE$>~V!# z^M2byriD({$ZP_EpzX8b)WM)JEMEGLW#@`3gkqo0VvZhoyZd&XV`rpAs@&DW?VoZk zNSa|Bu&w@FPr8&E0b4Az=%Dum6VQP_TqtGaft%!nU!{1o3Xii-Tan1TYBuXI>+3uY zTO%=zR-)TgA!#`D>n-l%wa*E1!VCyK)KYD=&S6VRCLhYbf2Nv%N&44kR9LP}&dc)o z&_3shC5u5;`Rq6W8JrvZYzAw}>In}mCabSNplodTcp88yI#Xi8qCk5PHxGxDp6BfCjKzMoY|FC+4Dj$S#VS+aRQXfW$u~V1J0jZxgk6uWQpZ!wscx#WI5xK#k#jbH zXHj=#7eM{7jy?7B!2QQ1H-fj*!W^l2%uuTntDR$jYZhhg9W-iSSUJ8^2=1Oj9H3 zNXs9!KS6x#be#58W`m`XOQpCr@UmCs96q(goz5&Np(T@qvCC`~X}_zNY_=OZU@pIE zU9;>JsCB^jMpTyzpAdG510az5!KnJ3^sgh^dH) z#jiS(gRDp1{3q!sbg#U1S-uDemiNa;QsbH8IpQS@Hysb*Bt0w-n&J%E*&Zw;-Iy^^ zb4HtJ#%2=M(<$lomS6#~SY_7c^dj`s-aD!BDp7mJwVPw@I8tiAn_kUl;iuYWQadDE zQEpO0vjYf0!$l=1~A5Gue49=6G>#wE5Rlo9TiQ3P~pUD)I-z($y}SYcYFp47|E)cJ|;5- zc#X4QCbpaGBIWG4;`RkW2zUY~uR<($-h65@243nrZbOq_&)bhC*X}_V;ZOQbxz1s? zapnheLThqjYP0C=)JX=NtH#n>RhVdCY)?Cr5FvYar~5S8h*dbH%cHUDk!vA;;V~nr zyjFjX3ICeCnf1VrzG3%DkQK^hwHV{p6AsbUOwglYHBM8|*dK~KcW7J*;RdQ#IXN-k zP3~;)5EgZuiwID6FoSXJ`EYcIdMH+#(wnCk^_?oG%=MIjP37G(s@U8ybjlHYAit|v ztbUIM4GU(8oQ?3&wHG1em&nWO`*wNcd(6frk~C5RdX8B=5w=ah=8OwhH2fhU}_(Dsr)Ev4weljBRr3+vMr=GY;XLv#g6406_JJ!^i}Q$@L|P zBYY|aGg~3UthiXQ9K|f>8G%K9(>U+IToTH52290O99{&Zd>$)@&Thm}zx@ziXNxd- zpK0^dm>2%EM*N~}rZs60ddSN;H?nRIYADv^YVEH$>GKyI-QHI29J5IK%{F~MWBYV7 zr-X=GE4+-?_2wha_$!Bd6eJ`GOpsngCDhnQMy16`DGgD1yjm8r$OY4TtAiHBb%#}I@2T;z#vRhr!62G&l<(X=AYBptyiQS$?t&8vJ#~I{l z+6dyTScH=CKA4*t;#2}9SfTO_XMqN=qoYP@A9ri|*C(+*e+VqX1rf3-n;xKRlE&I= z9TY@yZmvo&0h6V~F;BN!VlgXxU(4|s29AwsX|v@?;rn(VmK)Y-@+Iks2l1PHhxQ zb8X6jg6KZ(jopAfy>gh>Ze{Y^5G5Psh+Qd8H&#>n<=hL=I1ZEA?`Xaw-*Ksg^-kTQ zhm_82GtrOvewz6LMRH~LIL&59G&)p#^h6L>HifIR^>fVM?tkakO01r=L?=q#xrE>T zxfuljHi|e9w>peQjbAp&i@?6cd^fV8S>i_9PYzqe(CdTjpgU+=OB6dPYJX(i+!Mss zf}9;WK@Df7>8$@0kk=|?JbJbu(cFwuIh*q0?FM~55E-@kgnSL(`Q=-ECt1tx;ax_~ zcX>aHw;7zAan>14j=3GQF)Nu(a1L`s;Bv(+vioS zsuh6Wf>ESZR*OHHuWw%z9LENTgNZ7frsP?+p-R_6<`^p0(iG?r$ZBQ2#do)Dze1r4 zC}dQ1fuQL|k4gyP43<^l ztlx%d!4B`rE_Y>>uhl+TSCExl1wt|wW#Ssg0@d1FTOmwN95Z5Sm%J?deJFsoBy%?V z-~DQFaRv{nMW7Ob5*gW}y_~$5Kz3gB_H_`GXXs&+6xb+pe>SvH<6&MG)cP{~@>t=^ zYcfE^*GUAg`J?y6qg031$J8xV?J&9x6ab|X%;L4vG;+G{m1D;F`bu{-DFtJv^(AaO zFRt?q#tRn`+L3mv3Il&4!Zl=#q0#-P{*N5G0T%xUIWN> z&j_Lv^VUw4{rH`kdiDi|14V;1Ufxued_gAen#LR12$t5pJ|Q5Syl7tR3fAp4)U8>- zLplNcd_0uKdF?`zncwVnZ%EuGT2k()Xe36XR01r)r0e6|Z@Ie&2q;%&I3obSotQ zSppG@a*@QM3u(^MMAU6G zw9Eta%GQHPi+n1hsl_iCLrx6QnkPri6fAIIywC=}O)^WV<{s6Z&<{nQltk`?u#HpWR{N48@vX;{04 z0LJ85_vte})ddZ46~P(5Y|T-iJ+#th5sD9kb+kHK6?pXni5kyX8@`jw=(-2>{1yOa ztR-|J!U4**Z=rqLa9sMkfw;JrIWVl*j;|2sBRk z?NTMJ+}YqSY*UkKtzn}oZr3-|C$cxW=Y^nV=X;6G-ujb4Y16^k)gBF1PT13%FRqX+%VALz6gt@LP01+lrGs;T`VLdgzv`AgL`SoF>aLa)bV=tHq?ujgS-Xan=FEz7X8Q#Um>4!b z8JEWua$o>L*8-U=MgF;rgRW)4V%hi3xycV-oO7J=XS^Rt=>&6hv!fTB*4;9g0@^+3 ze%}KCf<=m)U52d1iSK3W8Fe1vml^q{c=N-4j-yhi{Rai+7kRZ0a*(DkwxeHvkI4wl zxb}z~&TbEiF=*K9%wn4(#zgwWzfZVvQAP znZY;2EyYz$(e~NBXH#Rwp9ximRg~<5>8c>NvE_H9t54kR3bJlHv)H-}hLB&wRu67K zi?N86YOz!Pl(J-^Yy8xZyV_{=(!smIkI7zeMgS#q7uSM+SO9#VhvZK~q-X`I<;?sAoTnIN#r+eL_NUZ7%eU(UWH4JIVDZ-(~=do9G+_JRPMGZnt zkIQRn)C`{3zBB%Qlq`j39o!5qsMx zYf|oP$c})}oP@T?p?%`h1zCH?!fB%|j}L8B4F{p!nnZ9bX_QmoM zBXKS+Gz<65q06oE53*p_z6CaA28g0Kazzz?=LhQ?!O;=JmdViH37&K>-vt%xjj#~K z4#t$S+6MLg#8c1IABv_`RDSWG((#JOC*<)2{+h0AYgv$^Mg5++klF#7$X>_?O+^6# zdI}>k+B4*52q)>EGYxw^th#YVF*#gH`)|V-jt42yD8+OqbgdaY-k^Fj~X|HLp9nT(@Y_46p<^n03cPzzLHa9!=C*8~JL7`++!k zwxN>E`f7?l&zAyURBGYy&@b3o2}ShGhE2$FEzP`{(ir%gSC{G0;ENHMF&>JG=jd$s z%jxZ|`l!ZAfBQ2&cJDF8Qugf(@X)ekvBrb`T}E}v>s2qL=vW@a$>d^doX2h5e4U(C zR*kXAnTtJH1njue>r3z|5PwcC;g(-|V%%n9v9>Y|Nu+_*#!Kp29GiZP+c0bG(o>YH z;Az74jX3Hmlo{J;)(07;aJ%eo{I8V*+ZwIb!wJ%Y z82knFfcxCBX?B8Tn4x`HKp}ULtqZaXq^wdjuQdPA;rRJSfEhYKuY)L}U?Q_>fB*RT z{^#JYFh&J^SGO`02td zcq}l`w6rjrz|x*Kb?xDHmIM^#F@^O5C4uxCK2Obupy7k7*Ry{G)1Cq_crqY4=oddm zcYPGNFQ{&`Lbo@b&ih0tsto`Co@-#Ys1vP@2QkGlTqL+U>~sgp;CFozG`WfK5ec(p z{)KGMgF#S?q=)_e)D~<}wmE~nAuQ_4_nU%yh;8I}W#J<}YzZq+&L-nhYpp(}4Fd&;EI8bxV#dVZ zk<8v^eap<8jiM5)4E2Dj!w1^sT33vwxB!2y^2c%-c%2>_e{H04#xH4m;>0Bp#3uPH z`A(_hdBo#$%q=Y0_)f%#hx69tG^=Y};Hgpd9-aT~*?$l1ANC$Anqa?vRqG@e^J7i2jZ!+PmQM)9Tbr*nc)8dAD4k`bGYpH=;@>t#CjJX4#P(;fmD;R4RX~ z??TX`l6!7#OHJq`92=VlT~wM0+;4X|VRbMh9vk6W3N?;CD^CgV6^K4i3&2 z=fsf8SXSl{G&Q?J;-@gD3erP{G*pm~=#0a99alYpt=F}_;?mLTkFB)ewyV7t%en5q zR&OP04(3;Q2QfC7w`x+>5d2QsYbp5m4w$L;5p}3E{^Nd0@B&ha=xF3Y&N*k*3RGms zqX2_{Pyz92?}Qt`N08QAWZ7qNLYUx0gCD-9TRi>+BN#3~|V?k_rkkcvv{(c(Xy2;I|WKOL|mn*-nZzsJ{W_Tb=P|G0(c zV5a?4rLYEH|2scX4-0@mzg8L@4T!b@5~r_cM=TpZ!v8Uhhgy13)P=IXup0%|(iNb3PErA{&)(z6} z?BC8aVBZua!MB&|Vj^@=fo?VKy&EPr{^=VZ7bnQ>zn$)MwTbtc(u|1`wYvpN)Ytr5 z3+e|F()%F%3l6a1`uWXsd9^AkyiM=?&j98Tv6rwm;ecT(0=Dtd2}d!}NgP(!t3TJ> zMdvh|-Wc+Pz&B;TxZ~;Z)yN@wM>Te{Nxf5Lk0Ti(71Lw0yHV6Tx+X5|k7P?ZhYXX{ z306??4iC2Ur9n`e!TjkA^yoIrrq(;qJqS&b$`hXu;gZZoo~{p55{Q8rIlbS?&dM@2 zgL7&S)EDja8nq4g=$w-Rn1u8c+<|MD0RGymXG-S=2HC)o9t3Df{qlx{*DA@zz*(ag zR?~x&p(QT@zV96yo@E$18Q~}b;{V=Y`T4IyWG@KVlV`8(jUgatAt0iP zfO`0v@TZuou1sTw83O!TJM7w9~u7xcOk`8%(m z?ChRjf`(;#AmOQtd{xeduFH67>YY2jP1D>4TdsWa&AqbAythFk-*ukwV~CQYn$n|9 zoeK{mT#LvR-Ic@IQ=x1tJ;1Nvb6f}G6y?1mxeG^IFG-xUiRwWo(E?UAQKg98cW39t z3E*>6-2G$&Y+M!-#{SnP{;!Rk0Om%MCjjGfKZc~ZpkO|zd$YOmAsIF$NlgsCDxAkd zeEh@;5rR=4Jt!t)>nIwV*|rHzXMRI!^34@eTk0(npGP43-pIsmkB^n0>hGMg!u4?b zqE)@k4@U4W6ze3+B%v8>iWZ1>PQvtnTLpPsD0R|)dXPn~93>F?;EWylGpLG6c-TT; zEX^d$TL*$G4ae;(FML^G?$ns$gFHvU>s~6$SruuG!T>T(^hJ$=t((H1oa2)=4n!;4 z7dTT;{k&h;$UrmAMbaB~wb}r;#nTC7OeJHY#YNRsQ*Ns2pk23zOqvsSp?k2L@7SH^ z9kT?U#%$O@K*m@Ti3=GJ?9lMt$mfz`4qwF)eceQol#nZ~trvb`Ae zD2v>?p6g;~_^J4`HMt(0{J0^*jnlS0BhzA=VvIA4;62HB?jEG4)G^4mBOIaK@BhO} zfKhewQ`U!kAs4n1^pKZ>qhdp$rf#~kow0}@rT9%Quv7-G)A@2O3f>N?jY%jGxU+oG zZ#Lbj2b^KT~y98Qv0hVb89^jnfCXz;u&lE-OXMl zOTd%*wkkbV^Y(_QsQog&{<(WW^QFZuuMa^6H$4VsL|2oMyNnHkss)`Ou&G{>`FQqA z+ZyG4S_i29Y`G?WU#R~ZETF|>bjB`Wx_;@hMsQE{KDdrDg(ooZ-7n{T?|=ot5}(z( zN+Cg-dJ6jDpioD9PpA3WWVt-2Gwo@ep4743Mo6k_cJaFfV)Bo$5#UH_pjb-`9Kkk479w_MU&~n;)LV9&IP#Qi*A0eVQpC&YwieA5+hpW9!GCbz2AtwmZAZZaeQzlH!VHOg0HVGOLn~1;{$StDI{J1eFk+IGT;Z* z>2%%k*d%?-l*eHweX$~duq1*H!^dp3{=;SJ!zS6w36}FKG7ya4dnJ&gIxix0{z42A z6T?9!2@tg%zzXKovuMO~vZ^T`1mYyvD?K?Ip=o_J} z9`i+~``OH3OiY@H{Ky&h#J2=s11h_pyVf34#+cy(pNWk%kv6qb30@4kYfW^=d^)g%-G*X*?Xktci&Zve14oM=Q1NykcGwem@zN8%JL zr&{_=OTh4JU{_<#ie?%Q&`t78(OcgnNDd6Xgo8RPE|9Vfch9f30j z@gyPr(E{cIG7SSjcC-+0DpI>5(f4ZYy^%mxrzFG2t-g^i9-I`KC!JOdchpJkASmibX5RW#L3gRZ76{mca;@9d${y`MBL;6=JKp3|f~irU0#+_{t7XL1 z*XLCecEY~IwUV(5=}4;@thx)3e>2%jX?=2Fy0lJXcbl$3ZW_y|6t#^wv2)1nENeUG z@}&2b=s4beY)DZ<@o}pv90*0mFv^VnoeY&jZuKJWnM}r1w5qAOY~__g57@@!UgI&E zzXlp(BK4d(PJ2=AIG!RH03{JC3^uwcb@oblr4%v&qInGF{ZL8Ua*}OH^n8u_y|E$@ z9Xi@{C{?7-Jln;^x_qjZb>-DvR(oUwrR8w0p6D5LTviStK*~cGH8Cr{XhBGCKt1=| zSr{*Pwrx#(pO3P7$Ui{@<5i7Q8M8+KocafvB&W% z0A#}Q%ni7F3qb`v$6({ePLLW5;#yDq?5mz&N~}%Jx~Jy#XBz&a1qe2|c^qVBox1%g z$9MXk2v&#$5VlBg&3U4r?D~^6sz@}Up8vDY^sX^@a@qT6;S49Dc8(fx(C#^jCzEUZ#PtHcc++#vB`%os#Bk-y!<`6reOgQ z*eR;FYX|N*Z#U!z+m%lE9!jr%&bP)w*BSgp$SyCMeK7sR8L=c>efsVi&kDu(7}DU& zb=yMoI{Pm)mf+mG#2-^x5F0DDg%#HAYlZYWPO*pVrJr^jf0|BJ#nt=AlmZQ>HYVb5 zcc(wbGGi9l=Y85x^9_nI8~4ON;l65Win9X49hWo5XByNJ^R1fQqy0a*tG$gza`8Q9 zi*#XpZg#Jj^0(Trd{lzKy1Df%K2Or0E~Q}JgIH^%OybOMPu>(echmFj(Hcf^R{tag-p0=9(kT@y<9P+WVm43Bhr++ zGf~TCMQ2f6>&8k|HHzlNJT;@y=i<(d14jjmk?`~s?0$~1Yl@U1o|lOf2GxcfqcD*y z@z&Gfrbv~V!b3+BMakY}5fjjJ3XXDuTY)3*oS=dQ0Bs8>$f=Q)$JQa^C`#srmro1- z%Ym++T%ac{PNS0KtxRQV)I&-RiPm9Xv{X`OT~d!(9jqZRldX(V$$lkP znXpN~h7f2Nl4EH}|AJ2@j?6;+Mj0pXFRLe_t5^x}n>9!T>2HIdF(M@GDvRvMt>uIl zSEQ2mL4iDbMnX&UH^~TcEb_+U8k}8q&+=dXq5=k~1;vVUp95Si6vxMi zAI%Vk5z+=Z9k_}{Q#_9HlRnC3Lm-*)xLGKm@~n9}pAL?r12x3a7o8f*&TNghG8PL$tzW3j0gLkhBA0qzJF4pbpczi0qGyldgKwW=ZT-09^h|hc9)TkrH zp0JH6D*tt0=cFx{Z_YZtn8_icWV>p(&{h6=GT_^Rqy6Jn0J4JcYXZ||vGxxSY8Eb^ zPOpr_+aG_TxZ9$(M1oE9Zn=b?3`NA1o5}$`R~}9sRL>W+c&UXYZp}As-t()h>ToJq zO#KgS2W@9bmUQalwMKsc@`{$gbhnD4p)&_dOA1D?adw8OyL?nq$nU6kzxfb=bdBeB zL)!ZhIM_W147$Ac^a^ZU79l!xs?T*q^)^~U@E42Y3+x<8G8LK{rqpr$FRqU$*?Y@~rAQT5fIYiJ&pI;FXP<})1J z1YgMg>Qj{a0ifK+agiKzEzyw2-EsY})|jJZ`As7$u!gfpCOi*n9gY55STuGrNCH4g zg=L*i>8$;$K;k<~OwgjINh}vrn`Ym^wmPe!hfnOfTY|rAPuK2S`taQxK3}A_ zr<23iW>NGK(^k92M0+azGmj@`U0f{4D+h7;!H&MHsxaTsp{8q!yN`p5^_*LCMGi4x2m*bFRu6k8`fW&;IveLXz_HJvskwy=l%+xhD&rdUE|3^AKM+6H724=UvfT%b7j}zGsv7zFSURHoFz!hK%;45LEiV1J)_Ijc0 zD+cc-SC`m+gTzW>QY~-5>D>VmRsiB${YDA0t+&7JUONiRs@QP~<|b_?xeHAps%!XB z>AZj5E_ERgbIr7*jxxfg<`+-N1r)KVG(H=06afJN0j?uktsO|zo?1jU3zm_cLw$_kf}AGbup4a zG~To2*$zVkuik^_H?(=z=e7@BEp6yn`d>)Wq5kkef^hBF*gK2fjNC}bq#u(H4N9A} zmXR0^1}lkmwAB6RT${1EtxQL#CO@Q9vyCFoH`ouFy*DZ8ZECnDtMlk&VJVCFNzK6- zKu8ib=;h`)%j-}4gNQadPJU*O$VW5asASNE)X1{SN|FuwNli>IltIN<#X0tt5pE`K zVNfqyS3>J#V~TDk^Xt6$dyYfWN6}L;;ngaoP=CmMW0<$Ay(moZpT;A&pNhi6v!~x? zS6CM7oOTPoP)l;qph~DoDZvQ9!Wk$a0tD%MNVFcz^ZCc{6-~jIz>nwkYU9phcz+n( zmK#;Po&(uuoa2|>x`o}&Cdu)M4MBW<8w9Njj3tY7V#iqi}{0%6rY* zl%71WBfcDQG!ca?>lg*rbn7Hws=Y3)9Gz7|yQ83_R&9I7(tZz}uiv;V5Jyn6tN2Vy zRu6c*$5aUh%lvu!q!AlNG(LUaz}c$I;yGm&i7q4&y8zNE)jB1&h$|$~lbZ9^2kWtF z6voP%hWX7qUj<_&a?lg{Q8Rg;8XK@8rkf{@^POBraGH9-Gw5)%m>G|mg<|E@X6sxV zMyC@Z?b*@nHkH$lxOYspU+8}nXquh-kl-M5aOiZyoiu;ju$w0JjjrqwP>+;9ofXC$ zVmR96NIhh)Hs2=dmxYlE3RbvOg=rhuUXm4+l=l@VNZ<{$K&h12@S%D|2j-9Sbn|!` z=`5)u?15>Fxl}LdOYA$pl_!o5Z9* zGX)=qziEyoOR_|ImvrLL&CEdHcH7oD%>cW!c2x!(2Z&^^t2jE^H851i!;IXTLY?A9A3ML!C zcQlza!B(#+=QRhH~a@{_CP!KVaprEoq?UjT;01}zj1OuS+XD2VLsCerP|Bk}4(=3Z2?c9X5 z-S$hqr{PEiel5|{BB&c7JSp93wWSlcdFWNN23k)02l2pp!D+uIgo#i3Yr3kD;<3+Dcf9UAy%0@@8DdBgP>#D$< zfdqq~fog+YgJ6s)3kd9aq8D=C-ojDnGgw(4nvae$Nld3Wk!ia+!c!b87-4g3>8NH& z1TeaoD5t&P4rzE`JA}gk2yJGtbp=bb#xpr$vH>a zK)Auu0{}osHVsA|QW;C*8^F$D%^MB;Xz`A}KmM1SH*K*!m&C*k{TG-wyMoW#=c{!3 zNtbiWbn}NMB~dIAprpKHNO&!p@V`s|Fvzpn4oyU*l} zMELXlIk#Xedx4*fWnTiIa&n@vuuGzH49M(gN=in(X&u#2nKIvu%z!d>V#_VAZ^4Su zJFK#s&gh;my`M}MESsF3IZXVK6y&5E?)N{l-7V7w=ovEA_+N~@1yCeyo26YijnlZh zySrQCH10I+?rx2{yE`=Q4vo9JySw{e?>9TMGZXXgZbn8x1sN4ZL}lia=RVgtrtR)0 zrlz(A?~P8c!(q}P0M$7BUFXpcW%BB=kbN>8UVHEP#gk|)uSk-f znnsO#>RZiMVz}WS|AUCG`dFz5-pyCt*Y}MA$t!`VFSwWB?ol}Z=esA7=yS3i><&0+ zz*Y-U#P8>R5FuFKTbjMFGhC23CuB(srW$hKK-b^TZ(C2QP2n%kzt3v^`T2z}Ix8kP z4Nbv)(_)yYHl}Cggg+@Q*ImQk90_blR#W+1vnyS=I%+_6h1+^|d4dvb!2(l7_ z*xfb?3V-bOoFJtK7{E_VSeu~q2o$hA5Nizx;}N<(>&g#NL)vY%D&IiW(t=81nK{4& zsN(oK2Nq;)6jDVzLLG4k7|>9D+hFn>%UBI|`%X89YW>%fD=p@6GKblHx6quvt5#(h zaTP!ubt2kI__gSDmv?w5GBFVno$1RK0i~|dAC{zKbwMthec(b87UfD!Yo(rYT!W`oASym{#)QW{$lb zp?{7w@C5>#ncj3JOIX!=raI58>i2(+Tz%}){Ndht+$>6tyY z&$7$xIJ$XpN8#u;Y+hT^$n@e65F2??LTT-_xm)`>v{N6zsm|hHakKUcMcd2}FzMY8 z-rcYO|B{?-^m-|SfQ_cr+`rg$h`UDaB>tak|+x{Kcu9hQzo>rS)ViH@T5MClXLA?Y@f&|#wvKSnRMJ|%(SadkDwf+ ze4Ks(faT~RC zGe46J(tM@6R*d0teV*cBv)bq_&(^Hn>V337U+0+3FvbWFGJe0nqj7;e;wWCtH_{Bs znAX)@l#PLXV37v~JUbU?PVeAgbyTfQGi%$L%9xIx)$=dH3*3{QtEewmAZ=-+qsrCg zxQb3?E!l28===tE%as6rDc)DxLC!XW?Y;U$Pcbq@n`-}BO|ss{CpeNn!?7$Iub({pP%@@OQ1h7uhW;zKU93HC{;;3I$W$znkTPRO%z{)O)yA!w=tX#u;_B1!a^wg z%~E!i;P%cPxvA#3%M9VsCstH=-)btS$`+k4;K6WZGn+MkY4BHeG-8ihwkgFV9>njD z!Q6&ovg}{LbLM_US#>@qs36+lvHGf1eyv_LpIUaKTsZZ1e~Yg%!>O@m+^#E59GRmf zwL7P@;Vh`hU{XLnFmQ&dc4W*LtNRKdhy)^b(8CStgbnaphMRr?r}7kP+JO*BiWT@) zP5~JESej@74y-HI{%9;nM}b0X%qhg(<<@mkURL&4W#NMnF+s{tbE2dU<6CRK@2ke= z-9bLa0Y%Vl3^CXdI1~00)J?4o7M@5RhYv;wW|)s;&r6fB_%80sg->g%+1(u_(MC&?pP3*hNU zs-0J<2S1!DyCHpRX+?CU=furXqcGj^u-o`CAl^(AALy#hB6Nb<(bX0&ViF$bHt zoP^Hl7+PwHpgisBt<}P7QlH}gAFtpo4Z2(-xil@H0ml?ubGduefQ-x$V7B%JrWvPv zv1^!L^Kt_?_8xca`XCBmay}O^h#rqBILayTG`JKqKmn091!}D&lUlw9cgHjELOR$! z!v~~I!$FCD00+6q}&5o@@*6m>t&-0N;j9osts z9Nh(HF!wr~djXpF$txvptBRnRO8yTzb%r+pYG?=?;{5rJXyWjB-Bj$xk(2LwycE$q zm&ZXt_Y_c%&)!wrHD_c{e>k_dwNei%npKuCk z!j3|zCt+ePdtwCmz*}r@E(NQibuisY`&;F7+}H3zv==zYWZs$E{vBK1Btb4^C`rd% zRH_>7?rI9{s7~nwYT|TcM0#C%{;`a|&J|ZNZM{{nC*R`ZM1Q(o$D5WHfjfjIo5A z`xg@#RS95!qE*w7;SiXV6|m`N%yy&VJmys5s)cLOe`K-H1GXi=FhQag>w@^vd$8Pi z{je!Bp~-HhFD@CffbB=4(^$NJh<VqAkza8U7moJ)TK0!wJGYbpuC(hFS^B#=;ez70%4*^Fy5FcB?MW8W z^0IT7(d6Z~yJy1PE;kyhbZw`qCBPe1s2!uL+4B!J)Ehs?kM3ylylYlW!YCJn zM5A(&X-MJTZ;6dLfJ^0pT>3*h#)Xr}FQl~B77|zlz6mToc;Joeqvh^bAI+8a4SB%0C{ z+D(&n-E_Ws!*fkCRc~x8VLW|6j*qPM2MHG!H$NX-6UZ}I3z!?&vryDecjftygE4tF z7|9su;g}y|T-=I4Wb*=aSHi47E5QIP(5Yc!V;dv2KIYlck7`u%T-#2 z4zpjBP(VVAtb!6jFyG=)2!VO z9_|28*{Haf1hH7#5?jJSVJIa7qU~E!YZxJ+Rg^S^a237_pH&(ghn4V+z_+JnXwYx7 z(uu_}#_mFquX841snJ`TEYJg|Z-PkRb~_ajx$oYc_n2H*)Px5AU@$=hXJS4kG5Pcb zgph)Hql`>09|S1WoPzB;(t)t{J+pZ(jK?y;Ku!ha9nyKg0;Rg$I_kLFxX%u&P(=kN zprk-CYph{20@jUf1UXYO1Pou>Mdi)2`6*;I$LJqGrN%*7Mo2{n4uT1In&d@-8|sIa`5iWuhtL@*Jv#TBp}C?G5drvL*!XR zruKY)OO{5qUo=B;|4vX95_RwtW|i=X(Php4KyOxDv6gno zX#N)E-x0SDCYr&@yO{$LhW@1Mzp(*my)8|n6)~$l9jlE;R-2M>UQXvSnA&@p9 zk9xLpUYh~ZC|Li{DCD9C;U3vo$aDC^284o1bMgByjp>JDtt}mye=x%^QIOw_G#%oK zq~*aOvi#!!mLR3FT2UbE!Z-8dnVG-8rpy0Fx2Q7m``eO>?rCdc;U)K+#GICElab0o z?VRz!spJZdVs^KI9Ak;k*D}mdx`YqF2^>UF(mEs*li0T8%1l-QT|!1=|9EQ$au2o-(M~;#vR& zW-g*aso!bfq%KlL&}mof2~=CXuldxk16%LK%P(%Lx0zONQiW~lGNxq+6;K6okm~&3 z3`K-q5(%`Vff7}<%g^{=v1x9zw&HJ{@v~BO*bd2^MUte)*5*(S-%F`@i1Ufm%Mb=znM7084 zza&65hB~j1-O#3Pm^xt?9x=Kpg5eK27c(RqcK(UG%1gw8(Y& zb(Ea>X}pnOdN>#%^s5d!Gg*{2{Z>sh0wSXC?#{hiRrL{RlCSr#@Fj9lyElf!1Jexz zxo7njvvv2(sU4WhDIG8j-byszA)HeqcM-n(kvDCd?#uaN@I7zhVoNglPZ`zvHN%vB z_xrBK=D_0Pjtc&Ttq-@Pf+utlNYF+_cQ?AGY_!rY7(`H$nWo2y`Hec7hi|ba)t^y(hbx{_rUE z`(wwl=y+c|F1?2q9)rPpL^N6}qb}4+PlgCc10Ba>-nee#RTN<69canhj*t4k9DGC# z_PdPdOmN-aDcJ;eKRyP?k_vQy%>B=g{4r2#G5-?nlw0N3l{h>{`MD>~QYhlZh6x)Ym1CZ^m5?X~?Phj&7v91&TyD6NL<$>j&k zdI`?J{{RSrfP=(Bu<4aL2G8E>wWP1Z`8I={sD>a+TFOulhkwY%4V#8Frsh%-Gb&jc zx$fXF|7C^|;T6#f3hIPu@9vEyYEYB1u3>s)XAO$lb3Td70pECF8`s)D0Y+^}H!~D! zC;whRJ3?F1hgL+}@uX#J#2*?hu$7n{hq5firbY|toY(`Il=H>wn=x=E{u9~A@y8O( zGEZ~Z3~_?DxA5L!_bZ}b-MpR(4O+G=Z*sHUp#7z|XE@?n%5=hdRcr*!R)@5a|#Q(r#E zERvN^gVNpRKzN?egmX;j^b_Ig2+InN(ZyuA7S9#yvEDNROjzy0(x847x!Z@cRFLpi z`0;Q#IRx9XnN1*L$sM`8I8E;7c3;0HF0gFPS=WIzIVv#s-dn+nEO^NW-eZ7+Sp{8_ zS`>IDk%&S!M=!27OU5sbp|8f^5N4nxsBJl2YElmw3Y!`72%Lu3@z%w$CydT&?a7x9^*=&t-=Z#??UZ0)t%Ty==)b zX3V+joz)wJv;c*w?0nY(2TEPnVi@M+G#VSL2X$ns2{d4!mZ~voLdZCgE?({a-;L|m zh>6zD$N)egT1{%t-2zIoCgO%eKe>}egsMk%kQ&2evil+5)mqf+Zw4w`wJ zUM(KX^NL`brYUk9oNfp?0Fh0pxK&I_H;>m0%yB^)xdCRY`HLQQti7Cw;SqC)yIOI4 z_p;EFTK}_loS0Zr#e_+yc*w2VDLGt%V2z3%yqs)Oz$q8DlVq{1QmFX(rc|0OMfFm!D&upNM zpwfGJ_m7T%$zqjtCh-J6XS0Xe`gzA|ayo5(ilf%=$tm9%n#XE=CcE~=k}IO`^Dal! zCqCx&RhSw3-#Ml{r%P5F%@7pOy2CToAsg2$7ad@t=fO6x+r;!&1V)9mu8l8pLAh4d zh7En-;#X*pLFfitrL|@8Mse)VH2*P|)0xi?%zY#A{o~Y%FMSN%@WX~~1ceyS%BBq^ z{~4o)@sclRvv7pQs#5*x;L3`JKKRYNK$n_9#ufZ{`D?Cs`BKiD1PrurKj~VPVyF@hnkutD zgmuMN6!b7A7!Ck1fQHL0Xm(MHn*RbZu?#6Tj`IE|%1Orl(g~_nHiCRBd4cV$gvf)5 zRboRSEJw3+Xzwd_2LfeSH=6Pem zmk}ZhW)mo?yFHv?0e<(Pe!uyO6}~$?nsY-Ti9eakff=9n9X`4+IJbV<6-k})58Z5P z{CIMg|BK-|*2aAmx|vo0R;rNp9qk{W!F%g%N5#ZP?+KB%&5+}IddP)Jt>8BKlKpYH z?d5Gt&2rKWwKhI!+gKkfcAx~?3YmZUK+>@sA&7m1ahze`gAI-tUCUSf>yQ2(-r(z5 zOs2|OWV~J#!FKV%R$=S-`*ypf$a9SLIQk%ERm;6sh1_lS$(_Se$F;eoFT#^+6eRA@ z*K+bTH}Ct=7TI&E(?i+rAK6*u{E`6Hf zg`^q+oDq_}q~LV{yap*9%FO?1d{iLICA2yr!cU~)N3EI01)pfnXJ@Mk6A1?RHGsH& zVL!COPW}-YvYH0pSg%@>hV(=iIQL%&i{E-y47-BXN|LB#L;x?J3N|@tVPO!$v;9*s zHCSvLrM-uNM%GI7AW)yXELRW$C;<5NF4W~?V$);BTR%!n>PNI2m-q*)=9oP9XWpmN z4F^fUKjW0`uW#?~B~Xt7<^g2IY+Najk?$S+VkjR zMC8BI6eof^SnFND1_AFO`Lcp2du$m z0sh3flx4&)=#Y@=rOT4L_QTC{$|Ek8B zsm0}AK*m9ge%5CW^KZ9BJdaH$LBgXt2K8%q^#<0c932*Wec2m{iOGtIJ-CQ!0|0Eb z6eg9Vo*yh83m}%iFb1_(YxSQ@i*?H4&v*&cumR1-b1c;pCA;D76o>NW=v*cKyKqs|5nDygkKI%Uhw%=*7` z#NN|#$0ja}9B$hw)9OiP4{u7% z^fV5t1Cfb2C`5!Pf^KXq)EWD-D7Ooy=gYxj->JSjyT_Cm&m%9IT~NUF7;JzxepZXR z>JUca0jbX_-}9w^gbIAOuO9}~Z!28f*QRZ?F4@_R>BXM0I|3_ty4`%2x32;?7}Guq z?*8vP30SCIz?BBtGBb9H6etHi# z{T=$r$>@tH>=4F|*xzqqa#~{SV5=ehROjYNc5U$X4%_S((a}vrwBPe-p1VuILKT8P zCTxumJIQhBjc!+GMN8>r(@cN^^5LSAK1oQZ<+LXb9?euuxY~?1Hp+yL`X=iF^o~EU zC_~RahRty;uLr_sFZv6_j_9h5e9+SNjs)&9xJKrDW}$lm&2wL4TP3Jg0qM;dCipV% zHi$Nd3nyUYTeSPGrz=5vHz&8RlB&Uco=#WRT;?A|F=+3qI9U(IQwkN0&F_P7+PCRc zcGZ{A~0zwPYbXKS)9F%-GF^@JlvZyi2{9w=Z!>Rk4r z7_)FD>*sWYiBpDmn0p)n0+!^5%w&|}ZmH|Laj1rt4I;r68viDZ(|w1elZU_tf4OY+ z<+jQ|FK}UMJ#I3QSQLiMA&OnZ&0Br$iP~>S{(C;Y>}xl~;#no>g|2d~x8rLEy)hU(3P)9P zl9NRr9^O?+HF4Dx_2Yw=WMov<>q!li6N>T z|0rQapjsrMeNz!j>Ip3dCE=fqe?wzdV#_$8fVK8SwmiWxXb9ptFjfYU_eRojtp+fO%|0omY{C4U@^3vJ zl++W$lX8OWVor9~;YYO~J9Q}RmLnXZCvMIf$FIk%cOdnqBVmoqW}z14e9!LRUN`~Q zcXue17Tmz3E;pTnwF~{X%KKA$rBY>4W%TbFvc-sbMlaag?}E~aYN0Jlax1c~#U z@8=iKf4@|3hT!F=AnWY_r8J#ax72M&K{f(cBBH|jSY`x(bW{Lxf3g3g7v*)pE8Y?e zFvw2DP;jbXPy8POg*A(pJ!&o&K%QqYGj29+bB~Z3 zdD-VPtmV|#co%6VAoE;JZ^7!mAD~W!!50{%8mW4+P5@#!n9Zrjv|zetro)XK8vlxr zfxwxR#e=W)g&CoCZxxue@md1$5GAL|bh$Ovptc2K0=Dq1hcJO_Zu-w=SzN+l=V+Dn zBaVFa7{|>zzWdEBgD8;pH6vQxnJ1!8j-RV{{J;YtCdC)n_}s5VRmuB-iuz}w?ZWxQ zayJMKCmYp1oW#py1-`Zx%Rt~rP?QO=T(1p2tHWTYL2~aRnDdWs>=4-YA=Fi-36EP% zvd~GYz%?934Y&vew3&;Ai)bSp^yqNEI#8~ce zk+%LA92(RKnp5BfC$ej(iI^k9u<)CP!pJ}QH70y^RzkPuJAQ~y7UcFt^O4oK{2=;7 zZ!s@hLST8jI$jg0>QXakL_@~W<&#gS&z`pE2g+>8$JV`XxGH^69lXpryvkv6Kz8J9 zJhb3m-D|?9@4>%KPl?~i|CZFSzPw-JY4$S5Sj%c&#X=$kWt=p|q)u5vR&B|>6Pt

    hm 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

    %4Ocm&BH;Uj&U@R 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 UIOjPIG!MF^q;aEA!TiyG0f4c#8UO$Q diff --git a/src/contrib/monitoring/nagios/hostgroups.cfg b/src/contrib/monitoring/nagios/hostgroups.cfg deleted file mode 100644 index ea59a66e874..00000000000 --- a/src/contrib/monitoring/nagios/hostgroups.cfg +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# A group containing all the ZooKeeper nodes - -define hostgroup { - hostgroup_name zookeeper-servers - alias ZooKeeper Servers - members localhost -} - - diff --git a/src/contrib/monitoring/nagios/services.cfg b/src/contrib/monitoring/nagios/services.cfg deleted file mode 100644 index dde6ab75952..00000000000 --- a/src/contrib/monitoring/nagios/services.cfg +++ /dev/null @@ -1,67 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ZooKeeper Node specific services - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Open_File_Descriptors_Count - check_command check_zk_node!zk_open_file_descriptor_count!500!800 -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Ephemerals_Count - check_command check_zk_node!zk_ephemerals_count!10000!100000 -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Avg_Latency - check_command check_zk_node!zk_avg_latency!500!1000 -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Max_Latency - check_command check_zk_node!zk_max_latency!1000!2000 -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Min_Latency - check_command check_zk_node!zk_min_latency!500!1000 -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Outstanding_Requests - check_command check_zk_node!zk_outstanding_requests!20!50 -} - -define service { - hostgroup_name zookeeper-servers - use generic-service - service_description ZK_Watch_Count - check_command check_zk_node!zk_watch_count!100!500 -} - diff --git a/src/contrib/monitoring/nagios/zookeeper.cfg b/src/contrib/monitoring/nagios/zookeeper.cfg deleted file mode 100644 index ed4cfab834f..00000000000 --- a/src/contrib/monitoring/nagios/zookeeper.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# 'check_zookeeper' command definition -define command { - command_name check_zookeeper - command_line /usr/lib/nagios/plugins/check_zookeeper.py -s "localhost:2181,localhost:2182,localhost:2183" -o nagios -k '$ARG1$' -w '$ARG2$' -c '$ARG3$' - # ATTENTION: you should update the list of servers defined above -} - -# 'check_zk_node' command definition -define command { - command_name check_zk_node - command_line /usr/lib/nagios/plugins/check_zookeeper.py -s $HOSTADDRESS$:2181 -o nagios -k '$ARG1$' -w '$ARG2$' -c '$ARG3$' - # ATTENTION: you should update the port. default: 2181 -} - diff --git a/src/contrib/monitoring/test.py b/src/contrib/monitoring/test.py deleted file mode 100755 index 3941291fcab..00000000000 --- a/src/contrib/monitoring/test.py +++ /dev/null @@ -1,282 +0,0 @@ -#! /usr/bin/env python -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -import socket -import sys - -from StringIO import StringIO - -from check_zookeeper import ZooKeeperServer, NagiosHandler, CactiHandler, GangliaHandler - -ZK_MNTR_OUTPUT = """zk_version\t3.4.0--1, built on 06/19/2010 15:07 GMT -zk_avg_latency\t1 -zk_max_latency\t132 -zk_min_latency\t0 -zk_packets_received\t640 -zk_packets_sent\t639 -zk_outstanding_requests\t0 -zk_server_state\tfollower -zk_znode_count\t4 -zk_watch_count\t0 -zk_ephemerals_count\t0 -zk_approximate_data_size\t27 -zk_open_file_descriptor_count\t22 -zk_max_file_descriptor_count\t1024 -""" - -ZK_MNTR_OUTPUT_WITH_BROKEN_LINES = """zk_version\t3.4.0 -zk_avg_latency\t23 -broken-line - -""" - -ZK_STAT_OUTPUT = """Zookeeper version: 3.3.0-943314, built on 05/11/2010 22:20 GMT -Clients: - /0:0:0:0:0:0:0:1:34564[0](queued=0,recved=1,sent=0) - -Latency min/avg/max: 0/40/121 -Received: 11 -Sent: 10 -Outstanding: 0 -Zxid: 0x700000003 -Mode: follower -Node count: 4 -""" - -class SocketMock(object): - def __init__(self): - self.sent = [] - - def settimeout(self, timeout): - self.timeout = timeout - - def connect(self, address): - self.address = address - - def send(self, data): - self.sent.append(data) - return len(data) - - def recv(self, size): - return ZK_MNTR_OUTPUT[:size] - - def close(self): pass - -class ZK33xSocketMock(SocketMock): - def __init__(self): - SocketMock.__init__(self) - self.got_stat_cmd = False - - def recv(self, size): - if 'stat' in self.sent: - return ZK_STAT_OUTPUT[:size] - else: - return '' - -class UnableToConnectSocketMock(SocketMock): - def connect(self, _): - raise socket.error('[Errno 111] Connection refused') - -def create_server_mock(socket_class): - class ZooKeeperServerMock(ZooKeeperServer): - def _create_socket(self): - return socket_class() - return ZooKeeperServerMock() - -class TestCheckZookeeper(unittest.TestCase): - - def setUp(self): - self.zk = ZooKeeperServer() - - def test_parse_valid_line(self): - key, value = self.zk._parse_line('something\t5') - - self.assertEqual(key, 'something') - self.assertEqual(value, 5) - - def test_parse_line_raises_exception_on_invalid_output(self): - invalid_lines = ['something', '', 'a\tb\tc', '\t1'] - for line in invalid_lines: - self.assertRaises(ValueError, self.zk._parse_line, line) - - def test_parser_on_valid_output(self): - data = self.zk._parse(ZK_MNTR_OUTPUT) - - self.assertEqual(len(data), 14) - self.assertEqual(data['zk_znode_count'], 4) - - def test_parse_should_ignore_invalid_lines(self): - data = self.zk._parse(ZK_MNTR_OUTPUT_WITH_BROKEN_LINES) - - self.assertEqual(len(data), 2) - - def test_parse_stat_valid_output(self): - data = self.zk._parse_stat(ZK_STAT_OUTPUT) - - result = { - 'zk_version' : '3.3.0-943314, built on 05/11/2010 22:20 GMT', - 'zk_min_latency' : 0, - 'zk_avg_latency' : 40, - 'zk_max_latency' : 121, - 'zk_packets_received': 11, - 'zk_packets_sent': 10, - 'zk_server_state': 'follower', - 'zk_znode_count': 4 - } - for k, v in result.iteritems(): - self.assertEqual(v, data[k]) - - def test_recv_valid_output(self): - zk = create_server_mock(SocketMock) - - data = zk.get_stats() - self.assertEqual(len(data), 14) - self.assertEqual(data['zk_znode_count'], 4) - - def test_socket_unable_to_connect(self): - zk = create_server_mock(UnableToConnectSocketMock) - - self.assertRaises(socket.error, zk.get_stats) - - def test_use_stat_cmd_if_mntr_is_not_available(self): - zk = create_server_mock(ZK33xSocketMock) - - data = zk.get_stats() - self.assertEqual(data['zk_version'], '3.3.0-943314, built on 05/11/2010 22:20 GMT') - -class HandlerTestCase(unittest.TestCase): - - def setUp(self): - try: - sys._stdout - except: - sys._stdout = sys.stdout - - sys.stdout = StringIO() - - def tearDown(self): - sys.stdout = sys._stdout - - def output(self): - sys.stdout.seek(0) - return sys.stdout.read() - - -class TestNagiosHandler(HandlerTestCase): - - def _analyze(self, w, c, k, stats): - class Opts(object): - warning = w - critical = c - key = k - - return NagiosHandler().analyze(Opts(), {'localhost:2181':stats}) - - def test_ok_status(self): - r = self._analyze(10, 20, 'a', {'a': 5}) - - self.assertEqual(r, 0) - self.assertEqual(self.output(), 'Ok "a"!|localhost:2181=5;10;20\n') - - r = self._analyze(20, 10, 'a', {'a': 30}) - self.assertEqual(r, 0) - - def test_warning_status(self): - r = self._analyze(10, 20, 'a', {'a': 15}) - self.assertEqual(r, 1) - self.assertEqual(self.output(), - 'Warning "a" localhost:2181!|localhost:2181=15;10;20\n') - - r = self._analyze(20, 10, 'a', {'a': 15}) - self.assertEqual(r, 1) - - def test_critical_status(self): - r = self._analyze(10, 20, 'a', {'a': 30}) - self.assertEqual(r, 2) - self.assertEqual(self.output(), - 'Critical "a" localhost:2181!|localhost:2181=30;10;20\n') - - r = self._analyze(20, 10, 'a', {'a': 5}) - self.assertEqual(r, 2) - - def test_check_a_specific_key_on_all_hosts(self): - class Opts(object): - warning = 10 - critical = 20 - key = 'latency' - - r = NagiosHandler().analyze(Opts(), { - 's1:2181': {'latency': 5}, - 's2:2181': {'latency': 15}, - 's3:2181': {'latency': 35}, - }) - self.assertEqual(r, 2) - self.assertEqual(self.output(), - 'Critical "latency" s3:2181!|s1:2181=5;10;20 '\ - 's3:2181=35;10;20 s2:2181=15;10;20\n') - -class TestCactiHandler(HandlerTestCase): - class Opts(object): - key = 'a' - leader = False - - def __init__(self, leader=False): - self.leader = leader - - def test_output_values_for_all_hosts(self): - r = CactiHandler().analyze(TestCactiHandler.Opts(), { - 's1:2181':{'a':1}, - 's2:2181':{'a':2, 'b':3} - }) - self.assertEqual(r, None) - self.assertEqual(self.output(), 's1_2181:1 s2_2181:2') - - def test_output_single_value_for_leader(self): - r = CactiHandler().analyze(TestCactiHandler.Opts(leader=True), { - 's1:2181': {'a':1, 'zk_server_state': 'leader'}, - 's2:2181': {'a':2} - }) - self.assertEqual(r, 0) - self.assertEqual(self.output(), '1\n') - - -class TestGangliaHandler(unittest.TestCase): - - class TestableGangliaHandler(GangliaHandler): - def __init__(self): - GangliaHandler.__init__(self) - self.cli_calls = [] - - def call(self, cli): - self.cli_calls.append(' '.join(cli)) - - def test_send_single_metric(self): - class Opts(object): - @property - def gmetric(self): return '/usr/bin/gmetric' - opts = Opts() - - h = TestGangliaHandler.TestableGangliaHandler() - h.analyze(opts, {'localhost:2181':{'latency':10}}) - - cmd = "%s -n latency -v 10 -t uint32" % opts.gmetric - assert cmd in h.cli_calls - -if __name__ == '__main__': - unittest.main() - diff --git a/src/contrib/rest/NOTICE.txt b/src/contrib/rest/NOTICE.txt deleted file mode 100644 index 2a92254421e..00000000000 --- a/src/contrib/rest/NOTICE.txt +++ /dev/null @@ -1,7 +0,0 @@ -This contrib module includes software developed under the -COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 - -This contrib depends on binary only jar libraries developed at: - -https://jersey.dev.java.net/ -https://grizzly.dev.java.net/ diff --git a/src/contrib/rest/README.txt b/src/contrib/rest/README.txt deleted file mode 100644 index 30f2e5d192e..00000000000 --- a/src/contrib/rest/README.txt +++ /dev/null @@ -1,72 +0,0 @@ - -ZooKeeper REST implementation using Jersey JAX-RS. --------------------------------------------------- - -This is an implementation of version 2 of the ZooKeeper REST spec. - -Note: This interface is currently experimental, may change at any time, -etc... In general you should be using the Java/C client bindings to access -the ZooKeeper server. - -This REST ZooKeeper gateway is useful because most of the languages -have built-in support for working with HTTP based protocols. - -See SPEC.txt for details on the REST binding. - -Quickstart: ------------ - -1) start a zookeeper server on localhost port 2181 - -2) run "ant run" - -3) use a REST client to access the data (see below for more details) - - curl http://localhost:9998/znodes/v1/ - -or use the provided src/python scripts - - zk_dump_tree.py - - -Tests: ----------- - -1) the full testsuite can be run via "ant test" target -2) the python client library also contains a test suite - -Examples Using CURL -------------------- - -First review the spec SPEC.txt in this directory. - -#get the root node data -curl http://localhost:9998/znodes/v1/ - -#get children of the root node -curl http://localhost:9998/znodes/v1/?view=children - -#get "/cluster1/leader" as xml (default is json) -curl -H'Accept: application/xml' http://localhost:9998/znodes/v1/cluster1/leader - -#get the data as text -curl -w "\n%{http_code}\n" "http://localhost:9998/znodes/v1/cluster1/leader?dataformat=utf8" - -#set a node (data.txt contains the ascii text you want to set on the node) -curl -T data.txt -w "\n%{http_code}\n" "http://localhost:9998/znodes/v1/cluster1/leader?dataformat=utf8" - -#create a node -curl -d "data1" -H'Content-Type: application/octet-stream' -w "\n%{http_code}\n" "http://localhost:9998/znodes/v1/?op=create&name=cluster2&dataformat=utf8" - -curl -d "data2" -H'Content-Type: application/octet-stream' -w "\n%{http_code}\n" "http://localhost:9998/znodes/v1/cluster2?op=create&name=leader&dataformat=utf8" - -#create a new session -curl -d "" -H'Content-Type: application/octet-stream' -w "\n%{http_code}\n" "http://localhost:9998/sessions/v1/?op=create&expire=10" - -#session heartbeat -curl -X "PUT" -H'Content-Type: application/octet-stream' -w "\n%{http_code}\n" "http://localhost:9998/sessions/v1/02dfdcc8-8667-4e53-a6f8-ca5c2b495a72" - -#delete a session -curl -X "DELETE" -H'Content-Type: application/octet-stream' -w "\n%{http_code}\n" "http://localhost:9998/sessions/v1/02dfdcc8-8667-4e53-a6f8-ca5c2b495a72" - - diff --git a/src/contrib/rest/SPEC.txt b/src/contrib/rest/SPEC.txt deleted file mode 100644 index 8c5f70175a6..00000000000 --- a/src/contrib/rest/SPEC.txt +++ /dev/null @@ -1,355 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -A REST HTTP gateway for ZooKeeper -================================= - -Specification Version: 2 - -ZooKeeper is meant to enable distributed coordination and also store -system configuration and other relatively small amounts of information -that must be stored in a persistent and consistent manner. The -information stored in ZooKeeper is meant to be highly available to a -large number of nodes in a distributed-computing cluster. - -ZooKeeper offers a client-side library that supports rich semantics -that include strict ordering guarantees on operations, the creation of -ephemeral znodes, and the ability to watch for changes to state. -However, where clients need simple "CRUD" (create, read, update, -delete) operations, the ZooKeeper libraries can be cumbersome, both to -the programmers who have to use them (who are increasingly used to -REST-style APIs), and to the operators who have to deploy and update -them (for whom deploying and updating client libraries can be very -painful). - -It turns out that most languages comes with client libraries for HTTP -that are easy and familiar to program against, and deployed as part of -the language runtime. Thus, for simple CRUD clients, an HTTP gateway -would be a less cumbersome interface than the ZooKeeper library. - -This document describes a gatway for using HTTP to interact with a -ZooKeeper repository. - -Binding ZooKeeper to HTTP -------------------------- - -Encoding --------- - -UTF-8 unless otherwise noted - -Paths ------ - -A ZooKeeper paths are mapped to IRIs and URIs as follows. ZK paths -are converted to IRIs by simply percent-encoding any characters in the -ZK path that are not allowed in IRI paths. ZK paths are converted to -URIs by mapping them first to IRIs, then converting to URIs in the -standard way. - -Going from URIs and IRIs is the reverse of the above but for one -difference: any "." and ".." segments in an IRI or URI must be folded -before conversion. (Fortunately, ZK does not allow "." and ".." -segments in its paths.) - -ZK and IRIs recommend the same practices when it comes to Unicode -normalization: ultimately, normalization is left to application -designers, but both recommend that application designers use NFC as a -best practice. - -Root ----- - -The following examples assume that the ZooKeeper znode heirarchy is -bound to the root of the HTTP servers namespace. This may not be the -case in practice however, the gateway may bind to some prefix, for -example the URL for accessing /a/b/c may be: - - http://localhost/zookeeper/znodes/v1/a/b/c - -This is perfectly valid. Users of the REST service should be aware of -this fact and code their clients to support any root (in this case -"/zookeeper" on the server localhost). - - -Basics: GET, PUT, HEAD, and DELETE ----------------------------------- - -HTTP's GET, PUT, HEAD, and DELETE operations map naturally to -ZooKeeper's "get," "set," "exists," and "delete" operations. - -ZooKeeper znodes have a version number that changes each time the -znode's value is updated. This number is returned by "get," "set," and -"exists" operations. The "set" and "delete" operations optionally take -a version number. If one is supplied, then "set" or "delete" will fail -if the current version of the znode doesn't match the version-number -supplied in the call. This mechanism supports atomic read-modify-write -cycles. Set/delete requests may include an optional parameter -"version" which defaults to no version check. - - -Getting ZooKeeper children --------------------------- - -We overload the GET method to return the children of a ZooKeeper. In -particular, the GET method takes an optional parameter "view" which -could be set to one of type values, either "data" or "children". The -default is "data". Thus, to get the children of a znode named -"/a/b/c", then the GET request should start: - - GET /znodes/v1/a/b/c?view=children HTTP/1.1 - -If the requested view is "data", then the data of a znode is returned -as described in the previous section. If the requested view is -"children", then a list of children is returned in either an XML -document, or in a JSON object. (The default is JSON, but this can be -controlled changed by setting the Accept header.) - - -Creating a ZooKeeper session ----------------------------- - -In order to be able to create ephemeral nodes you first need to start -a new session. - - POST /sessions/v1?op=create&expire= HTTP/1.1 - -If the session creation is successful, then a 201 code will be returned. - -A session is just an UUID that you can pass around as a parameter and -the REST server will foward your request on the attached persistent -connection. - -Keeping a session alive ------------------------ - -To keep a session alive you must send hearbeat requests: - - PUT /sessions/v1/ HTTP/1.1 - -Closing a ZooKeeper session ---------------------------- - -You can close a connection by sending a DELETE request. - - DELETE /sessions/v1/ HTTP/1.1 - -If you don't close a session it will automatically expire after -the amount of time you specified on creation. - -Creating a ZooKeeper znode --------------------------- - -We use the POST method to create a ZooKeeper znode. For example, to -create a znode named "c" under a parent named "/a/b", then the POST -request should start: - - POST /znodes/v1/a/b?op=create&name=c HTTP/1.1 - -If the creation is successful, then a 201 code will be returned. If -it fails, then a number of different codes might be returned -(documented in a later subsection). - -ZooKeeper's create operation has a flag that tells the server to -append a sequence-number to the client-supplied znode-name in order to -make the znode-name unique. If you set this flag and ask to create a -znode named "/a/b/c", and a znode named "/a/b" already exists, then -"create" will create a znode named "/a/b/c-#" instead, where "#" is and -integer required to generate a unique name in for format %10d. - -To obtain this behavior, an additional "sequence=true" parameter -should be added to the parameters of the POST. (Note that "sequence" -is an optional parameter, that defaults to "false"; this default may -be provided explicitly if desired.) - -On success the actual path of the created znode will be returned. - -If you want to create an ephemeral node you need to specify an -additional "ephemeral=true" parameter. (Note that "ephemeral" is an optional -parameter, that defaults to "false") - -(Note: ZooKeeper also allows the client to set ACLs for the -newly-created znode. This feature is not currently supported by the -HTTP gateway to ZooKeeper.) - - -Content types and negotiation ------------------------------ - -ZooKeeper REST gateway implementations may support three content-types -for request and response messages: - -* application/octet-stream - - HEAD - returns nothing (note below: status = 204) - GET - returns the znode data as an octet-stream - PUT - send binary data, returns nothing - POST - send binary data, returns the name of the znode - DELETE - returns nothing - - For PUT and HEAD some other content-type (i.e. JSON or XML) must be - used to access the Stat information of a znode. - -* application/json, application/javascript & application/xml - - HEAD - returns nothing - GET - returns a STAT or CHILD structure - PUT - send binary data, returns a STAT structure (sans data field) - POST - send binary data, returns a PATH structure - DELETE - returns nothing - - (structures defined below) - - Results returning DATA may include an optional "dataformat" - parameter which has two possible values; base64 (default) or - utf8. This allows the caller to control the format of returned data - and may simplify usage -- for example cat'ing results to the command - line with something like curl, or accessing a url through a browser. - Care should be exercised however, if utf8 is used on non character - data errors may result. - - "application/javascript" requests may include an optional "callback" - parameter. The response is wrapped in a callback method of your - choice. e.g. appending &callback=foo to your request will result in - a response body of: foo(...). Callbacks may only contain - alphanumeric characters and underscores. - -PATH - path : string - uri: string - - path is the full path to the znode as seen by ZooKeeper - - uri is the full URI of the znode as seen by the REST server, does not - include any query parameters (i.e. it's the path to the REST resource) - -SESSION - id : string UUID - uri : string - -CHILD - PATH - child_uri_template: string - children : [ string* ] - - The children list of strings contains only the name of the child - znodes, not the full path. - - child_uri_template is a template for URI of child znodes as seen by the - REST server. e.g. "http://localhost:9998/znodes/v1/foo/{child}", where - foo is the parent node, and {child} can be substituted with the name - of each child in the children array in order to access that resource. - This template is provided to simplify child access. - -STAT - PATH - encoding : value of "base64" or "utf8" - data : base64 or utf8 encoded string - stat : - czxid : number - mzxid : number - ctime : number - mtime : number - version : number - cversion : number - aversion : number - ephemeralOwner : number - datalength : number - numChildren : number - pzxid : number - - -Error Codes ------------ - -The ZooKeeper gateway uses HTTP response codes as follows: - - * 200 (Success) - ZOK for "get" "set" "delete", "yes" case of "exists" (json/xml) - * 201 (Created) - ZOK for "create" - * 204 (No Content) - ZOK for "yes" case of "exists" (octet) - * 400 (Bad Request) - ZINVALIDACL, ZBADARGUMENTS, version param not a number - * 401 (Unauthorized) - ZAUTHFAILED - * 404 (Not Found) - ZOK for "no" case of "exists;" ZNONODE for "get," "set," and "delete" - * 409 (Conflict) - ZNODEEXISTS, ZNONODE for "create," ZNOTEMPTY, - * 412 (Precondition Failed) - ZBADVERSION - * 415 (Unsupported Media Type) - if content-type of PUT or POST is not "application/octet-stream" - * 500 (Internal Server Error) - Failure in gateway code - * 501 (Not Implemented) - HTTP method other than GET, PUT, HEAD, DELETE - * 502 (Bad Gateway) - All other ZooKeeper error codes - * 503 (Service Unavailable) - ZSESSIONEXPIRED, ZCONNECTIONLOSS, (gateway will try to reestablish the connection, but will not hold the request waiting...) - * 504 (Gateway Timeout) - ZOPERATIONTIMEOUT, or ZooKeeper does not return in a timely manner - -Note that these are the codes used by the HTTP-to-Gateway software -itself. Depending on how this software is configured into a Web -server, the resulting Web Server might behave differently, e.g., it -might do redirection, check other headers, etc. - -Error Messages --------------- - -Error messages are returned to the caller, format is dependent on the -format requested in the call. - -* application/octet-stream - - A string containing the error message. It should include the request - and information detailing the reason for the error. - -* application/json - - { "request":"GET /a/b/c", "message":"Node doesn't exist" } - -* application/xml - - - - GET /a/b/c - Node doesn't exist - - - -Binding ZooKeeper to an HTTP server ------------------------------------ - -It might be sage to assume that everyone is happy to run an Apache -server, and thus write a "mod_zookeeper" for Apache that works only -for the Apache Web Server. However, different operational -environments prefer different Web Servers, and it would be nice to -support more than one Web server. - -Issues: - - * Configuration. - - * Defining a root: Need to provide a URL alias and associate it - with a server. Need to be able to map different aliases to - different servers (implemented via multiple ZK connections). - - * Sharing connection across multiple processes. - - * Asynchronous. - - * Adaptors. - - * Code re-use. - - -Authentication -- TBD, not currently supported - -...the config file should contain authentication material for the gateway - -...the config file should contain an ACL list to be passed along to "create" - -...would we ever want to authenticate each request to ZooKeeper?... diff --git a/src/contrib/rest/build.xml b/src/contrib/rest/build.xml deleted file mode 100644 index 649dff75295..00000000000 --- a/src/contrib/rest/build.xml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tests failed! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/rest/conf/keys/README b/src/contrib/rest/conf/keys/README deleted file mode 100644 index 085810a067b..00000000000 --- a/src/contrib/rest/conf/keys/README +++ /dev/null @@ -1,8 +0,0 @@ - -In order to generate .jks (java keystore files) you need to use keytool. - -The password for the existing .jks is "123456" (without quotes). - -Some tutorials: - - http://www.mobilefish.com/tutorials/java/java_quickguide_keytool.html - diff --git a/src/contrib/rest/conf/keys/rest.cer b/src/contrib/rest/conf/keys/rest.cer deleted file mode 100644 index 13e5aabe562f0d0507d3c6709a3a3527ce258f19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 595 zcmXqLV)8d=V%)KSnTe5!iNz;sF0TPE8>d#AN85K^Mn+av27??!0RuiZ=1>+kVfN6x z?7aN)JeUXvh6p=`2p5J3H--o^T*N?5oY&C6z{0@P(9qDt$S_Kr*Vxp+*vK5prL$`q z=Oc#(BP#=QV=se2V<%H%BSYWbGS>f3PJXvm4%r-kd}HQ?l{*@lYdP% zKXaEoN*khNe+YWI`;11mY-+!bZpKk1GMmr3`n#2w6gv+_ab<1?{)@-|1UO&Uzt2^?V`hx> zo~?GG?UC!1EZ6iZIv5CUjxayK`25YYdr9R_ivm4AKHV<1dRh-#_&ZUf9R~ND9?75o t@hPEFdt$!Tolof%M$N1r7$#`FG}+r_l_j^PQl#fZW6;l-MbVp5Spm(9$n*dJ diff --git a/src/contrib/rest/conf/keys/rest.jks b/src/contrib/rest/conf/keys/rest.jks deleted file mode 100644 index 539e8be7ba44e0dc3cb39deb822280a4fbde67af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1363 zcmezO_TO6u1_mY|W&~r_+{*0KN+3_m@oVinpv*3VCZ=r$d~96WY>X_7T1J%57Nhc{i> zJWVv!CM&G;pKJ1F`Eydn8DX>4x8`(zSaIhqXF2aCF+QER+=YLS?SH#k?)!w(4c&fo zKk7Exu6e?c<~~{0x^8-+)!C|#nx4Hzp%*`A+gLFCy?gR@u13_fpKF5B^E}t?QD%LX z5p!;a2&0!zztzrfJQuE4uuNJz`+_ZBGXGRVy~N3x4_sq@ysP*sD<$b(+QLw~VQJf^ zd%r)51}K^S)hT4q6me+0D9Pv@6J+`QNqwBGgq^QW4wu%!!n^-UrdS<5b^R%C&&%0P z4eY^tdmqbnE^cj!_&3>X`nC5{w^9e zOV{?oh78_@+vR1aT0TYTHiXuHXY6O4zsqcjWq|M#uBH@?gkBxv?ea?9mlr9w-cks; z<5yI3Pe;MtdVc*b<0GF$T{e}uME}mUG`jcx%f88*G<9O4!+j;~a@{pL%g+A(@Jsf@ z=Dq*Q*%@A0*#j|HF%N9H1UsSl@e|w@_=G=0DQ{9(pYLv@+i)F7E zot$-blI}ET?>>)`3LA&Fys;T}5$&;VX zoqQ30pgLpU^H+)C8_%s;vC4ev#pda=)_ZVdtqgBtnY6HBD);ulD=Ujc=gm9guO7C` zI#kts=@Fg01SN4H%hnr74-PjUoAOCKvE6O*RIl|H+AnAQ?t&!f2t88+OJG6{1}0>G zgC@ou3z(T0nV497qUQ1%@Un4gwRyCC=LM!}RtAF{Ljhn?XAWgy6J`(1%g)O$&x46@ zV2H3|h;U(uaASxt!$l0_#CZ)33@i*x4Gj%Vj0~g1d5ujCjE&5pTspg^aXxZr05dsr zV=se2V<%H%BSYWbGS>f3PJXvm4%r-kd}HQ?l{*@lYdP%KXaEoN*khN ze+YWI`; z11mY-+!bZpKk1GMmr3`n#2w6gv+_ab<1?{)@-|1UO&Uzt2^?V`hx>o~?GG?UC!1 zEZ6iZIv5CUjxayK`25YYdr9R_ivm4AKHV<1dRh-#_&ZUf9R~ND9?75o@hPEFdt$!T zolof%M$N1r7$#`FG}+r_l_j^PQl#fZW6;l-MbVp5S#Ql>`)8x9kFnIL9nVyf=6eU7 H+RO<6^fNHf diff --git a/src/contrib/rest/conf/log4j.properties b/src/contrib/rest/conf/log4j.properties deleted file mode 100644 index c294b3d4eca..00000000000 --- a/src/contrib/rest/conf/log4j.properties +++ /dev/null @@ -1,72 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# - -# -# ZooKeeper Logging Configuration -# - -# Format is " (, )+ - -# DEFAULT: console appender only -log4j.rootLogger=INFO, CONSOLE - -# Example with rolling log file -#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE - -# Example with rolling log file and tracing -#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE - -# -# Log INFO level and above messages to the console -# -log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.Threshold=INFO -log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n - -# -# Add ROLLINGFILE to rootLogger to get log file output -# Log DEBUG level and above messages to a log file -log4j.appender.ROLLINGFILE=org.apache.log4j.ConsoleAppender -log4j.appender.ROLLINGFILE.Threshold=DEBUG -log4j.appender.ROLLINGFILE.File=bookkeeper.log -log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout -log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n - -# Max log file size of 10MB -log4j.appender.ROLLINGFILE.MaxFileSize=10MB -# uncomment the next line to limit number of backup files -#log4j.appender.ROLLINGFILE.MaxBackupIndex=10 - -log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout -log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n - - -# -# Add TRACEFILE to rootLogger to get log file output -# Log DEBUG level and above messages to a log file -log4j.appender.TRACEFILE=org.apache.log4j.FileAppender -log4j.appender.TRACEFILE.Threshold=TRACE -log4j.appender.TRACEFILE.File=bookkeeper_trace.log - -log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout -### Notice we are including log4j's NDC here (%x) -log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n diff --git a/src/contrib/rest/conf/rest.properties b/src/contrib/rest/conf/rest.properties deleted file mode 100644 index f0abb4541fa..00000000000 --- a/src/contrib/rest/conf/rest.properties +++ /dev/null @@ -1,70 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# - -# -# ZooKeeper REST Gateway Configuration file -# - -rest.port = 9998 - -# -# Endpoint definition -# - -# plain configuration ; -rest.endpoint.1 = /;localhost:2181,localhost:2182 - -# ... or chrooted to /zookeeper -# rest.endpoint.1 = /;localhost:2181,localhost:2182/zookeeper - -# HTTP Basic authentication for this endpoint -# rest.endpoint.1.http.auth = root:root1 - -# create -e /a data digest:'demo:ojnHEyje6F33LLzGVzg+yatf4Fc=':cdrwa -# any session on this endpoint will use authentication -# rest.endpoint.1.zk.digest = demo:test - -# you can easily generate the ACL using Python: -# import sha; sha.sha('demo:test').digest().encode('base64').strip() - -# -# ... you can define as many endpoints as you wish -# - -# rest.endpoint.2 = /restricted;localhost:2181 -# rest.endpoint.2.http.auth = admin:pass - -# rest.endpoint.3 = /cluster1;localhost:2181,localhost:2182 -# ** you should configure one end-point for each ZooKeeper cluster -# etc. - -# Global HTTP Basic Authentication -# You should also enable HTTPS-only access -# The authentication credentials are sent as plain text - -# rest.http.auth = guest:guest1 - -# Uncomment the lines bellow to allow https-only access - -# rest.ssl = true -# rest.ssl.jks = keys/rest.jks -# rest.ssl.jks.pass = 123456 - \ No newline at end of file diff --git a/src/contrib/rest/ivy.xml b/src/contrib/rest/ivy.xml deleted file mode 100644 index 7982fa23d38..00000000000 --- a/src/contrib/rest/ivy.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - ZooKeeper REST - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/rest/rest.sh b/src/contrib/rest/rest.sh deleted file mode 100644 index daa819836e2..00000000000 --- a/src/contrib/rest/rest.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/sh - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -# If this scripted is run out of /usr/bin or some other system bin directory -# it should be linked to and not copied. Things like java jar files are found -# relative to the canonical path of this script. -# - -# Only follow symlinks if readlink supports it -if readlink -f "$0" > /dev/null 2>&1 -then - ZKREST=`readlink -f "$0"` -else - ZKREST="$0" -fi -ZKREST_HOME=`dirname "$ZKREST"` - -if $cygwin -then - # cygwin has a "kill" in the shell itself, gets confused - KILL=/bin/kill -else - KILL=kill -fi - -if [ -z $ZKREST_PIDFILE ] - then ZKREST_PIDFILE=$ZKREST_HOME/server.pid -fi - -ZKREST_MAIN=org.apache.zookeeper.server.jersey.RestMain - -ZKREST_CONF=$ZKREST_HOME/conf -ZKREST_LOG=$ZKREST_HOME/zkrest.log - -CLASSPATH="$ZKREST_CONF:$CLASSPATH" - -for i in "$ZKREST_HOME"/lib/*.jar -do - CLASSPATH="$i:$CLASSPATH" -done - -for i in "$ZKREST_HOME"/zookeeper-*.jar -do - CLASSPATH="$i:$CLASSPATH" -done - -case $1 in -start) - echo "Starting ZooKeeper REST Gateway ... " - java -cp "$CLASSPATH" $JVMFLAGS $ZKREST_MAIN >$ZKREST_LOG 2>&1 & - /bin/echo -n $! > "$ZKREST_PIDFILE" - echo STARTED - ;; -stop) - echo "Stopping ZooKeeper REST Gateway ... " - if [ ! -f "$ZKREST_PIDFILE" ] - then - echo "error: could not find file $ZKREST_PIDFILE" - exit 1 - else - $KILL -9 $(cat "$ZKREST_PIDFILE") - rm "$ZKREST_PIDFILE" - echo STOPPED - fi - ;; -restart) - shift - "$0" stop ${@} - sleep 3 - "$0" start ${@} - ;; -*) - echo "Usage: $0 {start|stop|restart}" >&2 - -esac diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/RestMain.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/RestMain.java deleted file mode 100644 index 7ebf595529c..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/RestMain.java +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.server.jersey.cfg.Credentials; -import org.apache.zookeeper.server.jersey.cfg.Endpoint; -import org.apache.zookeeper.server.jersey.cfg.RestCfg; -import org.apache.zookeeper.server.jersey.filters.HTTPBasicAuth; - -import com.sun.grizzly.SSLConfig; -import com.sun.grizzly.http.embed.GrizzlyWebServer; -import com.sun.grizzly.http.servlet.ServletAdapter; -import com.sun.jersey.spi.container.servlet.ServletContainer; - -/** - * Demonstration of how to run the REST service using Grizzly - */ -public class RestMain { - - private static Logger LOG = Logger.getLogger(RestMain.class); - - private GrizzlyWebServer gws; - private RestCfg cfg; - - public RestMain(RestCfg cfg) { - this.cfg = cfg; - } - - public void start() throws IOException { - System.out.println("Starting grizzly ..."); - - boolean useSSL = cfg.useSSL(); - gws = new GrizzlyWebServer(cfg.getPort(), "/tmp/23cxv45345/2131xc2/", useSSL); - // BUG: Grizzly needs a doc root if you are going to register multiple adapters - - for (Endpoint e : cfg.getEndpoints()) { - ZooKeeperService.mapContext(e.getContext(), e); - gws.addGrizzlyAdapter(createJerseyAdapter(e), new String[] { e - .getContext() }); - } - - if (useSSL) { - System.out.println("Starting SSL ..."); - String jks = cfg.getJKS("keys/rest.jks"); - String jksPassword = cfg.getJKSPassword(); - - SSLConfig sslConfig = new SSLConfig(); - URL resource = getClass().getClassLoader().getResource(jks); - if (resource == null) { - LOG.error("Unable to find the keystore file: " + jks); - System.exit(2); - } - try { - sslConfig.setKeyStoreFile(new File(resource.toURI()) - .getAbsolutePath()); - } catch (URISyntaxException e1) { - LOG.error("Unable to load keystore: " + jks, e1); - System.exit(2); - } - sslConfig.setKeyStorePass(jksPassword); - gws.setSSLConfig(sslConfig); - } - - gws.start(); - } - - public void stop() { - gws.stop(); - ZooKeeperService.closeAll(); - } - - private ServletAdapter createJerseyAdapter(Endpoint e) { - ServletAdapter jersey = new ServletAdapter(); - - jersey.setServletInstance(new ServletContainer()); - jersey.addInitParameter("com.sun.jersey.config.property.packages", - "org.apache.zookeeper.server.jersey.resources"); - jersey.setContextPath(e.getContext()); - - Credentials c = Credentials.join(e.getCredentials(), cfg - .getCredentials()); - if (!c.isEmpty()) { - jersey.addFilter(new HTTPBasicAuth(c), e.getContext() - + "-basic-auth", null); - } - - return jersey; - } - - /** - * The entry point for starting the server - * - */ - public static void main(String[] args) throws Exception { - RestCfg cfg = new RestCfg("rest.properties"); - - final RestMain main = new RestMain(cfg); - main.start(); - - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - main.stop(); - System.out.println("Got exit request. Bye."); - } - }); - - printEndpoints(cfg); - System.out.println("Server started."); - } - - private static void printEndpoints(RestCfg cfg) { - int port = cfg.getPort(); - - for (Endpoint e : cfg.getEndpoints()) { - - String context = e.getContext(); - if (context.charAt(context.length() - 1) != '/') { - context += "/"; - } - - System.out.println(String.format( - "Started %s - WADL: http://localhost:%d%sapplication.wadl", - context, port, context)); - } - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java deleted file mode 100644 index b1f0a7c45f3..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; -import java.util.TreeSet; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.Watcher.Event.KeeperState; -import org.apache.zookeeper.server.jersey.cfg.Endpoint; - -/** - * Singleton which provides JAX-RS resources access to the ZooKeeper client. - * There's a single session for each base uri (so usually just one). - */ -public class ZooKeeperService { - - private static Logger LOG = Logger.getLogger(ZooKeeperService.class); - - /** Map base uri to ZooKeeper host:port parameters */ - private static Map contextMap = new HashMap(); - - /** Map base uri to ZooKeeper session */ - private static Map zkMap = new HashMap(); - - /** Session timers */ - private static Map zkSessionTimers = new HashMap(); - private static Timer timer = new Timer(); - - /** Track the status of the ZooKeeper session */ - private static class MyWatcher implements Watcher { - final String contextPath; - - /** Separate watcher for each base uri */ - public MyWatcher(String contextPath) { - this.contextPath = contextPath; - } - - /** - * Track state - in particular watch for expiration. if it happens for - * re-creation of the ZK client session - */ - synchronized public void process(WatchedEvent event) { - if (event.getState() == KeeperState.Expired) { - close(contextPath); - } - } - } - - /** ZooKeeper session timer */ - private static class SessionTimerTask extends TimerTask { - - private int delay; - private String contextPath, session; - private Timer timer; - - public SessionTimerTask(int delayInSeconds, String session, - String contextPath, Timer timer) { - delay = delayInSeconds * 1000; // convert to milliseconds - this.contextPath = contextPath; - this.session = session; - this.timer = timer; - reset(); - } - - public SessionTimerTask(SessionTimerTask t) { - this(t.delay / 1000, t.session, t.contextPath, t.timer); - } - - @Override - public void run() { - if (LOG.isInfoEnabled()) { - LOG.info(String.format("Session '%s' expired after " - + "'%d' milliseconds.", session, delay)); - } - ZooKeeperService.close(contextPath, session); - } - - public void reset() { - timer.schedule(this, delay); - } - - } - - /** - * Specify ZooKeeper host:port for a particular context path. The host:port - * string is passed to the ZK client, so this can be formatted with more - * than a single host:port pair. - */ - synchronized public static void mapContext(String contextPath, Endpoint e) { - contextMap.put(contextPath, e); - } - - /** - * Reset timer for a session - */ - synchronized public static void resetTimer(String contextPath, - String session) { - if (session != null) { - String uri = concat(contextPath, session); - - SessionTimerTask t = zkSessionTimers.remove(uri); - t.cancel(); - - zkSessionTimers.put(uri, new SessionTimerTask(t)); - } - } - - /** - * Close the ZooKeeper session and remove it from the internal maps - */ - public static void close(String contextPath) { - close(contextPath, null); - } - - /** - * Close the ZooKeeper session and remove it - */ - synchronized public static void close(String contextPath, String session) { - String uri = concat(contextPath, session); - - TimerTask t = zkSessionTimers.remove(uri); - if (t != null) { - t.cancel(); - } - - ZooKeeper zk = zkMap.remove(uri); - if (zk == null) { - return; - } - try { - zk.close(); - } catch (InterruptedException e) { - LOG.error("Interrupted while closing ZooKeeper connection.", e); - } - } - - /** - * Close all the ZooKeeper sessions and remove them from the internal maps - */ - synchronized public static void closeAll() { - Set sessions = new TreeSet(zkMap.keySet()); - for (String key : sessions) { - close(key); - } - } - - /** - * Is there an active connection for this session? - */ - synchronized public static boolean isConnected(String contextPath, - String session) { - return zkMap.containsKey(concat(contextPath, session)); - } - - /** - * Return a ZooKeeper client not tied to a specific session. - */ - public static ZooKeeper getClient(String contextPath) throws IOException { - return getClient(contextPath, null); - } - - /** - * Return a ZooKeeper client for a session with a default expire time - * - * @throws IOException - */ - public static ZooKeeper getClient(String contextPath, String session) - throws IOException { - return getClient(contextPath, session, 5); - } - - /** - * Return a ZooKeeper client which may or may not be connected, but it will - * not be expired. This method can be called multiple times, the same object - * will be returned except in the case where the session expires (at which - * point a new session will be returned) - */ - synchronized public static ZooKeeper getClient(String contextPath, - String session, int expireTime) throws IOException { - final String connectionId = concat(contextPath, session); - - ZooKeeper zk = zkMap.get(connectionId); - if (zk == null) { - - if (LOG.isInfoEnabled()) { - LOG.info(String.format("creating new " - + "connection for : '%s'", connectionId)); - } - Endpoint e = contextMap.get(contextPath); - zk = new ZooKeeper(e.getHostPort(), 30000, new MyWatcher( - connectionId)); - - for (Map.Entry p : e.getZooKeeperAuthInfo().entrySet()) { - zk.addAuthInfo("digest", String.format("%s:%s", p.getKey(), - p.getValue()).getBytes()); - } - - zkMap.put(connectionId, zk); - - // a session should automatically expire after an amount of time - if (session != null) { - zkSessionTimers.put(connectionId, new SessionTimerTask( - expireTime, session, contextPath, timer)); - } - } - return zk; - } - - private static String concat(String contextPath, String session) { - if (session != null) { - return String.format("%s@%s", contextPath, session); - } - return contextPath; - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java deleted file mode 100644 index 0730be57cc6..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.cfg; - -import java.util.HashMap; - -public class Credentials extends HashMap { - - public static Credentials join(Credentials a, Credentials b) { - Credentials result = new Credentials(); - result.putAll(a); - result.putAll(b); - return result; - } - - public Credentials() { - super(); - } - - public Credentials(String credentials) { - super(); - - if (!credentials.trim().equals("")) { - String[] parts = credentials.split(","); - for(String p : parts) { - String[] userPass = p.split(":"); - put(userPass[0], userPass[1]); - } - } - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java deleted file mode 100644 index 2a6278250e8..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.cfg; - -public class Endpoint { - - private String context; - private HostPortSet hostPort; - private Credentials credentials; - private Credentials zookeeperAuth; - - public Endpoint(String context, String hostPortList) { - this.context = context; - this.hostPort = new HostPortSet(hostPortList); - } - - public String getContext() { - return context; - } - - public String getHostPort() { - return hostPort.toString(); - } - - public Credentials getCredentials() { - return credentials; - } - - public void setCredentials(String c) { - this.credentials = new Credentials(c); - } - - public void setZooKeeperAuthInfo(String digest) { - zookeeperAuth = new Credentials(digest); - } - - public final Credentials getZooKeeperAuthInfo() { - return zookeeperAuth; - } - - @Override - public boolean equals(Object o) { - Endpoint e = (Endpoint) o; - return context.equals(e.context); - } - - @Override - public int hashCode() { - return context.hashCode(); - } - - @Override - public String toString() { - return String.format("", context, hostPort.toString()); - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java deleted file mode 100644 index 51a1bdd2ceb..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.cfg; - -public class HostPort { - - private String host; - private int port; - - public HostPort(String hostPort) { - String[] parts = hostPort.split(":"); - host = parts[0]; - port = Integer.parseInt(parts[1]); - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - @Override - public boolean equals(Object o) { - HostPort p = (HostPort) o; - return host.equals(p.host) && port == p.port; - } - - @Override - public int hashCode() { - return String.format("%s:%d", host, port).hashCode(); - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java deleted file mode 100644 index 301a5656c39..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.cfg; - -import java.util.HashSet; -import java.util.Set; - -public class HostPortSet { - - private Set hostPortSet = new HashSet(); - private String original; - - public HostPortSet(String hostPortList) { - original = hostPortList; - - int chrootStart = hostPortList.indexOf('/'); - String hostPortPairs; - if (chrootStart != -1) { - hostPortPairs = hostPortList.substring(0, chrootStart); - } else { - hostPortPairs = hostPortList; - } - - String[] parts = hostPortPairs.split(","); - for(String p : parts) { - hostPortSet.add(new HostPort(p)); - } - } - - @Override - public String toString() { - return original; - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java deleted file mode 100644 index c7730201d4c..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.cfg; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Properties; -import java.util.Set; - -public class RestCfg { - - private Properties cfg = new Properties(); - - private Set endpoints = new HashSet(); - private Credentials credentials = new Credentials(); - - public RestCfg(String resource) throws IOException { - this(RestCfg.class.getClassLoader().getResourceAsStream(resource)); - } - - public RestCfg(InputStream io) throws IOException { - cfg.load(io); - extractEndpoints(); - extractCredentials(); - } - - private void extractCredentials() { - if (cfg.containsKey("rest.http.auth")) { - credentials = new Credentials(cfg.getProperty("rest.http.auth", "")); - } - } - - private void extractEndpoints() { - int count = 1; - while (true) { - String e = cfg.getProperty( - String.format("rest.endpoint.%d", count), null); - if (e == null) { - break; - } - - String[] parts = e.split(";"); - if (parts.length != 2) { - count++; - continue; - } - Endpoint point = new Endpoint(parts[0], parts[1]); - - String c = cfg.getProperty(String.format( - "rest.endpoint.%d.http.auth", count), ""); - point.setCredentials(c); - - String digest = cfg.getProperty(String.format( - "rest.endpoint.%d.zk.digest", count), ""); - point.setZooKeeperAuthInfo(digest); - - endpoints.add(point); - count++; - } - } - - public int getPort() { - return Integer.parseInt(cfg.getProperty("rest.port", "9998")); - } - - public boolean useSSL() { - return Boolean.valueOf(cfg.getProperty("rest.ssl", "false")); - } - - public final Set getEndpoints() { - return endpoints; - } - - public final Credentials getCredentials() { - return credentials; - } - - public String getJKS() { - return cfg.getProperty("rest.ssl.jks"); - } - - public String getJKS(String def) { - return cfg.getProperty("rest.ssl.jks", def); - } - - public String getJKSPassword() { - return cfg.getProperty("rest.ssl.jks.pass"); - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java deleted file mode 100644 index 49640b530c2..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.filters; - -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.zookeeper.server.jersey.cfg.Credentials; - -import com.sun.jersey.core.util.Base64; - -public class HTTPBasicAuth implements Filter { - - private Credentials credentials; - - public HTTPBasicAuth(Credentials c) { - credentials = c; - } - - @Override - public void doFilter(ServletRequest req0, ServletResponse resp0, - FilterChain chain) throws IOException, ServletException { - - HttpServletRequest request = (HttpServletRequest) req0; - HttpServletResponse response = (HttpServletResponse) resp0; - - String authorization = request.getHeader("Authorization"); - if (authorization != null) { - String c[] = parseAuthorization(authorization); - if (c != null && credentials.containsKey(c[0]) - && credentials.get(c[0]).equals(c[1])) { - chain.doFilter(request, response); - return; - } - } - - response.setHeader("WWW-Authenticate", "Basic realm=\"Restricted\""); - response.sendError(401); - } - - private String[] parseAuthorization(String authorization) { - String parts[] = authorization.split(" "); - if (parts.length == 2 && parts[0].equalsIgnoreCase("Basic")) { - String userPass = Base64.base64Decode(parts[1]); - - int p = userPass.indexOf(":"); - if (p != -1) { - return new String[] { userPass.substring(0, p), - userPass.substring(p + 1) }; - } - } - return null; - } - - @Override - public void init(FilterConfig arg0) throws ServletException { - } - - @Override - public void destroy() { - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java deleted file mode 100644 index b3fad554437..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.jaxb; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; -import javax.xml.bind.annotation.XmlRootElement; - - -/** - * Represents the CHILD using JAXB. - * Special JSON version is required to get proper formatting in both - * JSON and XML output. See details in ZNodeResource. - */ -@XmlRootElement(name="child") -public class ZChildren { - public String path; - public String uri; - - public String child_uri_template; - @XmlElementWrapper(name="children") - @XmlElement(name="child") - public List children; - - public ZChildren() { - // needed by jersey - children = new ArrayList(); - } - - public ZChildren(String path, String uri, String child_uri_template, - List children) - { - this.path = path; - this.uri = uri; - this.child_uri_template = child_uri_template; - if (children != null) { - this.children = children; - } else { - this.children = new ArrayList(); - } - } - - @Override - public int hashCode() { - return path.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ZChildren)) { - return false; - } - ZChildren o = (ZChildren) obj; - return path.equals(o.path) && children.equals(o.children); - } - - @Override - public String toString() { - return "ZChildren(" + path + "," + children + ")"; - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java deleted file mode 100644 index 0dcece07ef4..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.jaxb; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.annotation.XmlRootElement; - - -/** - * Represents the CHILD using JAXB. - * Special JSON version is required to get proper formatting in both - * JSON and XML output. See details in ZNodeResource. - */ -@XmlRootElement(name="child") -public class ZChildrenJSON { - public String path; - public String uri; - - public String child_uri_template; - public List children; - - public ZChildrenJSON() { - // needed by jersey - children = new ArrayList(); - } - - public ZChildrenJSON(String path, String uri, String child_uri_template, - List children) - { - this.path = path; - this.uri = uri; - this.child_uri_template = child_uri_template; - if (children != null) { - this.children = children; - } else { - this.children = new ArrayList(); - } - } - - @Override - public int hashCode() { - return path.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ZChildrenJSON)) { - return false; - } - ZChildrenJSON o = (ZChildrenJSON) obj; - return path.equals(o.path) && children.equals(o.children); - } - - @Override - public String toString() { - return "ZChildrenJSON(" + path + "," + children + ")"; - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java deleted file mode 100644 index e976ee0e3b0..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.jaxb; - -import javax.xml.bind.annotation.XmlRootElement; - - -/** - * Represents an ERROR using JAXB. - */ -@XmlRootElement(name="error") -public class ZError { - public String request; - public String message; - - public ZError(){ - // needed by jersey - } - - public ZError(String request, String message) { - this.request = request; - this.message = message; - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java deleted file mode 100644 index 4d83717803c..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.jaxb; - -import javax.xml.bind.annotation.XmlRootElement; - - -/** - * Represents a PATH using JAXB. - */ -@XmlRootElement(name="path") -public class ZPath { - public String path; - public String uri; - - public ZPath(){ - // needed by jersey - } - - public ZPath(String path) { - this(path, null); - } - - public ZPath(String path, String uri) { - this.path = path; - this.uri = uri; - } - - @Override - public int hashCode() { - return path.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ZPath)) { - return false; - } - ZPath o = (ZPath) obj; - return path.equals(o.path); - } - - @Override - public String toString() { - return "ZPath(" + path + ")"; - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java deleted file mode 100644 index 06ca9e57b16..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.jaxb; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name="session") -public class ZSession { - public String id; - public String uri; - - public ZSession() { - // needed by jersey - } - - public ZSession(String id, String uri) { - this.id = id; - this.uri = uri; - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if(!(obj instanceof ZSession)) { - return false; - } - ZSession s = (ZSession) obj; - return id.equals(s.id); - } - - @Override - public String toString() { - return "ZSession(" + id +")"; - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java deleted file mode 100644 index af70d18262b..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.jaxb; - -import javax.xml.bind.annotation.XmlRootElement; - - -/** - * Represents a STAT using JAXB. - */ -@XmlRootElement(name="stat") -public class ZStat { - public String path; - public String uri; - public byte[] data64; - public String dataUtf8; - - public long czxid; - public long mzxid; - public long ctime; - public long mtime; - public int version; - public int cversion; - public int aversion; - public long ephemeralOwner; - public int dataLength; - public int numChildren; - public long pzxid; - - - public ZStat(){ - // needed by jersey - } - - public ZStat(String path, byte[] data64, String dataUtf8) - { - this.path = path; - this.data64 = data64; - this.dataUtf8 = dataUtf8; - } - - public ZStat(String path, String uri, byte[] data64, String dataUtf8, - long czxid, long mzxid, long ctime, long mtime, int version, - int cversion, int aversion, long ephemeralOwner, int dataLength, - int numChildren, long pzxid) - { - this.path = path; - this.uri = uri; - this.data64 = data64; - this.dataUtf8 = dataUtf8; - - this.czxid = czxid; - this.mzxid = mzxid; - this.ctime = ctime; - this.mtime = mtime; - this.version = version; - this.cversion = cversion; - this.aversion = aversion; - this.ephemeralOwner = ephemeralOwner; - this.dataLength = dataLength; - this.numChildren = numChildren; - this.pzxid = pzxid; - } - - @Override - public int hashCode() { - return path.hashCode(); - } - - /** - * This method considers two ZStats equal if their path, encoding, and - * data match. It does not compare the ZooKeeper - * org.apache.zookeeper.data.Stat class fields. - */ - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ZStat)) { - return false; - } - ZStat o = (ZStat) obj; - return toString().equals(o.toString()); - } - - @Override - public String toString() { - return "ZStat(" + path + "," + "b64[" - + (data64 == null ? null : new String(data64)) + "]," - + dataUtf8 + ")"; - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java deleted file mode 100644 index 08935868fef..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.resources; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import javax.ws.rs.ext.ContextResolver; -import javax.ws.rs.ext.Provider; -import javax.xml.bind.JAXBContext; - -import org.apache.zookeeper.server.jersey.jaxb.ZChildrenJSON; -import org.apache.zookeeper.server.jersey.jaxb.ZPath; -import org.apache.zookeeper.server.jersey.jaxb.ZStat; - -import com.sun.jersey.api.json.JSONConfiguration; -import com.sun.jersey.api.json.JSONJAXBContext; - -/** - * Tell Jersey how to resolve JSON formatting. Specifically detail the - * fields which are arrays and which are numbers (not strings). - */ -@Provider -@SuppressWarnings("unchecked") -public final class JAXBContextResolver implements ContextResolver { - private final JAXBContext context; - - private final Set typesSet; - - public JAXBContextResolver() throws Exception { - Class[] typesArr = - new Class[]{ZPath.class, ZStat.class, ZChildrenJSON.class}; - typesSet = new HashSet(Arrays.asList(typesArr)); - context = new JSONJAXBContext( - JSONConfiguration.mapped() - .arrays("children") - .nonStrings("czxid") - .nonStrings("mzxid") - .nonStrings("ctime") - .nonStrings("mtime") - .nonStrings("version") - .nonStrings("cversion") - .nonStrings("aversion") - .nonStrings("ephemeralOwner") - .nonStrings("dataLength") - .nonStrings("numChildren") - .nonStrings("pzxid") - .build(), - typesArr); - } - - public JAXBContext getContext(Class objectType) { - return (typesSet.contains(objectType)) ? context : null; - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java deleted file mode 100644 index fdfc27b5c5d..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.resources; - -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.server.jersey.jaxb.ZError; - - -/** - * Map KeeperException to HTTP status codes - */ -@Provider -public class KeeperExceptionMapper implements ExceptionMapper { - private UriInfo ui; - - public KeeperExceptionMapper(@Context UriInfo ui) { - this.ui = ui; - } - - public Response toResponse(KeeperException e) { - Response.Status status; - String message; - - String path = e.getPath(); - - switch(e.code()) { - case AUTHFAILED: - status = Response.Status.UNAUTHORIZED; - message = path + " not authorized"; - break; - case BADARGUMENTS: - status = Response.Status.BAD_REQUEST; - message = path + " bad arguments"; - break; - case BADVERSION: - status = Response.Status.PRECONDITION_FAILED; - message = path + " bad version"; - break; - case INVALIDACL: - status = Response.Status.BAD_REQUEST; - message = path + " invalid acl"; - break; - case NODEEXISTS: - status = Response.Status.CONFLICT; - message = path + " already exists"; - break; - case NONODE: - status = Response.Status.NOT_FOUND; - message = path + " not found"; - break; - case NOTEMPTY: - status = Response.Status.CONFLICT; - message = path + " not empty"; - break; - default: - status = Response.Status.fromStatusCode(502); // bad gateway - message = "Error processing request for " + path - + " : " + e.getMessage(); - } - - return Response.status(status).entity( - new ZError(ui.getRequestUri().toString(), message)).build(); - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java deleted file mode 100644 index 46f33bb4321..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.resources; - -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -import org.apache.zookeeper.server.jersey.jaxb.ZError; - -/** - * Map RuntimeException to HTTP status codes - */ -@Provider -public class RuntimeExceptionMapper - implements ExceptionMapper -{ - private UriInfo ui; - - public RuntimeExceptionMapper(@Context UriInfo ui) { - this.ui = ui; - } - - public Response toResponse(RuntimeException e) { - // don't try to handle jersey exceptions ourselves - if (e instanceof WebApplicationException) { - WebApplicationException ie =(WebApplicationException) e; - return ie.getResponse(); - } - - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( - new ZError(ui.getRequestUri().toString(), - "Error processing request due to " + e - )).build(); - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java deleted file mode 100644 index 62b0e7fa2e1..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.resources; - -import java.io.IOException; -import java.net.URI; -import java.util.UUID; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.server.jersey.ZooKeeperService; -import org.apache.zookeeper.server.jersey.jaxb.ZError; -import org.apache.zookeeper.server.jersey.jaxb.ZSession; - -import com.sun.jersey.api.json.JSONWithPadding; - -@Path("sessions/v1/{session: .*}") -public class SessionsResource { - - private static Logger LOG = Logger.getLogger(SessionsResource.class); - - private String contextPath; - - public SessionsResource(@Context HttpServletRequest request) { - contextPath = request.getContextPath(); - if (contextPath.equals("")) { - contextPath = "/"; - } - } - - @PUT - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML }) - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - public Response keepAliveSession(@PathParam("session") String session, - @Context UriInfo ui, byte[] data) { - - if (!ZooKeeperService.isConnected(contextPath, session)) { - throwNotFound(session, ui); - } - - ZooKeeperService.resetTimer(contextPath, session); - return Response.status(Response.Status.OK).build(); - } - - @POST - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML }) - public Response createSession(@QueryParam("op") String op, - @DefaultValue("5") @QueryParam("expire") String expire, - @Context UriInfo ui) { - if (!op.equals("create")) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).entity( - new ZError(ui.getRequestUri().toString(), "")).build()); - } - - int expireInSeconds; - try { - expireInSeconds = Integer.parseInt(expire); - } catch (NumberFormatException e) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).build()); - } - - String uuid = UUID.randomUUID().toString(); - while (ZooKeeperService.isConnected(contextPath, uuid)) { - uuid = UUID.randomUUID().toString(); - } - - // establish the connection to the ZooKeeper cluster - try { - ZooKeeperService.getClient(contextPath, uuid, expireInSeconds); - } catch (IOException e) { - LOG.error("Failed while trying to create a new session", e); - - throw new WebApplicationException(Response.status( - Response.Status.INTERNAL_SERVER_ERROR).build()); - } - - URI uri = ui.getAbsolutePathBuilder().path(uuid).build(); - return Response.created(uri).entity( - new JSONWithPadding(new ZSession(uuid, uri.toString()))) - .build(); - } - - @DELETE - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML, MediaType.APPLICATION_OCTET_STREAM }) - public void deleteSession(@PathParam("session") String session, - @Context UriInfo ui) { - ZooKeeperService.close(contextPath, session); - } - - private static void throwNotFound(String session, UriInfo ui) - throws WebApplicationException { - throw new WebApplicationException(Response.status( - Response.Status.NOT_FOUND).entity( - new ZError(ui.getRequestUri().toString(), session - + " not found")).build()); - } - -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java deleted file mode 100644 index 706ab89f1e4..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.resources; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; - -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.Provider; - -import org.apache.zookeeper.server.jersey.jaxb.ZError; - -/** - * Tell Jersey how to format an octet response error message. - */ -@Produces(MediaType.APPLICATION_OCTET_STREAM) -@Provider -public class ZErrorWriter implements MessageBodyWriter { - - public long getSize(ZError t, Class type, Type genericType, - Annotation[] annotations, MediaType mediaType) { - return -1; - } - - public boolean isWriteable(Class type, Type genericType, - Annotation[] annotations, MediaType mediaType) { - return ZError.class.isAssignableFrom(type); - } - - public void writeTo(ZError t, Class type, Type genericType, - Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, - OutputStream os) - throws IOException, WebApplicationException - { - PrintStream p = new PrintStream(os); - p.print("Request " + t.request + " failed due to " + t.message); - p.flush(); - } -} diff --git a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java b/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java deleted file mode 100644 index 77371eab53d..00000000000 --- a/src/contrib/rest/src/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey.resources; - -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.HEAD; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.server.jersey.ZooKeeperService; -import org.apache.zookeeper.server.jersey.jaxb.ZChildren; -import org.apache.zookeeper.server.jersey.jaxb.ZChildrenJSON; -import org.apache.zookeeper.server.jersey.jaxb.ZError; -import org.apache.zookeeper.server.jersey.jaxb.ZPath; -import org.apache.zookeeper.server.jersey.jaxb.ZStat; - -import com.sun.jersey.api.json.JSONWithPadding; - -/** - * Version 1 implementation of the ZooKeeper REST specification. - */ -// TODO test octet fully -@Path("znodes/v1{path: /.*}") -public class ZNodeResource { - private final ZooKeeper zk; - - public ZNodeResource(@DefaultValue("") @QueryParam("session") String session, - @Context UriInfo ui, - @Context HttpServletRequest request - ) - throws IOException { - - String contextPath = request.getContextPath(); - if (contextPath.equals("")) { - contextPath = "/"; - } - if (session.equals("")) { - session = null; - } else if (!ZooKeeperService.isConnected(contextPath, session)) { - throw new WebApplicationException(Response.status( - Response.Status.UNAUTHORIZED).build()); - } - zk = ZooKeeperService.getClient(contextPath, session); - } - - private void ensurePathNotNull(String path) { - if (path == null) { - throw new IllegalArgumentException("Invalid path \"" + path + "\""); - } - } - - @HEAD - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML }) - public Response existsZNode(@PathParam("path") String path, - @Context UriInfo ui) throws InterruptedException, KeeperException { - Stat stat = zk.exists(path, false); - if (stat == null) { - throwNotFound(path, ui); - } - return Response.status(Response.Status.OK).build(); - } - - @HEAD - @Produces( { MediaType.APPLICATION_OCTET_STREAM }) - public Response existsZNodeAsOctet(@PathParam("path") String path, - @Context UriInfo ui) throws InterruptedException, KeeperException { - Stat stat = zk.exists(path, false); - if (stat == null) { - throwNotFound(path, ui); - } - return Response.status(Response.Status.NO_CONTENT).build(); - } - - /* - * getZNodeList and getZNodeListJSON are bogus - but necessary. - * Unfortunately Jersey 1.0.3 is unable to render both xml and json properly - * in the case where a object contains a list/array. It's impossible to get - * it to render properly for both. As a result we need to split into two - * jaxb classes. - */ - - @GET - @Produces( { MediaType.APPLICATION_JSON, "application/javascript" }) - public Response getZNodeListJSON( - @PathParam("path") String path, - @QueryParam("callback") String callback, - @DefaultValue("data") @QueryParam("view") String view, - @DefaultValue("base64") @QueryParam("dataformat") String dataformat, - @Context UriInfo ui) throws InterruptedException, KeeperException { - return getZNodeList(true, path, callback, view, dataformat, ui); - } - - @GET - @Produces(MediaType.APPLICATION_XML) - public Response getZNodeList( - @PathParam("path") String path, - @QueryParam("callback") String callback, - @DefaultValue("data") @QueryParam("view") String view, - @DefaultValue("base64") @QueryParam("dataformat") String dataformat, - @Context UriInfo ui) throws InterruptedException, KeeperException { - return getZNodeList(false, path, callback, view, dataformat, ui); - } - - private Response getZNodeList(boolean json, String path, String callback, - String view, String dataformat, UriInfo ui) - throws InterruptedException, KeeperException { - ensurePathNotNull(path); - - if (view.equals("children")) { - List children = new ArrayList(); - for (String child : zk.getChildren(path, false)) { - children.add(child); - } - - Object child; - String childTemplate = ui.getAbsolutePath().toString(); - if (!childTemplate.endsWith("/")) { - childTemplate += "/"; - } - childTemplate += "{child}"; - if (json) { - child = new ZChildrenJSON(path, - ui.getAbsolutePath().toString(), childTemplate, - children); - } else { - child = new ZChildren(path, ui.getAbsolutePath().toString(), - childTemplate, children); - } - return Response.status(Response.Status.OK).entity( - new JSONWithPadding(child, callback)).build(); - } else { - Stat stat = new Stat(); - byte[] data = zk.getData(path, false, stat); - - byte[] data64; - String dataUtf8; - if (data == null) { - data64 = null; - dataUtf8 = null; - } else if (!dataformat.equals("utf8")) { - data64 = data; - dataUtf8 = null; - } else { - data64 = null; - dataUtf8 = new String(data); - } - ZStat zstat = new ZStat(path, ui.getAbsolutePath().toString(), - data64, dataUtf8, stat.getCzxid(), stat.getMzxid(), stat - .getCtime(), stat.getMtime(), stat.getVersion(), - stat.getCversion(), stat.getAversion(), stat - .getEphemeralOwner(), stat.getDataLength(), stat - .getNumChildren(), stat.getPzxid()); - - return Response.status(Response.Status.OK).entity( - new JSONWithPadding(zstat, callback)).build(); - } - } - - @GET - @Produces(MediaType.APPLICATION_OCTET_STREAM) - public Response getZNodeListAsOctet(@PathParam("path") String path) - throws InterruptedException, KeeperException { - ensurePathNotNull(path); - - Stat stat = new Stat(); - byte[] data = zk.getData(path, false, stat); - - if (data == null) { - return Response.status(Response.Status.NO_CONTENT).build(); - } else { - return Response.status(Response.Status.OK).entity(data).build(); - } - } - - @PUT - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML }) - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - public Response setZNode( - @PathParam("path") String path, - @QueryParam("callback") String callback, - @DefaultValue("-1") @QueryParam("version") String versionParam, - @DefaultValue("base64") @QueryParam("dataformat") String dataformat, - @DefaultValue("false") @QueryParam("null") String setNull, - @Context UriInfo ui, byte[] data) throws InterruptedException, - KeeperException { - ensurePathNotNull(path); - - int version; - try { - version = Integer.parseInt(versionParam); - } catch (NumberFormatException e) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).entity( - new ZError(ui.getRequestUri().toString(), path - + " bad version " + versionParam)).build()); - } - - if (setNull.equals("true")) { - data = null; - } - - Stat stat = zk.setData(path, data, version); - - ZStat zstat = new ZStat(path, ui.getAbsolutePath().toString(), null, - null, stat.getCzxid(), stat.getMzxid(), stat.getCtime(), stat - .getMtime(), stat.getVersion(), stat.getCversion(), - stat.getAversion(), stat.getEphemeralOwner(), stat - .getDataLength(), stat.getNumChildren(), stat - .getPzxid()); - - return Response.status(Response.Status.OK).entity( - new JSONWithPadding(zstat, callback)).build(); - } - - @PUT - @Produces(MediaType.APPLICATION_OCTET_STREAM) - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - public void setZNodeAsOctet(@PathParam("path") String path, - @DefaultValue("-1") @QueryParam("version") String versionParam, - @DefaultValue("false") @QueryParam("null") String setNull, - @Context UriInfo ui, byte[] data) throws InterruptedException, - KeeperException { - ensurePathNotNull(path); - - int version; - try { - version = Integer.parseInt(versionParam); - } catch (NumberFormatException e) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).entity( - new ZError(ui.getRequestUri().toString(), path - + " bad version " + versionParam)).build()); - } - - if (setNull.equals("true")) { - data = null; - } - - zk.setData(path, data, version); - } - - @POST - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML }) - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - public Response createZNode( - @PathParam("path") String path, - @QueryParam("callback") String callback, - @DefaultValue("create") @QueryParam("op") String op, - @QueryParam("name") String name, - @DefaultValue("base64") @QueryParam("dataformat") String dataformat, - @DefaultValue("false") @QueryParam("null") String setNull, - @DefaultValue("false") @QueryParam("sequence") String sequence, - @DefaultValue("false") @QueryParam("ephemeral") String ephemeral, - @Context UriInfo ui, byte[] data) throws InterruptedException, - KeeperException { - ensurePathNotNull(path); - - if (path.equals("/")) { - path += name; - } else { - path += "/" + name; - } - - if (!op.equals("create")) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).entity( - new ZError(ui.getRequestUri().toString(), path - + " bad operaton " + op)).build()); - } - - if (setNull.equals("true")) { - data = null; - } - - CreateMode createMode; - if (sequence.equals("true")) { - if (ephemeral.equals("false")) { - createMode = CreateMode.PERSISTENT_SEQUENTIAL; - } else { - createMode = CreateMode.EPHEMERAL_SEQUENTIAL; - } - } else if (ephemeral.equals("false")) { - createMode = CreateMode.PERSISTENT; - } else { - createMode = CreateMode.EPHEMERAL; - } - - String newPath = zk.create(path, data, Ids.OPEN_ACL_UNSAFE, createMode); - - URI uri = ui.getAbsolutePathBuilder().path(newPath).build(); - - return Response.created(uri).entity( - new JSONWithPadding(new ZPath(newPath, ui.getAbsolutePath() - .toString()))).build(); - } - - @POST - @Produces(MediaType.APPLICATION_OCTET_STREAM) - @Consumes(MediaType.APPLICATION_OCTET_STREAM) - public Response createZNodeAsOctet(@PathParam("path") String path, - @DefaultValue("create") @QueryParam("op") String op, - @QueryParam("name") String name, - @DefaultValue("false") @QueryParam("null") String setNull, - @DefaultValue("false") @QueryParam("sequence") String sequence, - @Context UriInfo ui, byte[] data) throws InterruptedException, - KeeperException { - ensurePathNotNull(path); - - if (path.equals("/")) { - path += name; - } else { - path += "/" + name; - } - - if (!op.equals("create")) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).entity( - new ZError(ui.getRequestUri().toString(), path - + " bad operaton " + op)).build()); - } - - if (setNull.equals("true")) { - data = null; - } - - CreateMode createMode; - if (sequence.equals("true")) { - createMode = CreateMode.PERSISTENT_SEQUENTIAL; - } else { - createMode = CreateMode.PERSISTENT; - } - - String newPath = zk.create(path, data, Ids.OPEN_ACL_UNSAFE, createMode); - - URI uri = ui.getAbsolutePathBuilder().path(newPath).build(); - - return Response.created(uri).entity( - new ZPath(newPath, ui.getAbsolutePath().toString())).build(); - } - - @DELETE - @Produces( { MediaType.APPLICATION_JSON, "application/javascript", - MediaType.APPLICATION_XML, MediaType.APPLICATION_OCTET_STREAM }) - public void deleteZNode(@PathParam("path") String path, - @DefaultValue("-1") @QueryParam("version") String versionParam, - @Context UriInfo ui) throws InterruptedException, KeeperException { - ensurePathNotNull(path); - - int version; - try { - version = Integer.parseInt(versionParam); - } catch (NumberFormatException e) { - throw new WebApplicationException(Response.status( - Response.Status.BAD_REQUEST).entity( - new ZError(ui.getRequestUri().toString(), path - + " bad version " + versionParam)).build()); - } - - zk.delete(path, version); - } - - private static void throwNotFound(String path, UriInfo ui) - throws WebApplicationException { - throw new WebApplicationException(Response.status( - Response.Status.NOT_FOUND).entity( - new ZError(ui.getRequestUri().toString(), path + " not found")) - .build()); - } - -} diff --git a/src/contrib/rest/src/python/README.txt b/src/contrib/rest/src/python/README.txt deleted file mode 100644 index acc8ffb8e3d..00000000000 --- a/src/contrib/rest/src/python/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -Some basic python scripts which use the REST interface: - -zkrest.py -- basic REST ZooKeeper client -demo_master_election.py -- shows how to implement master election -demo_queue.py -- basic queue -zk_dump_tree.py -- dumps the nodes & data of a znode hierarchy - -Generally these scripts require: - * simplejson diff --git a/src/contrib/rest/src/python/demo_master_election.py b/src/contrib/rest/src/python/demo_master_election.py deleted file mode 100644 index c0317c7fe86..00000000000 --- a/src/contrib/rest/src/python/demo_master_election.py +++ /dev/null @@ -1,90 +0,0 @@ -#! /usr/bin/env python - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import sys -import threading -import time - -from zkrest import ZooKeeper - -class Agent(threading.Thread): - """ A basic agent that wants to become a master and exit """ - - root = '/election' - - def __init__(self, id): - super(Agent, self).__init__() - self.zk = ZooKeeper() - self.id = id - - def run(self): - print 'Starting #%s' % self.id - with self.zk.session(expire=5): - - # signal agent presence - r = self.zk.create("%s/agent-" % self.root, - sequence=True, ephemeral=True) - self.me = r['path'] - - while True: - children = sorted([el['path'] \ - for el in self.zk.get_children(self.root)]) - master, previous = children[0], None - try: - index = children.index(self.me) - if index != 0: - previous = children[index-1] - except ValueError: - break - - if previous is None: - self.do_master_work() - # and don't forget to send heartbeat messages - break - else: - # do slave work in another thread - pass - - # wait for the previous agent or current master to exit / finish - while self.zk.exists(previous) or self.zk.exists(master): - time.sleep(0.5) - self.zk.heartbeat() - - # TODO signal the slave thread to exit and wait for it - # and rerun the election loop - - def do_master_work(self): - print "#%s: I'm the master: %s" % (self.id, self.me) - -def main(): - zk = ZooKeeper() - - # create the root node used for master election - if not zk.exists('/election'): - zk.create('/election') - - print 'Starting 10 agents ...' - agents = [Agent(id) for id in range(0,15)] - - map(Agent.start, agents) - map(Agent.join, agents) - - zk.delete('/election') - -if __name__ == '__main__': - sys.exit(main()) diff --git a/src/contrib/rest/src/python/demo_queue.py b/src/contrib/rest/src/python/demo_queue.py deleted file mode 100644 index 9ca4c6440cb..00000000000 --- a/src/contrib/rest/src/python/demo_queue.py +++ /dev/null @@ -1,99 +0,0 @@ -#! /usr/bin/env python - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# This is a simple message queue built on top of ZooKeeper. In order -# to be used in production it needs better error handling but it's -# still useful as a proof-of-concept. - -# Why use ZooKeeper as a queue? Highly available by design and has -# great performance. - -import sys -import threading -import time - -from zkrest import ZooKeeper - -class Queue(object): - def __init__(self, root, zk): - self.root = root - - self.zk = zk - - def put(self, data): - self.zk.create("%s/el-" % self.root, str(data), sequence=True, ephemeral=True) - - # creating ephemeral nodes for easy cleanup - # in a real world scenario you should create - # normal sequential znodes - - def fetch(self): - """ Pull an element from the queue - - This function is not blocking if the queue is empty, it will - just return None. - """ - children = sorted(self.zk.get_children(self.root), \ - lambda a, b: cmp(a['path'], b['path'])) - - if not children: - return None - - try: - first = children[0] - self.zk.delete(first['path'], version=first['version']) - if 'data64' not in first: - return '' - else: - return first['data64'].decode('base64') - - except (ZooKeeper.WrongVersion, ZooKeeper.NotFound): - # someone changed the znode between the get and delete - # this should not happen - # in practice you should retry the fetch - raise - - -def main(): - zk = ZooKeeper() - zk.start_session(expire=60) - - if not zk.exists('/queue'): - zk.create('/queue') - q = Queue('/queue', zk) - - print 'Pushing to queue 1 ... 5' - map(q.put, [1,2,3,4,5]) - - print 'Extracting ...' - while True: - el = q.fetch() - if el is None: - break - print el - - zk.close_session() - zk.delete('/queue') - - print 'Done.' - - -if __name__ == '__main__': - sys.exit(main()) - diff --git a/src/contrib/rest/src/python/test.py b/src/contrib/rest/src/python/test.py deleted file mode 100644 index 363747a6431..00000000000 --- a/src/contrib/rest/src/python/test.py +++ /dev/null @@ -1,163 +0,0 @@ -#! /usr/bin/env python - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import time -import unittest - -from zkrest import ZooKeeper - -class ZooKeeperREST_TestCase(unittest.TestCase): - - BASE_URI = 'http://localhost:9998' - - def setUp(self): - self.zk = ZooKeeper(self.BASE_URI) - - def tearDown(self): - try: - self.zk.delete('/test') - except ZooKeeper.NotFound: - pass - - def test_get_root_node(self): - assert self.zk.get('/') is not None - - def test_get_node_not_found(self): - self.assertRaises(ZooKeeper.NotFound, \ - self.zk.get, '/dummy-node') - - def test_exists_node(self): - assert self.zk.exists('/zookeeper') is True - - def test_get_children(self): - assert any([child['path'] == '/zookeeper/quota' \ - for child in self.zk.get_children('/zookeeper')]) - - def test_create_znode(self): - try: - self.zk.create('/test') - except ZooKeeper.ZNodeExists: - pass # it's ok if already exists - assert self.zk.exists('/test') is True - - def test_create_hierarchy(self): - try: - self.zk.delete(['/a/b', '/a']) - except ZooKeeper.NotFound: - pass - - self.zk.create('/a') - self.zk.create('/a/b') - - self.zk.delete(['/a/b', '/a']) - - def test_create_with_data(self): - self.zk.create('/test', 'some-data') - - zn = self.zk.get('/test') - self.assertEqual(zn.get('data64', None), \ - 'some-data'.encode('base64').strip()) - - def test_delete_znode(self): - self.zk.create('/test') - - self.zk.delete('/test') - assert not self.zk.exists('/test') - - def test_delete_older_version(self): - self.zk.create('/test') - - zn = self.zk.get('/test') - # do one more modification in order to increase the version number - self.zk.set('/test', 'dummy-data') - - self.assertRaises(ZooKeeper.WrongVersion, \ - self.zk.delete, '/test', version=zn['version']) - - def test_delete_raise_not_found(self): - self.zk.create('/test') - - zn = self.zk.get('/test') - self.zk.delete('/test') - - self.assertRaises(ZooKeeper.NotFound, \ - self.zk.delete, '/test', version=zn['version']) - - def test_set(self): - self.zk.create('/test') - - self.zk.set('/test', 'dummy') - - self.assertEqual(self.zk.get('/test')['data64'], \ - 'dummy'.encode('base64').strip()) - - def test_set_with_older_version(self): - if not self.zk.exists('/test'): - self.zk.create('/test', 'random-data') - - zn = self.zk.get('/test') - self.zk.set('/test', 'new-data') - self.assertRaises(ZooKeeper.WrongVersion, self.zk.set, \ - '/test', 'older-version', version=zn['version']) - - def test_set_null(self): - if not self.zk.exists('/test'): - self.zk.create('/test', 'random-data') - self.zk.set('/test', 'data') - assert 'data64' in self.zk.get('/test') - - self.zk.set('/test', null=True) - assert 'data64' not in self.zk.get('/test') - - def test_create_ephemeral_node(self): - with self.zk.session(): - if self.zk.exists('/ephemeral-test'): - self.zk.delete('/ephemeral-test') - - self.zk.create('/ephemeral-test', ephemeral=True) - zn = self.zk.get('/ephemeral-test') - - assert zn['ephemeralOwner'] != 0 - - def test_create_session(self): - with self.zk.session() as sid: - self.assertEqual(len(sid), 36) # UUID - - def test_session_invalidation(self): - self.zk.start_session(expire=1) - self.zk.create('/ephemeral-test', ephemeral=True) - - # keep the session alive by sending heartbeat requests - for _ in range(1,2): - self.zk.heartbeat() - time.sleep(0.9) - - time.sleep(2) # wait for the session to expire - self.assertRaises(ZooKeeper.InvalidSession, \ - self.zk.create, '/ephemeral-test', ephemeral=True) - - def test_presence_signaling(self): - with self.zk.session(expire=1): - self.zk.create('/i-am-online', ephemeral=True) - assert self.zk.exists('/i-am-online') - assert not self.zk.exists('/i-am-online') - - -if __name__ == '__main__': - unittest.main() - diff --git a/src/contrib/rest/src/python/zk_dump_tree.py b/src/contrib/rest/src/python/zk_dump_tree.py deleted file mode 100755 index 517d23b345d..00000000000 --- a/src/contrib/rest/src/python/zk_dump_tree.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/python - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import getopt -import sys -import simplejson -import urllib2 -from base64 import b64decode - -printdata = False -fullpath = False - -def dump_node(url, depth): - """Dump the node, then dump children recursively - - Arguments: - - `url`: - - `depth`: - """ - req = urllib2.urlopen(url) - resp = simplejson.load(req) - if 'Error' in resp: - raise resp['Error'] - - if fullpath: - name = resp['path'] - else: - name = '/' + resp['path'].split('/')[-1] - - data64 = resp.get('data64') - dataUtf8 = resp.get('dataUtf8') - if data64 and printdata: - data = b64decode(data64) - print '%(indent)s%(name)s = b64(%(data64)s) str(%(data)s)' % \ - {'indent':' '*2*depth, 'name':name, 'data64':data64, 'data':data} - elif dataUtf8 and printdata: - print '%(indent)s%(name)s = %(data)s' % \ - {'indent':' '*2*depth, 'name':name, 'data':dataUtf8} - else: - print '%(indent)s%(name)s' % {'indent':' '*2*depth, 'name':name} - - req = urllib2.urlopen(resp['uri'] + '?view=children') - resp = simplejson.load(req) - - for child in resp.get('children', []): - dump_node(resp['child_uri_template'] - .replace("{child}", urllib2.quote(child)), - depth + 1) - -def zk_dump_tree(url, root): - """Dump the tree starting at the roota - - Arguments: - - `root`: - """ - dump_node(url + '/znodes/v1' + root, 0) - -def usage(): - """Usage - """ - print 'Usage: zk_dump_tree.py [-h|--help -u|--url=url -d|--data -f|--fullpath -r|--root=root]' - print ' where url is the url of the rest server, data is whether to' - print ' to include node data on output, root is the znode root' - print ' fullpath prints the full node path (useful for copy/paste)' - -if __name__ == '__main__': - try: - opts, args = getopt.getopt(sys.argv[1:], - "hu:dfr:", ["help", "url=", "data", "fullpath", "root="]) - except getopt.GetoptError, err: - # print help information and exit: - print str(err) # will print something like "option -a not recognized" - usage() - sys.exit(2) - url ='http://localhost:9998' - root = '/' - for o, a in opts: - if o in ("-d", "--data"): - printdata = True - elif o in ("-h", "--help"): - usage() - sys.exit() - elif o in ("-u", "--url"): - url = a - elif o in ("-r", "--root"): - root = a - elif o in ("-f", "--fullpath"): - fullpath = True - else: - assert False, "unhandled option" - - print 'Accessing REST server at ' + url - zk_dump_tree(url, root) diff --git a/src/contrib/rest/src/python/zkrest.py b/src/contrib/rest/src/python/zkrest.py deleted file mode 100644 index c009d5d9b10..00000000000 --- a/src/contrib/rest/src/python/zkrest.py +++ /dev/null @@ -1,218 +0,0 @@ - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import urllib2 -import urllib -import simplejson - -from contextlib import contextmanager - -class RequestWithMethod(urllib2.Request): - """ Request class that know how to set the method name """ - def __init__(self, *args, **kwargs): - urllib2.Request.__init__(self, *args, **kwargs) - self._method = None - - def get_method(self): - return self._method or \ - urllib2.Request.get_method(self) - - def set_method(self, method): - self._method = method - -class ZooKeeper(object): - - class Error(Exception): pass - - class NotFound(Error): pass - - class ZNodeExists(Error): pass - - class InvalidSession(Error): pass - - class WrongVersion(Error): pass - - def __init__(self, uri = 'http://localhost:9998'): - self._base = uri - self._session = None - - def start_session(self, expire=5, id=None): - """ Create a session and return the ID """ - if id is None: - url = "%s/sessions/v1/?op=create&expire=%d" % (self._base, expire) - self._session = self._do_post(url)['id'] - else: - self._session = id - return self._session - - def close_session(self): - """ Close the session on the server """ - if self._session is not None: - url = '%s/sessions/v1/%s' % (self._base, self._session) - self._do_delete(url) - self._session = None - - def heartbeat(self): - """ Send a heartbeat request. This is needed in order to keep a session alive """ - if self._session is not None: - url = '%s/sessions/v1/%s' % (self._base, self._session) - self._do_put(url, '') - - @contextmanager - def session(self, *args, **kwargs): - """ Session handling using a context manager """ - yield self.start_session(*args, **kwargs) - self.close_session() - - def get(self, path): - """ Get a node """ - url = "%s/znodes/v1%s" % (self._base, path) - return self._do_get(url) - - def get_children(self, path): - """ Get all the children for a given path. This function creates a generator """ - url = "%s/znodes/v1%s?view=children" % (self._base, path) - resp = self._do_get(url) - for child in resp.get('children', []): - try: - yield self._do_get(resp['child_uri_template']\ - .replace('{child}', urllib2.quote(child))) - except ZooKeeper.NotFound: - continue - - def create(self, path, data=None, sequence=False, ephemeral=False): - """ Create a new node. By default this call creates a persistent znode. - - You can also create an ephemeral or a sequential znode. - """ - ri = path.rindex('/') - head, name = path[:ri+1], path[ri+1:] - if head != '/': head = head[:-1] - - flags = { - 'null': 'true' if data is None else 'false', - 'ephemeral': 'true' if ephemeral else 'false', - 'sequence': 'true' if sequence else 'false' - } - if ephemeral: - if self._session: - flags['session'] = self._session - else: - raise ZooKeeper.Error, 'You need a session '\ - 'to create an ephemeral node' - flags = urllib.urlencode(flags) - - url = "%s/znodes/v1%s?op=create&name=%s&%s" % \ - (self._base, head, name, flags) - - return self._do_post(url, data) - - def set(self, path, data=None, version=-1, null=False): - """ Set the value of node """ - url = "%s/znodes/v1%s?%s" % (self._base, path, \ - urllib.urlencode({ - 'version': version, - 'null': 'true' if null else 'false' - })) - return self._do_put(url, data) - - def delete(self, path, version=-1): - """ Delete a znode """ - if type(path) is list: - map(lambda el: self.delete(el, version), path) - return - - url = '%s/znodes/v1%s?%s' % (self._base, path, \ - urllib.urlencode({ - 'version':version - })) - try: - return self._do_delete(url) - except urllib2.HTTPError, e: - if e.code == 412: - raise ZooKeeper.WrongVersion(path) - elif e.code == 404: - raise ZooKeeper.NotFound(path) - raise - - def exists(self, path): - """ Do a znode exists """ - try: - self.get(path) - return True - except ZooKeeper.NotFound: - return False - - def _do_get(self, uri): - """ Send a GET request and convert errors to exceptions """ - try: - req = urllib2.urlopen(uri) - resp = simplejson.load(req) - - if 'Error' in resp: - raise ZooKeeper.Error(resp['Error']) - - return resp - except urllib2.HTTPError, e: - if e.code == 404: - raise ZooKeeper.NotFound(uri) - raise - - def _do_post(self, uri, data=None): - """ Send a POST request and convert errors to exceptions """ - try: - req = urllib2.Request(uri, {}) - req.add_header('Content-Type', 'application/octet-stream') - if data is not None: - req.add_data(data) - - resp = simplejson.load(urllib2.urlopen(req)) - if 'Error' in resp: - raise ZooKeeper.Error(resp['Error']) - return resp - - except urllib2.HTTPError, e: - if e.code == 201: - return True - elif e.code == 409: - raise ZooKeeper.ZNodeExists(uri) - elif e.code == 401: - raise ZooKeeper.InvalidSession(uri) - raise - - def _do_delete(self, uri): - """ Send a DELETE request """ - req = RequestWithMethod(uri) - req.set_method('DELETE') - req.add_header('Content-Type', 'application/octet-stream') - return urllib2.urlopen(req).read() - - def _do_put(self, uri, data): - """ Send a PUT request """ - try: - req = RequestWithMethod(uri) - req.set_method('PUT') - req.add_header('Content-Type', 'application/octet-stream') - if data is not None: - req.add_data(data) - - return urllib2.urlopen(req).read() - except urllib2.HTTPError, e: - if e.code == 412: # precondition failed - raise ZooKeeper.WrongVersion(uri) - raise - diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/Base.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/Base.java deleted file mode 100644 index 2b02cfea315..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/Base.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.io.ByteArrayInputStream; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.server.jersey.SetTest.MyWatcher; -import org.apache.zookeeper.server.jersey.cfg.RestCfg; -import org.junit.After; -import org.junit.Before; - -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.WebResource; - -/** - * Test stand-alone server. - * - */ -public class Base extends TestCase { - protected static final Logger LOG = Logger.getLogger(Base.class); - - protected static final String CONTEXT_PATH = "/zk"; - protected static final int GRIZZLY_PORT = 10104; - protected static final String BASEURI = String.format( - "http://localhost:%d%s", GRIZZLY_PORT, CONTEXT_PATH); - protected static final String ZKHOSTPORT = "localhost:22182"; - protected Client client; - protected WebResource znodesr, sessionsr; - - protected ZooKeeper zk; - - private RestMain rest; - - @Before - public void setUp() throws Exception { - super.setUp(); - - RestCfg cfg = new RestCfg(new ByteArrayInputStream(String.format( - "rest.port=%s\n" + - "rest.endpoint.1=%s;%s\n", - GRIZZLY_PORT, CONTEXT_PATH, ZKHOSTPORT).getBytes())); - - rest = new RestMain(cfg); - rest.start(); - - zk = new ZooKeeper(ZKHOSTPORT, 30000, new MyWatcher()); - - client = Client.create(); - znodesr = client.resource(BASEURI).path("znodes/v1"); - sessionsr = client.resource(BASEURI).path("sessions/v1/"); - } - - @After - public void tearDown() throws Exception { - super.tearDown(); - - client.destroy(); - zk.close(); - rest.stop(); - } - - protected static String createBaseZNode() throws Exception { - ZooKeeper zk = new ZooKeeper(ZKHOSTPORT, 30000, new MyWatcher()); - - String baseZnode = zk.create("/test-", null, Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT_SEQUENTIAL); - zk.close(); - - return baseZnode; - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java deleted file mode 100644 index b8d6f11762c..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/CreateTest.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; -import java.util.Collection; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.server.jersey.jaxb.ZPath; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.WebResource.Builder; - - -/** - * Test stand-alone server. - * - */ -@RunWith(Parameterized.class) -public class CreateTest extends Base { - protected static final Logger LOG = Logger.getLogger(CreateTest.class); - - private String accept; - private String path; - private String name; - private String encoding; - private ClientResponse.Status expectedStatus; - private ZPath expectedPath; - private byte[] data; - private boolean sequence; - - public static class MyWatcher implements Watcher { - public void process(WatchedEvent event) { - // FIXME ignore for now - } - } - - @Parameters - public static Collection data() throws Exception { - String baseZnode = Base.createBaseZNode(); - - return Arrays.asList(new Object[][] { - {MediaType.APPLICATION_JSON, - baseZnode, "foo bar", "utf8", - ClientResponse.Status.CREATED, - new ZPath(baseZnode + "/foo bar"), null, - false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t1", "utf8", - ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t1"), - null, false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t1", "utf8", - ClientResponse.Status.CONFLICT, null, null, false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t2", "utf8", - ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t2"), - "".getBytes(), false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t2", "utf8", - ClientResponse.Status.CONFLICT, null, null, false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t3", "utf8", - ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t3"), - "foo".getBytes(), false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t3", "utf8", - ClientResponse.Status.CONFLICT, null, null, false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-t4", "base64", - ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-t4"), - "foo".getBytes(), false }, - {MediaType.APPLICATION_JSON, baseZnode, "c-", "utf8", - ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-"), null, - true }, - {MediaType.APPLICATION_JSON, baseZnode, "c-", "utf8", - ClientResponse.Status.CREATED, new ZPath(baseZnode + "/c-"), null, - true } - }); - } - - public CreateTest(String accept, String path, String name, String encoding, - ClientResponse.Status status, ZPath expectedPath, byte[] data, - boolean sequence) - { - this.accept = accept; - this.path = path; - this.name = name; - this.encoding = encoding; - this.expectedStatus = status; - this.expectedPath = expectedPath; - this.data = data; - this.sequence = sequence; - } - - @Test - public void testCreate() throws Exception { - LOG.info("STARTING " + getName()); - - WebResource wr = znodesr.path(path).queryParam("dataformat", encoding) - .queryParam("name", name); - if (data == null) { - wr = wr.queryParam("null", "true"); - } - if (sequence) { - wr = wr.queryParam("sequence", "true"); - } - - Builder builder = wr.accept(accept); - - ClientResponse cr; - if (data == null) { - cr = builder.post(ClientResponse.class); - } else { - cr = builder.post(ClientResponse.class, data); - } - assertEquals(expectedStatus, cr.getClientResponseStatus()); - - if (expectedPath == null) { - return; - } - - ZPath zpath = cr.getEntity(ZPath.class); - if (sequence) { - assertTrue(zpath.path.startsWith(expectedPath.path)); - assertTrue(zpath.uri.startsWith(znodesr.path(path).toString())); - } else { - assertEquals(expectedPath, zpath); - assertEquals(znodesr.path(path).toString(), zpath.uri); - } - - // use out-of-band method to verify - byte[] data = zk.getData(zpath.path, false, new Stat()); - if (data == null && this.data == null) { - return; - } else if (data == null || this.data == null) { - assertEquals(data, this.data); - } else { - assertTrue(new String(data) + " == " + new String(this.data), - Arrays.equals(data, this.data)); - } - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java deleted file mode 100644 index 21ca5e4dc2e..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/DeleteTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; -import java.util.Collection; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.data.Stat; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.sun.jersey.api.client.ClientResponse; - - -/** - * Test stand-alone server. - * - */ -@RunWith(Parameterized.class) -public class DeleteTest extends Base { - protected static final Logger LOG = Logger.getLogger(DeleteTest.class); - - private String zpath; - private ClientResponse.Status expectedStatus; - - public static class MyWatcher implements Watcher { - public void process(WatchedEvent event) { - // FIXME ignore for now - } - } - - @Parameters - public static Collection data() throws Exception { - String baseZnode = Base.createBaseZNode(); - - return Arrays.asList(new Object[][] { - {baseZnode, baseZnode, ClientResponse.Status.NO_CONTENT }, - {baseZnode, baseZnode, ClientResponse.Status.NO_CONTENT } - }); - } - - public DeleteTest(String path, String zpath, ClientResponse.Status status) { - this.zpath = zpath; - this.expectedStatus = status; - } - - public void verify(String type) throws Exception { - if (expectedStatus != ClientResponse.Status.NOT_FOUND) { - zpath = zk.create(zpath, null, Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT_SEQUENTIAL); - } - - ClientResponse cr = znodesr.path(zpath).accept(type).type(type) - .delete(ClientResponse.class); - assertEquals(expectedStatus, cr.getClientResponseStatus()); - - // use out-of-band method to verify - Stat stat = zk.exists(zpath, false); - assertNull(stat); - } - - @Test - public void testDelete() throws Exception { - LOG.info("STARTING " + getName()); - verify(MediaType.APPLICATION_OCTET_STREAM); - verify(MediaType.APPLICATION_JSON); - verify(MediaType.APPLICATION_XML); - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java deleted file mode 100644 index 788d64ec179..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/ExistsTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; -import java.util.Collection; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.sun.jersey.api.client.ClientResponse; - - -/** - * Test stand-alone server. - * - */ -@RunWith(Parameterized.class) -public class ExistsTest extends Base { - protected static final Logger LOG = Logger.getLogger(ExistsTest.class); - - private String path; - private ClientResponse.Status expectedStatus; - - @Parameters - public static Collection data() throws Exception { - String baseZnode = Base.createBaseZNode(); - - return Arrays.asList(new Object[][] { - {baseZnode, ClientResponse.Status.OK }, - {baseZnode + "dkdk38383", ClientResponse.Status.NOT_FOUND } - }); - } - - public ExistsTest(String path, ClientResponse.Status status) { - this.path = path; - this.expectedStatus = status; - } - - private void verify(String type) { - ClientResponse cr = znodesr.path(path).accept(type).type(type).head(); - if (type.equals(MediaType.APPLICATION_OCTET_STREAM) - && expectedStatus == ClientResponse.Status.OK) { - assertEquals(ClientResponse.Status.NO_CONTENT, - cr.getClientResponseStatus()); - } else { - assertEquals(expectedStatus, cr.getClientResponseStatus()); - } - } - - @Test - public void testExists() throws Exception { - LOG.info("STARTING " + getName()); - verify(MediaType.APPLICATION_OCTET_STREAM); - verify(MediaType.APPLICATION_JSON); - verify(MediaType.APPLICATION_XML); - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java deleted file mode 100644 index 902eba11e8e..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetChildrenTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.server.jersey.jaxb.ZChildren; -import org.apache.zookeeper.server.jersey.jaxb.ZChildrenJSON; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.sun.jersey.api.client.ClientResponse; - - -/** - * Test stand-alone server. - * - */ -@RunWith(Parameterized.class) -public class GetChildrenTest extends Base { - protected static final Logger LOG = Logger.getLogger(GetChildrenTest.class); - - private String accept; - private String path; - private ClientResponse.Status expectedStatus; - private String expectedPath; - private List expectedChildren; - - @Parameters - public static Collection data() throws Exception { - String baseZnode = Base.createBaseZNode(); - String baseZnode2 = Base.createBaseZNode(); - String baseZnode3 = Base.createBaseZNode(); - String baseZnode4 = Base.createBaseZNode(); - String baseZnode5 = Base.createBaseZNode(); - String baseZnode6 = Base.createBaseZNode(); - - return Arrays.asList(new Object[][] { - {MediaType.APPLICATION_JSON, baseZnode + "abddkdkd", - ClientResponse.Status.NOT_FOUND, null, null }, - {MediaType.APPLICATION_XML, baseZnode + "abddkdkd", - ClientResponse.Status.NOT_FOUND, null, null }, - {MediaType.APPLICATION_JSON, baseZnode, ClientResponse.Status.OK, - baseZnode, Arrays.asList(new String[] {}) }, - {MediaType.APPLICATION_XML, baseZnode, ClientResponse.Status.OK, - baseZnode, Arrays.asList(new String[] {}) }, - {MediaType.APPLICATION_JSON, baseZnode, ClientResponse.Status.OK, - baseZnode, Arrays.asList(new String[] {"c1"}) }, - {MediaType.APPLICATION_XML, baseZnode4, ClientResponse.Status.OK, - baseZnode4, Arrays.asList(new String[] {"c1"}) }, - {MediaType.APPLICATION_JSON, baseZnode2, ClientResponse.Status.OK, - baseZnode2, Arrays.asList(new String[] {"c1", "c2"}) }, - {MediaType.APPLICATION_XML, baseZnode5, ClientResponse.Status.OK, - baseZnode5, Arrays.asList(new String[] {"c1", "c2"}) }, - {MediaType.APPLICATION_JSON, baseZnode3, ClientResponse.Status.OK, - baseZnode3, Arrays.asList(new String[] {"c1", "c2", "c3", "c4"}) }, - {MediaType.APPLICATION_XML, baseZnode6, ClientResponse.Status.OK, - baseZnode6, Arrays.asList(new String[] {"c1", "c2", "c3", "c4"}) } - - }); - } - - public GetChildrenTest(String accept, String path, ClientResponse.Status status, - String expectedPath, List expectedChildren) - { - this.accept = accept; - this.path = path; - this.expectedStatus = status; - this.expectedPath = expectedPath; - this.expectedChildren = expectedChildren; - } - - @Test - public void testGetChildren() throws Exception { - LOG.info("STARTING " + getName()); - - if (expectedChildren != null) { - for(String child : expectedChildren) { - zk.create(expectedPath + "/" + child, null, - Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - } - } - - ClientResponse cr = znodesr.path(path).queryParam("view", "children") - .accept(accept).get(ClientResponse.class); - assertEquals(expectedStatus, cr.getClientResponseStatus()); - - if (expectedChildren == null) { - return; - } - - if (accept.equals(MediaType.APPLICATION_JSON)) { - ZChildrenJSON zchildren = cr.getEntity(ZChildrenJSON.class); - Collections.sort(expectedChildren); - Collections.sort(zchildren.children); - assertEquals(expectedChildren, zchildren.children); - assertEquals(znodesr.path(path).toString(), zchildren.uri); - assertEquals(znodesr.path(path).toString() + "/{child}", - zchildren.child_uri_template); - } else if (accept.equals(MediaType.APPLICATION_XML)) { - ZChildren zchildren = cr.getEntity(ZChildren.class); - Collections.sort(expectedChildren); - Collections.sort(zchildren.children); - assertEquals(expectedChildren, zchildren.children); - assertEquals(znodesr.path(path).toString(), zchildren.uri); - assertEquals(znodesr.path(path).toString() + "/{child}", - zchildren.child_uri_template); - } else { - fail("unknown accept type"); - } - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java deleted file mode 100644 index c4e96a619e2..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/GetTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; -import java.util.Collection; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.server.jersey.jaxb.ZStat; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.sun.jersey.api.client.ClientResponse; - - -/** - * Test stand-alone server. - * - */ -@RunWith(Parameterized.class) -public class GetTest extends Base { - protected static final Logger LOG = Logger.getLogger(GetTest.class); - - private String accept; - private String path; - private String encoding; - private ClientResponse.Status expectedStatus; - private ZStat expectedStat; - - @Parameters - public static Collection data() throws Exception { - String baseZnode = Base.createBaseZNode(); - - return Arrays.asList(new Object[][] { - {MediaType.APPLICATION_JSON, baseZnode, "utf8", - ClientResponse.Status.OK, new ZStat(baseZnode, null, null) }, - {MediaType.APPLICATION_JSON, baseZnode, "utf8", - ClientResponse.Status.OK, new ZStat(baseZnode, null, "") }, - {MediaType.APPLICATION_JSON, baseZnode, "utf8", - ClientResponse.Status.OK, new ZStat(baseZnode, null, "foo") }, - {MediaType.APPLICATION_JSON, baseZnode, "base64", - ClientResponse.Status.OK, new ZStat(baseZnode, null, null) }, - {MediaType.APPLICATION_JSON, baseZnode, "base64", - ClientResponse.Status.OK, new ZStat(baseZnode, "".getBytes(), null) }, - {MediaType.APPLICATION_JSON, baseZnode, "base64", - ClientResponse.Status.OK, new ZStat(baseZnode, "".getBytes(), null) }, - {MediaType.APPLICATION_JSON, baseZnode, "base64", - ClientResponse.Status.OK, new ZStat(baseZnode, "foo".getBytes(), null) }, - {MediaType.APPLICATION_JSON, baseZnode + "abaddkdk", "utf8", - ClientResponse.Status.NOT_FOUND, null }, - {MediaType.APPLICATION_JSON, baseZnode + "abaddkdk", "base64", - ClientResponse.Status.NOT_FOUND, null }, - - {MediaType.APPLICATION_XML, baseZnode, "utf8", - ClientResponse.Status.OK, new ZStat(baseZnode, null, "foo") }, - {MediaType.APPLICATION_XML, baseZnode, "base64", - ClientResponse.Status.OK, - new ZStat(baseZnode, "foo".getBytes(), null) }, - {MediaType.APPLICATION_XML, baseZnode + "abaddkdk", "utf8", - ClientResponse.Status.NOT_FOUND, null }, - {MediaType.APPLICATION_XML, baseZnode + "abaddkdk", "base64", - ClientResponse.Status.NOT_FOUND, null } - - }); - } - - public GetTest(String accept, String path, String encoding, - ClientResponse.Status status, ZStat stat) - { - this.accept = accept; - this.path = path; - this.encoding = encoding; - this.expectedStatus = status; - this.expectedStat = stat; - } - - @Test - public void testGet() throws Exception { - LOG.info("STARTING " + getName()); - - if (expectedStat != null) { - if (expectedStat.data64 != null || expectedStat.dataUtf8 == null) { - zk.setData(expectedStat.path, expectedStat.data64, -1); - } else { - zk.setData(expectedStat.path, - expectedStat.dataUtf8.getBytes(), -1); - } - } - - ClientResponse cr = znodesr.path(path).queryParam("dataformat", encoding) - .accept(accept).get(ClientResponse.class); - assertEquals(expectedStatus, cr.getClientResponseStatus()); - - if (expectedStat == null) { - return; - } - - ZStat zstat = cr.getEntity(ZStat.class); - assertEquals(expectedStat, zstat); - assertEquals(znodesr.path(path).toString(), zstat.uri); - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java deleted file mode 100644 index fc69cafee8f..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RestTestSuite.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({WadlTest.class, GetTest.class, GetChildrenTest.class, - CreateTest.class, SetTest.class, ExistsTest.class, DeleteTest.class }) -public class RestTestSuite { - - @BeforeClass - public static void setUp() { - // suite setup - } - - @AfterClass - public static void tearDown() { - // suite setup - } - -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java deleted file mode 100644 index b28815f76b2..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/RootTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.server.jersey.jaxb.ZPath; -import org.junit.Test; - -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.WebResource.Builder; - - -/** - * Test stand-alone server. - * - */ -public class RootTest extends Base { - protected static final Logger LOG = Logger.getLogger(RootTest.class); - - @Test - public void testCreate() throws Exception { - LOG.info("STARTING " + getName()); - - String path = "/"; - String name = "roottest-create"; - byte[] data = "foo".getBytes(); - - WebResource wr = znodesr.path(path).queryParam("dataformat", "utf8") - .queryParam("name", name); - Builder builder = wr.accept(MediaType.APPLICATION_JSON); - - ClientResponse cr; - cr = builder.post(ClientResponse.class, data); - assertEquals(ClientResponse.Status.CREATED, cr.getClientResponseStatus()); - - ZPath zpath = cr.getEntity(ZPath.class); - assertEquals(new ZPath(path + name), zpath); - assertEquals(znodesr.path(path).toString(), zpath.uri); - - // use out-of-band method to verify - byte[] rdata = zk.getData(zpath.path, false, new Stat()); - assertTrue(new String(rdata) + " == " + new String(data), - Arrays.equals(rdata, data)); - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SessionTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SessionTest.java deleted file mode 100644 index 77bf45ce27d..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SessionTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.io.IOException; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.KeeperException; -import org.apache.zookeeper.ZooKeeper; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.server.jersey.jaxb.ZSession; -import org.codehaus.jettison.json.JSONException; -import org.junit.Test; - -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.WebResource.Builder; - -public class SessionTest extends Base { - protected static final Logger LOG = Logger.getLogger(SessionTest.class); - - private ZSession createSession() { - return createSession("30"); - } - - private ZSession createSession(String expire) { - WebResource wr = sessionsr.queryParam("op", "create") - .queryParam("expire", expire); - Builder b = wr.accept(MediaType.APPLICATION_JSON); - - ClientResponse cr = b.post(ClientResponse.class, null); - assertEquals(ClientResponse.Status.CREATED, cr - .getClientResponseStatus()); - - return cr.getEntity(ZSession.class); - } - - @Test - public void testCreateNewSession() throws JSONException { - ZSession session = createSession(); - assertEquals(session.id.length(), 36); - - // use out-of-band method to verify - assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - } - - @Test - public void testSessionExpires() throws InterruptedException { - ZSession session = createSession("1"); - - // use out-of-band method to verify - assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - - // wait for the session to be closed - Thread.sleep(1500); - assertFalse(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - } - - @Test - public void testDeleteSession() { - ZSession session = createSession("30"); - - WebResource wr = sessionsr.path(session.id); - Builder b = wr.accept(MediaType.APPLICATION_JSON); - - assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - ClientResponse cr = b.delete(ClientResponse.class, null); - assertEquals(ClientResponse.Status.NO_CONTENT, - cr.getClientResponseStatus()); - - assertFalse(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - } - - @Test - public void testSendHeartbeat() throws InterruptedException { - ZSession session = createSession("2"); - - Thread.sleep(1000); - WebResource wr = sessionsr.path(session.id); - Builder b = wr.accept(MediaType.APPLICATION_JSON); - - ClientResponse cr = b.put(ClientResponse.class, null); - assertEquals(ClientResponse.Status.OK, cr.getClientResponseStatus()); - - Thread.sleep(1500); - assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - - Thread.sleep(1000); - assertFalse(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); - } - - @Test - public void testCreateEphemeralZNode() - throws KeeperException, InterruptedException, IOException { - ZSession session = createSession("30"); - - WebResource wr = znodesr.path("/") - .queryParam("op", "create") - .queryParam("name", "ephemeral-test") - .queryParam("ephemeral", "true") - .queryParam("session", session.id) - .queryParam("null", "true"); - - Builder b = wr.accept(MediaType.APPLICATION_JSON); - ClientResponse cr = b.post(ClientResponse.class); - assertEquals(ClientResponse.Status.CREATED, cr.getClientResponseStatus()); - - Stat stat = new Stat(); - zk.getData("/ephemeral-test", false, stat); - - ZooKeeper sessionZK = ZooKeeperService.getClient(CONTEXT_PATH, session.id); - assertEquals(stat.getEphemeralOwner(), sessionZK.getSessionId()); - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SetTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SetTest.java deleted file mode 100644 index f4ce11cd7e8..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/SetTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import java.util.Arrays; -import java.util.Collection; - -import javax.ws.rs.core.MediaType; - -import org.apache.log4j.Logger; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.WatchedEvent; -import org.apache.zookeeper.Watcher; -import org.apache.zookeeper.ZooDefs.Ids; -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.server.jersey.jaxb.ZStat; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.WebResource.Builder; - - -/** - * Test stand-alone server. - * - */ -@RunWith(Parameterized.class) -public class SetTest extends Base { - protected static final Logger LOG = Logger.getLogger(SetTest.class); - - private String accept; - private String path; - private String encoding; - private ClientResponse.Status expectedStatus; - private ZStat expectedStat; - private byte[] data; - - public static class MyWatcher implements Watcher { - public void process(WatchedEvent event) { - // FIXME ignore for now - } - } - - @Parameters - public static Collection data() throws Exception { - String baseZnode = Base.createBaseZNode(); - - return Arrays.asList(new Object[][] { - {MediaType.APPLICATION_JSON, baseZnode + "/s-t1", "utf8", - ClientResponse.Status.OK, - new ZStat(baseZnode + "/s-t1", null, null), null }, - {MediaType.APPLICATION_JSON, baseZnode + "/s-t2", "utf8", - ClientResponse.Status.OK, - new ZStat(baseZnode + "/s-t2", null, null), new byte[0] }, - {MediaType.APPLICATION_JSON, baseZnode + "/s-t3", "utf8", - ClientResponse.Status.OK, - new ZStat(baseZnode + "/s-t3", null, null), "foobar".getBytes() }, - {MediaType.APPLICATION_JSON, baseZnode + "/s-t4", "base64", - ClientResponse.Status.OK, - new ZStat(baseZnode + "/s-t4", null, null), null }, - {MediaType.APPLICATION_JSON, baseZnode + "/s-t5", "base64", - ClientResponse.Status.OK, - new ZStat(baseZnode + "/s-t5", null, null), new byte[0] }, - {MediaType.APPLICATION_JSON, baseZnode + "/s-t6", "base64", - ClientResponse.Status.OK, - new ZStat(baseZnode + "/s-t6", null, null), - "foobar".getBytes() }, - {MediaType.APPLICATION_JSON, baseZnode + "/dkdkdkd", "utf8", - ClientResponse.Status.NOT_FOUND, null, null }, - {MediaType.APPLICATION_JSON, baseZnode + "/dkdkdkd", "base64", - ClientResponse.Status.NOT_FOUND, null, null }, - }); - } - - public SetTest(String accept, String path, String encoding, - ClientResponse.Status status, ZStat expectedStat, byte[] data) - { - this.accept = accept; - this.path = path; - this.encoding = encoding; - this.expectedStatus = status; - this.expectedStat = expectedStat; - this.data = data; - } - - @Test - public void testSet() throws Exception { - LOG.info("STARTING " + getName()); - - if (expectedStat != null) { - zk.create(expectedStat.path, "initial".getBytes(), Ids.OPEN_ACL_UNSAFE, - CreateMode.PERSISTENT); - } - - WebResource wr = znodesr.path(path).queryParam("dataformat", encoding); - if (data == null) { - wr = wr.queryParam("null", "true"); - } - - Builder builder = wr.accept(accept) - .type(MediaType.APPLICATION_OCTET_STREAM); - - ClientResponse cr; - if (data == null) { - cr = builder.put(ClientResponse.class); - } else { - // this shouldn't be necessary (wrapping data with string) - // but without it there are problems on the server - ie it - // hangs for 30 seconds and doesn't get the data. - // TODO investigate - cr = builder.put(ClientResponse.class, new String(data)); - } - assertEquals(expectedStatus, cr.getClientResponseStatus()); - - if (expectedStat == null) { - return; - } - - ZStat zstat = cr.getEntity(ZStat.class); - assertEquals(expectedStat, zstat); - - // use out-of-band method to verify - byte[] data = zk.getData(zstat.path, false, new Stat()); - if (data == null && this.data == null) { - return; - } else if (data == null || this.data == null) { - fail((data == null ? null : new String(data)) + " == " - + (this.data == null ? null : new String(this.data))); - } else { - assertTrue(new String(data) + " == " + new String(this.data), - Arrays.equals(data, this.data)); - } - } -} diff --git a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/WadlTest.java b/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/WadlTest.java deleted file mode 100644 index 1d5d6608a58..00000000000 --- a/src/contrib/rest/src/test/org/apache/zookeeper/server/jersey/WadlTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zookeeper.server.jersey; - -import org.apache.log4j.Logger; -import org.junit.Test; - -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.core.header.MediaTypes; - - -/** - * Test stand-alone server. - * - */ -public class WadlTest extends Base { - protected static final Logger LOG = Logger.getLogger(WadlTest.class); - - @Test - public void testApplicationWadl() { - WebResource r = client.resource(BASEURI); - String serviceWadl = r.path("application.wadl"). - accept(MediaTypes.WADL).get(String.class); - assertTrue("Something wrong. Returned wadl length not > 0.", - serviceWadl.length() > 0); - } -} diff --git a/src/contrib/rest/src/test/zkServer.sh b/src/contrib/rest/src/test/zkServer.sh deleted file mode 100755 index bff85f33e7b..00000000000 --- a/src/contrib/rest/src/test/zkServer.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ "x$1" == "x" ] -then - echo "USAGE: $0 startClean|start|stop hostPorts" - exit 2 -fi - -if [ "x$1" == "xstartClean" ] -then - if [ "x${base_dir}" == "x" ] - then - rm -rf /tmp/zkdata - else - rm -rf ${base_dir}/build/tmp - fi -fi - -# Make sure nothing is left over from before -if [ -r "/tmp/zk.pid" ] -then -pid=`cat /tmp/zk.pid` -kill -9 $pid -rm -f /tmp/zk.pid -fi - -if [ -r "${base_dir}/build/tmp/zk.pid" ] -then -pid=`cat ${base_dir}/build/tmp/zk.pid` -kill -9 $pid -rm -f ${base_dir}/build/tmp/zk.pid -fi - -if [ "x${base_dir}" == "x" ] -then -zk_base="../../../" -else -zk_base="${base_dir}" -fi - -CLASSPATH="$CLASSPATH:${zk_base}/build/classes" -CLASSPATH="$CLASSPATH:${zk_base}/conf" - -for i in "${zk_base}"/build/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" -done - -for i in "${zk_base}"/src/java/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" -done - -case $1 in -start|startClean) - if [ "x${base_dir}" == "x" ] - then - mkdir -p /tmp/zkdata - java -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain 22182 /tmp/zkdata &> /tmp/zk.log & - echo $! > /tmp/zk.pid - else - mkdir -p ${base_dir}/build/tmp/zkdata - java -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain 22182 ${base_dir}/build/tmp/zkdata &> ${base_dir}/build/tmp/zk.log & - echo $! > ${base_dir}/build/tmp/zk.pid - fi - sleep 5 - ;; -stop) - # Already killed above - ;; -*) - echo "Unknown command " + $1 - exit 2 -esac - diff --git a/src/contrib/zkfuse/Makefile.am b/src/contrib/zkfuse/Makefile.am deleted file mode 100644 index 36da1a50d4e..00000000000 --- a/src/contrib/zkfuse/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in - -SUBDIRS = src - diff --git a/src/contrib/zkfuse/README.txt b/src/contrib/zkfuse/README.txt deleted file mode 100644 index ee8ed9ee656..00000000000 --- a/src/contrib/zkfuse/README.txt +++ /dev/null @@ -1,62 +0,0 @@ -Original authors of zkfuse are Swee Lim & Bartlomiej M Niechwiej of Yahoo. -' -ZooKeeper FUSE (File System in Userspace) -========================================= - -Pre-requisites --------------- -1. Linux system with 2.6.X kernel. -2. Fuse (Filesystem in Userspace) must be installed on the build node. -3. Development build libraries: - a. fuse - b. log4cxx - c. pthread - -Build instructions ------------------- -1. cd into this directory -2. autoreconf -if -3. ./configure -4. make -5. zkfuse binary is under the src directory - -Testing Zkfuse --------------- -1. Depending on permission on /dev/fuse, you may need to sudo -u root. - * If /dev/fuse has permissions 0600, then you have to run Zkfuse as root. - * If /dev/fuse has permissions 0666, then you can run Zkfuse as any user. -2. Create or find a mount point that you have "rwx" permission. - * e.g. mkdir -p /tmp/zkfuse -3. Run Zkfuse as follows: - zkfuse -z -m /tmp/zkfuse -d - -z specifies ZooKeeper address(es) : - -m specifies the mount point - -d specifies the debug mode. - For additional command line options, try "zkfuse -h". - -FAQ ---- -Q. How to fix "warning: macro `AM_PATH_CPPUNIT' not found in library"? -A. * install cppunit (src or pkg) on build machine - -Q. Why can't Zkfuse cannot write to current directory? -A. * If Zkfuse is running as root on a NFS mounted file system, it will not - have root permissions because root user is mapped to another user by - NFS admin. - * If you run Zkfuse as root, it is a good idea to run Zkfuse from a - directory that you have write access to. This will allow core files - to be saved. - -Q. Why Zkfuse cannot mount? -A. * Check that the mount point exists and you have "rwx" permissions. - * Check that previous mounts have been umounted. If Zkfuse does not - exit cleanly, its mount point may have to be umounted manually. - If you cannot umount manually, make sure that there no files is open - within the mount point. - -Q. Why does Zkfuse complain about logging at startup? -A. * Zkfuse uses log4cxx for logging. It is looking for log4cxx.properties - file to obtain its logging configuration. - * There is an example log4cxx.properties file in the Zkfuse source - directory. - diff --git a/src/contrib/zkfuse/build.xml b/src/contrib/zkfuse/build.xml deleted file mode 100644 index f589453810a..00000000000 --- a/src/contrib/zkfuse/build.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/zkfuse/configure.ac b/src/contrib/zkfuse/configure.ac deleted file mode 100644 index ae2996bc506..00000000000 --- a/src/contrib/zkfuse/configure.ac +++ /dev/null @@ -1,70 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) - -AC_INIT([zkfuse], [2.2.0]) -AM_INIT_AUTOMAKE(foreign) - -AC_CONFIG_SRCDIR([src/zkadapter.h]) -AM_CONFIG_HEADER([config.h]) - -PACKAGE=zkfuse -VERSION=1.0 - -AC_SUBST(PACKAGE) -AC_SUBST(VERSION) - -BUILD_PATH="`pwd`" - -# Checks for programs. -AC_LANG_CPLUSPLUS -AC_PROG_CXX - -# Checks for libraries. -AC_CHECK_LIB([fuse], [main]) -AC_CHECK_LIB([log4cxx], [main]) -AC_CHECK_LIB([thread], [thr_create]) -AC_CHECK_LIB([pthread], [pthread_create]) -AC_CHECK_LIB([rt], [clock_gettime]) -AC_CHECK_LIB([socket], [socket]) -AC_CHECK_LIB([nsl], [gethostbyname]) -AC_CHECK_LIB([ulockmgr], [ulockmgr_op]) - -ZOOKEEPER_PATH=${BUILD_PATH}/../../c -AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER_LD="-L${ZOOKEEPER_PATH}/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_PATH}/.libs"]) - -AC_SUBST(ZOOKEEPER_PATH) -AC_SUBST(ZOOKEEPER_LD) - -# Checks for header files. -AC_HEADER_DIRENT -AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_HEADER_STDBOOL -AC_C_CONST -AC_TYPE_UID_T -AC_C_INLINE -AC_TYPE_INT32_T -AC_TYPE_INT64_T -AC_TYPE_MODE_T -AC_TYPE_OFF_T -AC_TYPE_SIZE_T -AC_CHECK_MEMBERS([struct stat.st_blksize]) -AC_STRUCT_ST_BLOCKS -AC_HEADER_TIME -AC_TYPE_UINT32_T -AC_TYPE_UINT64_T -AC_TYPE_UINT8_T -AC_C_VOLATILE - -# Checks for library functions. -AC_FUNC_UTIME_NULL -AC_CHECK_FUNCS([gettimeofday memset mkdir rmdir strdup strerror strstr strtol strtoul strtoull utime]) - -AC_CONFIG_FILES([Makefile]) -AC_CONFIG_FILES([src/Makefile]) -AC_OUTPUT -AC_C_VOLATILE diff --git a/src/contrib/zkfuse/src/Makefile.am b/src/contrib/zkfuse/src/Makefile.am deleted file mode 100644 index c0d87e317ff..00000000000 --- a/src/contrib/zkfuse/src/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CXXFLAGS = -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ - -I$(top_srcdir)/include -I/usr/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT - -noinst_PROGRAMS = zkfuse - -zkfuse_SOURCES = zkfuse.cc zkadapter.cc thread.cc log.cc -zkfuse_LDADD = ${ZOOKEEPER_LD} \ No newline at end of file diff --git a/src/contrib/zkfuse/src/blockingqueue.h b/src/contrib/zkfuse/src/blockingqueue.h deleted file mode 100644 index 4677290d7b6..00000000000 --- a/src/contrib/zkfuse/src/blockingqueue.h +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __BLOCKINGQUEUE_H__ -#define __BLOCKINGQUEUE_H__ - -#include - -#include "mutex.h" - -using namespace std; -USING_ZKFUSE_NAMESPACE - -namespace zk { - -/** - * \brief An unbounded blocking queue of elements of type E. - * - *

    - * This class is thread safe. - */ -template -class BlockingQueue { - public: - - /** - * \brief Adds the specified element to this queue, waiting if necessary - * \brief for space to become available. - * - * @param e the element to be added - */ - void put(E e); - - /** - * \brief Retrieves and removes the head of this queue, waiting if - * \brief no elements are present in this queue. - * - * @param timeout how long to wait until an element becomes availabe, - * in milliseconds; if 0 then wait forever - * @param timedOut if not NULL then set to true whether this function timed out - * @return the element from the queue - */ - E take(int32_t timeout = 0, bool *timedOut = NULL); - - /** - * Returns the current size of this blocking queue. - * - * @return the number of elements in this queue - */ - int size() const; - - /** - * \brief Returns whether this queue is empty or not. - * - * @return true if this queue has no elements; false otherwise - */ - bool empty() const; - - private: - - /** - * The queue of elements. Deque is used to provide O(1) time - * for head elements removal. - */ - deque m_queue; - - /** - * The mutex used for queue synchronization. - */ - mutable zkfuse::Mutex m_mutex; - - /** - * The conditionial variable associated with the mutex above. - */ - mutable Cond m_cond; - -}; - -template -int BlockingQueue::size() const { - int size; - m_mutex.Acquire(); - size = m_queue.size(); - m_mutex.Release(); - return size; -} - -template -bool BlockingQueue::empty() const { - bool isEmpty; - m_mutex.Acquire(); - isEmpty = m_queue.empty(); - m_mutex.Release(); - return isEmpty; -} - -template -void BlockingQueue::put(E e) { - m_mutex.Acquire(); - m_queue.push_back( e ); - m_cond.Signal(); - m_mutex.Release(); -} - -template - E BlockingQueue::take(int32_t timeout, bool *timedOut) { - m_mutex.Acquire(); - bool hasResult = true; - while (m_queue.empty()) { - if (timeout <= 0) { - m_cond.Wait( m_mutex ); - } else { - if (!m_cond.Wait( m_mutex, timeout )) { - hasResult = false; - break; - } - } - } - if (hasResult) { - E e = m_queue.front(); - m_queue.pop_front(); - m_mutex.Release(); - if (timedOut) { - *timedOut = false; - } - return e; - } else { - m_mutex.Release(); - if (timedOut) { - *timedOut = true; - } - return E(); - } -} - -} - -#endif /* __BLOCKINGQUEUE_H__ */ - diff --git a/src/contrib/zkfuse/src/doxygen.cfg b/src/contrib/zkfuse/src/doxygen.cfg deleted file mode 100644 index 308b09450a4..00000000000 --- a/src/contrib/zkfuse/src/doxygen.cfg +++ /dev/null @@ -1,1242 +0,0 @@ -# Doxyfile 1.4.3 - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = ZkFuse - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, -# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, -# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, -# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, -# Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# This tag can be used to specify the encoding used in the generated output. -# The encoding is not always determined by the language that is chosen, -# but also whether or not the output is meant for Windows or non-Windows users. -# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES -# forces the Windows encoding (this is the default for the Windows binary), -# whereas setting the tag to NO uses a Unix-style encoding (the default for -# all platforms other than Windows). - -USE_WINDOWS_ENCODING = NO - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like the Qt-style comments (thus requiring an -# explicit @brief command for a brief description. - -JAVADOC_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources -# only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. - -SHOW_DIRECTORIES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from the -# version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the progam writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. - -EXCLUDE_PATTERNS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = YES - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = NO - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_PREDEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_WIDTH = 1024 - -# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_HEIGHT = 1024 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that a graph may be further truncated if the graph's -# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH -# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), -# the graph is not depth-constrained. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/src/contrib/zkfuse/src/event.cc b/src/contrib/zkfuse/src/event.cc deleted file mode 100644 index 541657e1449..00000000000 --- a/src/contrib/zkfuse/src/event.cc +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "event.h" - -#define LOG_LEVEL LOG_FATAL -#define MODULE_NAME "Event" - -using namespace std; - -namespace zkfuse { - -} /* end of 'namespace zkfuse' */ - diff --git a/src/contrib/zkfuse/src/event.h b/src/contrib/zkfuse/src/event.h deleted file mode 100644 index 0506932f7a3..00000000000 --- a/src/contrib/zkfuse/src/event.h +++ /dev/null @@ -1,553 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __EVENT_H__ -#define __EVENT_H__ - -#include -#include -#include -#include -#ifdef GCC4 -# include -using namespace std::tr1; -#else -# include -using namespace boost; -#endif - -#include "log.h" -#include "blockingqueue.h" -#include "mutex.h" -#include "thread.h" - -using namespace std; -using namespace zk; - -namespace zkfuse { - -//forward declaration of EventSource -template -class EventSource; - -/** - * \brief This interface is implemented by an observer - * \brief of a particular {@link EventSource}. - */ -template -class EventListener { - public: - - /** - * \brief This method is invoked whenever an event - * \brief has been received by the event source being observed. - * - * @param source the source the triggered the event - * @param e the actual event being triggered - */ - virtual void eventReceived(const EventSource &source, const E &e) = 0; -}; - -/** - * \brief This class represents a source of events. - * - *

    - * Each source can have many observers (listeners) attached to it - * and in case of an event, this source may propagate the event - * using {@link #fireEvent} method. - */ -template -class EventSource { - public: - - /** - * \brief The type corresponding to the list of registered event listeners. - */ - typedef set *> EventListeners; - - /** - * \brief Registers a new event listener. - * - * @param listener the listener to be added to the set of listeners - */ - void addListener(EventListener *listener) { - m_listeners.insert( listener ); - } - - /** - * \brief Removes an already registered listener. - * - * @param listener the listener to be removed - */ - void removeListener(EventListener *listener) { - m_listeners.erase( listener ); - } - - /** - * \brief Destructor. - */ - virtual ~EventSource() {} - - protected: - - /** - * \brief Fires the given event to all registered listeners. - * - *

    - * This method essentially iterates over all listeners - * and invokes {@link fireEvent(EventListener *listener, const E &event)} - * for each element. All derived classes are free to - * override the method to provide better error handling - * than the default implementation. - * - * @param event the event to be propagated to all listeners - */ - void fireEvent(const E &event); - - /** - * \brief Sends an event to the given listener. - * - * @param listener the listener to whom pass the event - * @param event the event to be handled - */ - virtual void fireEvent(EventListener *listener, const E &event); - - private: - - /** - * The set of registered event listeners. - */ - EventListeners m_listeners; - -}; - -/** - * \brief The interface of a generic event wrapper. - */ -class AbstractEventWrapper { - public: - - /** - * \brief Destructor. - */ - virtual ~AbstractEventWrapper() {} - - /** - * \brief Returns the underlying wrapee's data. - */ - virtual void *getWrapee() = 0; -}; - -/** - * \brief A template based implementation of {@link AbstractEventWrapper}. - */ -template -class EventWrapper : public AbstractEventWrapper { - public: - EventWrapper(const E &e) : m_e(e) { - } - void *getWrapee() { - return &m_e; - } - private: - E m_e; -}; - -/** - * \brief This class represents a generic event. - */ -class GenericEvent { - public: - - /** - * \brief Constructor. - */ - GenericEvent() : m_type(0) {} - - /** - * \brief Constructor. - * - * @param type the type of this event - * @param eventWarpper the wrapper around event's data - */ - GenericEvent(int type, AbstractEventWrapper *eventWrapper) : - m_type(type), m_eventWrapper(eventWrapper) { - } - - /** - * \brief Returns the type of this event. - * - * @return type of this event - */ - int getType() const { return m_type; } - - /** - * \brief Returns the event's data. - * - * @return the event's data - */ - void *getEvent() const { return m_eventWrapper->getWrapee(); } - - private: - - /** - * The event type. - */ - int m_type; - - /** - * The event represented as abstract wrapper. - */ - shared_ptr m_eventWrapper; - -}; - -/** - * \brief This class adapts {@link EventListener} to a generic listener. - * Essentially this class listens on incoming events and fires them - * as {@link GenericEvent}s. - */ -template -class EventListenerAdapter : public virtual EventListener, - public virtual EventSource -{ - public: - - /** - * \brief Constructor. - * - * @param eventSource the source on which register this listener - */ - EventListenerAdapter(EventSource &eventSource) { - eventSource.addListener(this); - } - - void eventReceived(const EventSource &source, const E &e) { - AbstractEventWrapper *wrapper = new EventWrapper(e); - GenericEvent event(type, wrapper); - fireEvent( event ); - } - -}; - -/** - * \brief This class provides an adapter between an asynchronous and synchronous - * \brief event handling. - * - *

    - * This class queues up all received events and exposes them through - * {@link #getNextEvent()} method. - */ -template -class SynchronousEventAdapter : public EventListener { - public: - - void eventReceived(const EventSource &source, const E &e) { - m_queue.put( e ); - } - - /** - * \brief Returns the next available event from the underlying queue, - * \brief possibly blocking, if no data is available. - * - * @return the next available event - */ - E getNextEvent() { - return m_queue.take(); - } - - /** - * \brief Returns whether there are any events in the queue or not. - * - * @return true if there is at least one event and - * the next call to {@link #getNextEvent} won't block - */ - bool hasEvents() const { - return (m_queue.empty() ? false : true); - } - - /** - * \brief Destructor. - */ - virtual ~SynchronousEventAdapter() {} - - private: - - /** - * The blocking queue of all events received so far. - */ - BlockingQueue m_queue; - -}; - -/** - * This typedef defines the type of a timer Id. - */ -typedef int32_t TimerId; - -/** - * This class represents a timer event parametrized by the user's data type. - */ -template -class TimerEvent { - public: - - /** - * \brief Constructor. - * - * @param id the ID of this event - * @param alarmTime when this event is to be triggered - * @param userData the user data associated with this event - */ - TimerEvent(TimerId id, int64_t alarmTime, const T &userData) : - m_id(id), m_alarmTime(alarmTime), m_userData(userData) - {} - - /** - * \brief Constructor. - */ - TimerEvent() : m_id(-1), m_alarmTime(-1) {} - - /** - * \brief Returns the ID. - * - * @return the ID of this event - */ - TimerId getID() const { return m_id; } - - /** - * \brief Returns the alarm time. - * - * @return the alarm time - */ - int64_t getAlarmTime() const { return m_alarmTime; } - - /** - * \brief Returns the user's data. - * - * @return the user's data - */ - T const &getUserData() const { return m_userData; } - - /** - * \brief Returns whether the given alarm time is less than this event's - * \brief time. - */ - bool operator<(const int64_t alarmTime) const { - return m_alarmTime < alarmTime; - } - - private: - - /** - * The ID of ths event. - */ - TimerId m_id; - - /** - * The time at which this event triggers. - */ - int64_t m_alarmTime; - - /** - * The user specific data associated with this event. - */ - T m_userData; - -}; - -template -class Timer : public EventSource > { - public: - - /** - * \brief Constructor. - */ - Timer() : m_currentEventID(0), m_terminating(false) { - m_workerThread.Create( *this, &Timer::sendAlarms ); - } - - /** - * \brief Destructor. - */ - ~Timer() { - m_terminating = true; - m_lock.notify(); - m_workerThread.Join(); - } - - /** - * \brief Schedules the given event timeFromNow milliseconds. - * - * @param timeFromNow time from now, in milliseconds, when the event - * should be triggered - * @param userData the user data associated with the timer event - * - * @return the ID of the newly created timer event - */ - TimerId scheduleAfter(int64_t timeFromNow, const T &userData) { - return scheduleAt( getCurrentTimeMillis() + timeFromNow, userData ); - } - - /** - * \brief Schedules an event at the given time. - * - * @param absTime absolute time, in milliseconds, at which the event - * should be triggered; the time is measured - * from Jan 1st, 1970 - * @param userData the user data associated with the timer event - * - * @return the ID of the newly created timer event - */ - TimerId scheduleAt(int64_t absTime, const T &userData) { - m_lock.lock(); - typename QueueType::iterator pos = - lower_bound( m_queue.begin(), m_queue.end(), absTime ); - TimerId id = m_currentEventID++; - TimerEvent event(id, absTime, userData); - m_queue.insert( pos, event ); - m_lock.notify(); - m_lock.unlock(); - return id; - } - - /** - * \brief Returns the current time since Jan 1, 1970, in milliseconds. - * - * @return the current time in milliseconds - */ - static int64_t getCurrentTimeMillis() { - struct timeval now; - gettimeofday( &now, NULL ); - return now.tv_sec * 1000LL + now.tv_usec / 1000; - } - - /** - * \brief Cancels the given timer event. - * - * - * @param eventID the ID of the event to be canceled - * - * @return whether the event has been canceled - */ - bool cancelAlarm(TimerId eventID) { - bool canceled = false; - m_lock.lock(); - typename QueueType::iterator i; - for (i = m_queue.begin(); i != m_queue.end(); ++i) { - if (eventID == i->getID()) { - m_queue.erase( i ); - canceled = true; - break; - } - } - m_lock.unlock(); - return canceled; - } - - /** - * Executes the main loop of the worker thread. - */ - void sendAlarms() { - //iterate until terminating - while (!m_terminating) { - m_lock.lock(); - //1 step - wait until there is an event in the queue - if (m_queue.empty()) { - //wait up to 100ms to get next event - m_lock.wait( 100 ); - } - bool fire = false; - if (!m_queue.empty()) { - //retrieve the event from the queue and send it - TimerEvent event = m_queue.front(); - //check whether we can send it right away - int64_t timeToWait = - event.getAlarmTime() - getCurrentTimeMillis(); - if (timeToWait <= 0) { - m_queue.pop_front(); - //we fire only if it's still in the queue and alarm - //time has just elapsed (in case the top event - //is canceled) - fire = true; - } else { - m_lock.wait( timeToWait ); - } - m_lock.unlock(); - if (fire) { - fireEvent( event ); - } - } else { - m_lock.unlock(); - } - } - } - - private: - - /** - * The type of timer events queue. - */ - typedef deque > QueueType; - - /** - * The current event ID, auto-incremented each time a new event - * is created. - */ - TimerId m_currentEventID; - - /** - * The queue of timer events sorted by {@link TimerEvent#alarmTime}. - */ - QueueType m_queue; - - /** - * The lock used to guard {@link #m_queue}. - */ - Lock m_lock; - - /** - * The thread that triggers alarms. - */ - CXXThread > m_workerThread; - - /** - * Whether {@link #m_workerThread} is terminating. - */ - volatile bool m_terminating; - -}; - -template -void EventSource::fireEvent(const E &event) { - for (typename EventListeners::iterator i = m_listeners.begin(); - i != m_listeners.end(); - ++i) - { - fireEvent( *i, event ); - } -} - -template -void EventSource::fireEvent(EventListener *listener, const E &event) { - listener->eventReceived( *this, event ); -} - -} /* end of 'namespace zkfuse' */ - -#endif /* __EVENT_H__ */ diff --git a/src/contrib/zkfuse/src/log.cc b/src/contrib/zkfuse/src/log.cc deleted file mode 100644 index e2bfb0dd85a..00000000000 --- a/src/contrib/zkfuse/src/log.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "log.h" - -using namespace std; - -/** - * \brief This class encapsulates a log4cxx configuration. - */ -class LogConfiguration { - public: - LogConfiguration(const string &file) { - PropertyConfigurator::configureAndWatch( file, 5000 ); - } -}; - -//enforces the configuration to be initialized -static LogConfiguration logConfig( "log4cxx.properties" ); diff --git a/src/contrib/zkfuse/src/log.h b/src/contrib/zkfuse/src/log.h deleted file mode 100644 index aefce10b1d6..00000000000 --- a/src/contrib/zkfuse/src/log.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __LOG_H__ -#define __LOG_H__ - -#define ZKFUSE_NAMESPACE zkfuse -#define START_ZKFUSE_NAMESPACE namespace ZKFUSE_NAMESPACE { -#define END_ZKFUSE_NAMESPACE } -#define USING_ZKFUSE_NAMESPACE using namespace ZKFUSE_NAMESPACE; - -#include -#include -#include - -#include -#include -#include -using namespace log4cxx; -using namespace log4cxx::helpers; - -#define PRINTIP(x) ((uint8_t*)&x)[0], ((uint8_t*)&x)[1], \ - ((uint8_t*)&x)[2], ((uint8_t*)&x)[3] - -#define IPFMT "%u.%u.%u.%u" - -#define DECLARE_LOGGER(varName) \ -extern LoggerPtr varName; - -#define DEFINE_LOGGER(varName, logName) \ -static LoggerPtr varName = Logger::getLogger( logName ); - -#define MAX_BUFFER_SIZE 20000 - -#define SPRINTF_LOG_MSG(buffer, fmt, args...) \ - char buffer[MAX_BUFFER_SIZE]; \ - snprintf( buffer, MAX_BUFFER_SIZE, fmt, ##args ); - -// older versions of log4cxx don't support tracing -#ifdef LOG4CXX_TRACE -#define LOG_TRACE(logger, fmt, args...) \ - if (logger->isTraceEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_TRACE( logger, __tmp ); \ - } -#else -#define LOG_TRACE(logger, fmt, args...) \ - if (logger->isDebugEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_DEBUG( logger, __tmp ); \ - } -#endif - -#define LOG_DEBUG(logger, fmt, args...) \ - if (logger->isDebugEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_DEBUG( logger, __tmp ); \ - } - -#define LOG_INFO(logger, fmt, args...) \ - if (logger->isInfoEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_INFO( logger, __tmp ); \ - } - -#define LOG_WARN(logger, fmt, args...) \ - if (logger->isWarnEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_WARN( logger, __tmp ); \ - } - -#define LOG_ERROR(logger, fmt, args...) \ - if (logger->isErrorEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_ERROR( logger, __tmp ); \ - } - -#define LOG_FATAL(logger, fmt, args...) \ - if (logger->isFatalEnabled()) { \ - SPRINTF_LOG_MSG( __tmp, fmt, ##args ); \ - LOG4CXX_FATAL( logger, __tmp ); \ - } - -#ifdef DISABLE_TRACE -# define TRACE(logger, x) -#else -# define TRACE(logger, x) \ -class Trace { \ - public: \ - Trace(const void* p) : _p(p) { \ - LOG_TRACE(logger, "%s %p Enter", __PRETTY_FUNCTION__, p); \ - } \ - ~Trace() { \ - LOG_TRACE(logger, "%s %p Exit", __PRETTY_FUNCTION__, _p); \ - } \ - const void* _p; \ -} traceObj(x); -#endif /* DISABLE_TRACE */ - -#endif /* __LOG_H__ */ - diff --git a/src/contrib/zkfuse/src/log4cxx.properties b/src/contrib/zkfuse/src/log4cxx.properties deleted file mode 100644 index 1e373e42a9e..00000000000 --- a/src/contrib/zkfuse/src/log4cxx.properties +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Set root logger level to DEBUG and its only appender to A1. -log4j.rootLogger=TRACE, A1 - -# A1 is set to be a ConsoleAppender. -log4j.appender.A1=org.apache.log4cxx.ConsoleAppender - -# A1 uses PatternLayout. -log4j.appender.A1.layout=org.apache.log4cxx.PatternLayout -log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n - -log4j.category.zkfuse=TRACE - diff --git a/src/contrib/zkfuse/src/mutex.h b/src/contrib/zkfuse/src/mutex.h deleted file mode 100644 index 86c46043414..00000000000 --- a/src/contrib/zkfuse/src/mutex.h +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __MUTEX_H__ -#define __MUTEX_H__ - -#include -#include -#include - -#include "log.h" - -START_ZKFUSE_NAMESPACE - -class Cond; - -class Mutex { - friend class Cond; - public: - Mutex() { - pthread_mutexattr_init( &m_mutexAttr ); - pthread_mutexattr_settype( &m_mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP ); - pthread_mutex_init( &mutex, &m_mutexAttr ); - } - ~Mutex() { - pthread_mutex_destroy(&mutex); - pthread_mutexattr_destroy( &m_mutexAttr ); - } - void Acquire() { Lock(); } - void Release() { Unlock(); } - void Lock() { - pthread_mutex_lock(&mutex); - } - int TryLock() { - return pthread_mutex_trylock(&mutex); - } - void Unlock() { - pthread_mutex_unlock(&mutex); - } - private: - pthread_mutex_t mutex; - pthread_mutexattr_t m_mutexAttr; -}; - -class AutoLock { - public: - AutoLock(Mutex& mutex) : _mutex(mutex) { - mutex.Lock(); - } - ~AutoLock() { - _mutex.Unlock(); - } - private: - friend class AutoUnlockTemp; - Mutex& _mutex; -}; - -class AutoUnlockTemp { - public: - AutoUnlockTemp(AutoLock & autoLock) : _autoLock(autoLock) { - _autoLock._mutex.Unlock(); - } - ~AutoUnlockTemp() { - _autoLock._mutex.Lock(); - } - private: - AutoLock & _autoLock; -}; - -class Cond { - public: - Cond() { - static pthread_condattr_t attr; - static bool inited = false; - if(!inited) { - inited = true; - pthread_condattr_init(&attr); - } - pthread_cond_init(&_cond, &attr); - } - ~Cond() { - pthread_cond_destroy(&_cond); - } - - void Wait(Mutex& mutex) { - pthread_cond_wait(&_cond, &mutex.mutex); - } - - bool Wait(Mutex& mutex, long long int timeout) { - struct timeval now; - gettimeofday( &now, NULL ); - struct timespec abstime; - int64_t microSecs = now.tv_sec * 1000000LL + now.tv_usec; - microSecs += timeout * 1000; - abstime.tv_sec = microSecs / 1000000LL; - abstime.tv_nsec = (microSecs % 1000000LL) * 1000; - if (pthread_cond_timedwait(&_cond, &mutex.mutex, &abstime) == ETIMEDOUT) { - return false; - } else { - return true; - } - } - - void Signal() { - pthread_cond_signal(&_cond); - } - - private: - pthread_cond_t _cond; -}; - -/** - * A wrapper class for {@link Mutex} and {@link Cond}. - */ -class Lock { - public: - - void lock() { - m_mutex.Lock(); - } - - void unlock() { - m_mutex.Unlock(); - } - - void wait() { - m_cond.Wait( m_mutex ); - } - - bool wait(long long int timeout) { - return m_cond.Wait( m_mutex, timeout ); - } - - void notify() { - m_cond.Signal(); - } - - private: - - /** - * The mutex. - */ - Mutex m_mutex; - - /** - * The condition associated with this lock's mutex. - */ - Cond m_cond; -}; - -END_ZKFUSE_NAMESPACE - -#endif /* __MUTEX_H__ */ - diff --git a/src/contrib/zkfuse/src/thread.cc b/src/contrib/zkfuse/src/thread.cc deleted file mode 100644 index f1ed8166fee..00000000000 --- a/src/contrib/zkfuse/src/thread.cc +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "thread.h" - -DEFINE_LOGGER( LOG, "Thread" ) - -START_ZKFUSE_NAMESPACE - -void Thread::Create(void* ctx, ThreadFunc func) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, _stackSize); - int ret = pthread_create(&mThread, &attr, func, ctx); - if(ret != 0) { - LOG_FATAL( LOG, "pthread_create failed: %s", strerror(errno) ); - } - // pthread_attr_destroy(&attr); - _ctx = ctx; - _func = func; -} - -END_ZKFUSE_NAMESPACE diff --git a/src/contrib/zkfuse/src/thread.h b/src/contrib/zkfuse/src/thread.h deleted file mode 100644 index 0ed12d7f645..00000000000 --- a/src/contrib/zkfuse/src/thread.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __THREAD_H__ -#define __THREAD_H__ - -#include -#include -#include -#include - -#include "log.h" - -START_ZKFUSE_NAMESPACE - -class Thread { - public: - static const size_t defaultStackSize = 1024 * 1024; - typedef void* (*ThreadFunc) (void*); - Thread(size_t stackSize = defaultStackSize) - : _stackSize(stackSize), _ctx(NULL), _func(NULL) - { - memset( &mThread, 0, sizeof(mThread) ); - } - ~Thread() { } - - void Create(void* ctx, ThreadFunc func); - void Join() { - //avoid SEGFAULT because of unitialized mThread - //in case Create(...) was never called - if (_func != NULL) { - pthread_join(mThread, 0); - } - } - private: - pthread_t mThread; - void *_ctx; - ThreadFunc _func; - size_t _stackSize; -}; - - -template -struct ThreadContext { - typedef void (T::*FuncPtr) (void); - ThreadContext(T& ctx, FuncPtr func) : _ctx(ctx), _func(func) {} - void run(void) { - (_ctx.*_func)(); - } - T& _ctx; - FuncPtr _func; -}; - -template -void* ThreadExec(void *obj) { - ThreadContext* tc = (ThreadContext*)(obj); - assert(tc != 0); - tc->run(); - return 0; -} - -template -class CXXThread : public Thread { - public: - typedef void (T::*FuncPtr) (void); - CXXThread(size_t stackSize = Thread::defaultStackSize) - : Thread(stackSize), ctx(0) {} - ~CXXThread() { if (ctx) delete ctx; } - - void Create(T& obj, FuncPtr func) { - assert(ctx == 0); - ctx = new ThreadContext(obj, func); - Thread::Create(ctx, ThreadExec); - } - - private: - ThreadContext* ctx; -}; - - -END_ZKFUSE_NAMESPACE - -#endif /* __THREAD_H__ */ - diff --git a/src/contrib/zkfuse/src/zkadapter.cc b/src/contrib/zkfuse/src/zkadapter.cc deleted file mode 100644 index 886051d97ba..00000000000 --- a/src/contrib/zkfuse/src/zkadapter.cc +++ /dev/null @@ -1,881 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "blockingqueue.h" -#include "thread.h" -#include "zkadapter.h" - -using namespace std; -using namespace zk; - -DEFINE_LOGGER( LOG, "zookeeper.adapter" ) -DEFINE_LOGGER( ZK_LOG, "zookeeper.core" ) - -/** - * \brief A helper class to initialize ZK logging. - */ -class InitZooKeeperLogging -{ - public: - InitZooKeeperLogging() { - if (ZK_LOG->isDebugEnabled() -#ifdef LOG4CXX_TRACE - || ZK_LOG->isTraceEnabled() -#endif - ) - { - zoo_set_debug_level( ZOO_LOG_LEVEL_DEBUG ); - } else if (ZK_LOG->isInfoEnabled()) { - zoo_set_debug_level( ZOO_LOG_LEVEL_INFO ); - } else if (ZK_LOG->isWarnEnabled()) { - zoo_set_debug_level( ZOO_LOG_LEVEL_WARN ); - } else { - zoo_set_debug_level( ZOO_LOG_LEVEL_ERROR ); - } - } -}; - -using namespace std; - -namespace zk -{ - -/** - * \brief This class provides logic for checking if a request can be retried. - */ -class RetryHandler -{ - public: - RetryHandler(const ZooKeeperConfig &zkConfig) - : m_zkConfig(zkConfig) - { - if (zkConfig.getAutoReconnect()) { - retries = 2; - } else { - retries = 0; - } - } - - /** - * \brief Attempts to fix a side effect of the given RC. - * - * @param rc the ZK error code - * @return whether the error code has been handled and the caller should - * retry an operation the caused this error - */ - bool handleRC(int rc) - { - TRACE( LOG, "handleRC" ); - - //check if the given error code is recoverable - if (!retryOnError(rc)) { - return false; - } - LOG_TRACE( LOG, "RC: %d, retries left: %d", rc, retries ); - if (retries-- > 0) { - return true; - } else { - return false; - } - } - - private: - /** - * The ZK config. - */ - const ZooKeeperConfig &m_zkConfig; - - /** - * The number of outstanding retries. - */ - int retries; - - /** - * Checks whether the given error entitles this adapter - * to retry the previous operation. - * - * @param zkErrorCode one of the ZK error code - */ - static bool retryOnError(int zkErrorCode) - { - return (zkErrorCode == ZCONNECTIONLOSS || - zkErrorCode == ZOPERATIONTIMEOUT); - } -}; - - -//the implementation of the global ZK event watcher -void zkWatcher(zhandle_t *zh, int type, int state, const char *path, - void *watcherCtx) -{ - TRACE( LOG, "zkWatcher" ); - - //a workaround for buggy ZK API - string sPath = - (path == NULL || - state == ZOO_SESSION_EVENT || - state == ZOO_NOTWATCHING_EVENT) - ? "" - : string(path); - LOG_INFO( LOG, - "Received a ZK event - type: %d, state: %d, path: '%s'", - type, state, sPath.c_str() ); - ZooKeeperAdapter *zka = (ZooKeeperAdapter *)zoo_get_context(zh); - if (zka != NULL) { - zka->enqueueEvent( type, state, sPath ); - } else { - LOG_ERROR( LOG, - "Skipping ZK event (type: %d, state: %d, path: '%s'), " - "because ZK passed no context", - type, state, sPath.c_str() ); - } -} - - - -// ======================================================================= - -ZooKeeperAdapter::ZooKeeperAdapter(ZooKeeperConfig config, - ZKEventListener *listener, - bool establishConnection) - throw(ZooKeeperException) - : m_zkConfig(config), - mp_zkHandle(NULL), - m_terminating(false), - m_connected(false), - m_state(AS_DISCONNECTED) -{ - TRACE( LOG, "ZooKeeperAdapter" ); - - resetRemainingConnectTimeout(); - - //enforce setting up appropriate ZK log level - static InitZooKeeperLogging INIT_ZK_LOGGING; - - if (listener != NULL) { - addListener(listener); - } - - //start the event dispatcher thread - m_eventDispatcher.Create( *this, &ZooKeeperAdapter::processEvents ); - - //start the user event dispatcher thread - m_userEventDispatcher.Create( *this, &ZooKeeperAdapter::processUserEvents ); - - //optionally establish the connection - if (establishConnection) { - reconnect(); - } -} - -ZooKeeperAdapter::~ZooKeeperAdapter() -{ - TRACE( LOG, "~ZooKeeperAdapter" ); - - try { - disconnect(); - } catch (std::exception &e) { - LOG_ERROR( LOG, - "An exception while disconnecting from ZK: %s", - e.what() ); - } - m_terminating = true; - m_userEventDispatcher.Join(); - m_eventDispatcher.Join(); -} - -void -ZooKeeperAdapter::validatePath(const string &path) throw(ZooKeeperException) -{ - TRACE( LOG, "validatePath" ); - - if (path.find( "/" ) != 0) { - throw ZooKeeperException( string("Node path must start with '/' but" - "it was '") + - path + - "'" ); - } - if (path.length() > 1) { - if (path.rfind( "/" ) == path.length() - 1) { - throw ZooKeeperException( string("Node path must not end with " - "'/' but it was '") + - path + - "'" ); - } - if (path.find( "//" ) != string::npos) { - throw ZooKeeperException( string("Node path must not contain " - "'//' but it was '") + - path + - "'" ); - } - } -} - -void -ZooKeeperAdapter::disconnect() -{ - TRACE( LOG, "disconnect" ); - LOG_TRACE( LOG, "mp_zkHandle: %p, state %d", mp_zkHandle, m_state ); - - m_stateLock.lock(); - if (mp_zkHandle != NULL) { - zookeeper_close( mp_zkHandle ); - mp_zkHandle = NULL; - setState( AS_DISCONNECTED ); - } - m_stateLock.unlock(); -} - -void -ZooKeeperAdapter::reconnect() throw(ZooKeeperException) -{ - TRACE( LOG, "reconnect" ); - - m_stateLock.lock(); - //clear the connection state - disconnect(); - - //establish a new connection to ZooKeeper - mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), - zkWatcher, - m_zkConfig.getLeaseTimeout(), - NULL, this, 0); - resetRemainingConnectTimeout(); - if (mp_zkHandle != NULL) { - setState( AS_CONNECTING ); - m_stateLock.unlock(); - } else { - m_stateLock.unlock(); - throw ZooKeeperException( - string("Unable to connect to ZK running at '") + - m_zkConfig.getHosts() + "'" ); - } - - LOG_DEBUG( LOG, "mp_zkHandle: %p, state %d", mp_zkHandle, m_state ); -} - -void -ZooKeeperAdapter::handleEvent(int type, int state, const string &path) -{ - TRACE( LOG, "handleEvent" ); - LOG_TRACE( LOG, - "type: %d, state %d, path: %s", - type, state, path.c_str() ); - Listener2Context context, context2; - //ignore internal ZK events - if (type != ZOO_SESSION_EVENT && type != ZOO_NOTWATCHING_EVENT) { - m_zkContextsMutex.Acquire(); - //check if the user context is available - if (type == ZOO_CHANGED_EVENT || type == ZOO_DELETED_EVENT) { - //we may have two types of interest here, - //in this case lets try to notify twice - context = findAndRemoveListenerContext( GET_NODE_DATA, path ); - context2 = findAndRemoveListenerContext( NODE_EXISTS, path ); - if (context.empty()) { - //make sure that the 2nd context is NULL and - // assign it to the 1st one - context = context2; - context2.clear(); - } - } else if (type == ZOO_CHILD_EVENT) { - context = findAndRemoveListenerContext( GET_NODE_CHILDREN, path ); - } else if (type == ZOO_CREATED_EVENT) { - context = findAndRemoveListenerContext( NODE_EXISTS, path ); - } - m_zkContextsMutex.Release(); - } - - handleEvent( type, state, path, context ); - if (!context2.empty()) { - handleEvent( type, state, path, context2 ); - } -} - -void -ZooKeeperAdapter::handleEvent(int type, - int state, - const string &path, - const Listener2Context &listeners) -{ - TRACE( LOG, "handleEvents" ); - - if (listeners.empty()) { - //propagate with empty context - ZKWatcherEvent event(type, state, path); - fireEvent( event ); - } else { - for (Listener2Context::const_iterator i = listeners.begin(); - i != listeners.end(); - ++i) { - ZKWatcherEvent event(type, state, path, i->second); - if (i->first != NULL) { - fireEvent( i->first, event ); - } else { - fireEvent( event ); - } - } - } -} - -void -ZooKeeperAdapter::enqueueEvent(int type, int state, const string &path) -{ - TRACE( LOG, "enqueueEvents" ); - - m_events.put( ZKWatcherEvent( type, state, path ) ); -} - -void -ZooKeeperAdapter::processEvents() -{ - TRACE( LOG, "processEvents" ); - - while (!m_terminating) { - bool timedOut = false; - ZKWatcherEvent source = m_events.take( 100, &timedOut ); - if (!timedOut) { - if (source.getType() == ZOO_SESSION_EVENT) { - LOG_INFO( LOG, - "Received SESSION event, state: %d. Adapter state: %d", - source.getState(), m_state ); - m_stateLock.lock(); - if (source.getState() == ZOO_CONNECTED_STATE) { - m_connected = true; - resetRemainingConnectTimeout(); - setState( AS_CONNECTED ); - } else if (source.getState() == ZOO_CONNECTING_STATE) { - m_connected = false; - setState( AS_CONNECTING ); - } else if (source.getState() == ZOO_EXPIRED_SESSION_STATE) { - LOG_INFO( LOG, "Received EXPIRED_SESSION event" ); - setState( AS_SESSION_EXPIRED ); - } - m_stateLock.unlock(); - } - m_userEvents.put( source ); - } - } -} - -void -ZooKeeperAdapter::processUserEvents() -{ - TRACE( LOG, "processUserEvents" ); - - while (!m_terminating) { - bool timedOut = false; - ZKWatcherEvent source = m_userEvents.take( 100, &timedOut ); - if (!timedOut) { - try { - handleEvent( source.getType(), - source.getState(), - source.getPath() ); - } catch (std::exception &e) { - LOG_ERROR( LOG, - "Unable to process event (type: %d, state: %d, " - "path: %s), because of exception: %s", - source.getType(), - source.getState(), - source.getPath().c_str(), - e.what() ); - } - } - } -} - -void -ZooKeeperAdapter::registerContext(WatchableMethod method, - const string &path, - ZKEventListener *listener, - ContextType context) -{ - TRACE( LOG, "registerContext" ); - - m_zkContexts[method][path][listener] = context; -} - -ZooKeeperAdapter::Listener2Context -ZooKeeperAdapter::findAndRemoveListenerContext(WatchableMethod method, - const string &path) -{ - TRACE( LOG, "findAndRemoveListenerContext" ); - - Listener2Context listeners; - Path2Listener2Context::iterator elem = m_zkContexts[method].find( path ); - if (elem != m_zkContexts[method].end()) { - listeners = elem->second; - m_zkContexts[method].erase( elem ); - } - return listeners; -} - -void -ZooKeeperAdapter::setState(AdapterState newState) -{ - TRACE( LOG, "setState" ); - if (newState != m_state) { - LOG_INFO( LOG, "Adapter state transition: %d -> %d", m_state, newState ); - m_state = newState; - m_stateLock.notify(); - } else { - LOG_TRACE( LOG, "New state same as the current: %d", newState ); - } -} - - -//TODO move this code to verifyConnection so reconnect() -//is called from one place only -void -ZooKeeperAdapter::waitUntilConnected() - throw(ZooKeeperException) -{ - TRACE( LOG, "waitUntilConnected" ); - long long int timeout = getRemainingConnectTimeout(); - LOG_INFO( LOG, - "Waiting up to %lld ms until a connection to ZK is established", - timeout ); - bool connected; - if (timeout > 0) { - long long int toWait = timeout; - while (m_state != AS_CONNECTED && toWait > 0) { - //check if session expired and reconnect if so - if (m_state == AS_SESSION_EXPIRED) { - LOG_INFO( LOG, - "Reconnecting because the current session has expired" ); - reconnect(); - } - struct timeval now; - gettimeofday( &now, NULL ); - int64_t milliSecs = -(now.tv_sec * 1000LL + now.tv_usec / 1000); - LOG_TRACE( LOG, "About to wait %lld ms", toWait ); - m_stateLock.wait( toWait ); - gettimeofday( &now, NULL ); - milliSecs += now.tv_sec * 1000LL + now.tv_usec / 1000; - toWait -= milliSecs; - } - waitedForConnect( timeout - toWait ); - LOG_INFO( LOG, "Waited %lld ms", timeout - toWait ); - } - connected = (m_state == AS_CONNECTED); - if (!connected) { - if (timeout > 0) { - LOG_WARN( LOG, "Timed out while waiting for connection to ZK" ); - throw ZooKeeperException("Timed out while waiting for " - "connection to ZK"); - } else { - LOG_ERROR( LOG, "Global timeout expired and still not connected to ZK" ); - throw ZooKeeperException("Global timeout expired and still not " - "connected to ZK"); - } - } - LOG_INFO( LOG, "Connected!" ); -} - -void -ZooKeeperAdapter::verifyConnection() throw(ZooKeeperException) -{ - TRACE( LOG, "verifyConnection" ); - - m_stateLock.lock(); - try { - if (m_state == AS_DISCONNECTED) { - throw ZooKeeperException("Disconnected from ZK. " \ - "Please use reconnect() before attempting to use any ZK API"); - } else if (m_state != AS_CONNECTED) { - LOG_TRACE( LOG, "Checking if need to reconnect..." ); - //we are not connected, so check if connection in progress... - if (m_state != AS_CONNECTING) { - LOG_TRACE( LOG, - "yes. Checking if allowed to auto-reconnect..." ); - //...not in progres, so check if we can reconnect - if (!m_zkConfig.getAutoReconnect()) { - //...too bad, disallowed :( - LOG_TRACE( LOG, "no. Sorry." ); - throw ZooKeeperException("ZK connection is down and " - "auto-reconnect is not allowed"); - } else { - LOG_TRACE( LOG, "...yes. About to reconnect" ); - } - //...we are good to retry the connection - reconnect(); - } else { - LOG_TRACE( LOG, "...no, already in CONNECTING state" ); - } - //wait until the connection is established - waitUntilConnected(); - } - } catch (ZooKeeperException &e) { - m_stateLock.unlock(); - throw; - } - m_stateLock.unlock(); -} - -bool -ZooKeeperAdapter::createNode(const string &path, - const string &value, - int flags, - bool createAncestors, - string &returnPath) - throw(ZooKeeperException) -{ - TRACE( LOG, "createNode (internal)" ); - validatePath( path ); - - const int MAX_PATH_LENGTH = 1024; - char realPath[MAX_PATH_LENGTH]; - realPath[0] = 0; - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - rc = zoo_create( mp_zkHandle, - path.c_str(), - value.c_str(), - value.length(), - &ZOO_OPEN_ACL_UNSAFE, - flags, - realPath, - MAX_PATH_LENGTH ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) { - if (rc == ZNODEEXISTS) { - //the node already exists - LOG_WARN( LOG, "Error %d for %s", rc, path.c_str() ); - return false; - } else if (rc == ZNONODE && createAncestors) { - LOG_WARN( LOG, "Error %d for %s", rc, path.c_str() ); - //one of the ancestors doesn't exist so lets start from the root - //and make sure the whole path exists, creating missing nodes if - //necessary - for (string::size_type pos = 1; pos != string::npos; ) { - pos = path.find( "/", pos ); - if (pos != string::npos) { - try { - createNode( path.substr( 0, pos ), "", 0, true ); - } catch (ZooKeeperException &e) { - throw ZooKeeperException( string("Unable to create " - "node ") + - path, - rc ); - } - pos++; - } else { - //no more path components - return createNode( path, value, flags, false, returnPath ); - } - } - } - LOG_ERROR( LOG,"Error %d for %s", rc, path.c_str() ); - throw ZooKeeperException( string("Unable to create node ") + - path, - rc ); - } else { - LOG_INFO( LOG, "%s has been created", realPath ); - returnPath = string( realPath ); - return true; - } -} - -bool -ZooKeeperAdapter::createNode(const string &path, - const string &value, - int flags, - bool createAncestors) - throw(ZooKeeperException) -{ - TRACE( LOG, "createNode" ); - - string createdPath; - return createNode( path, value, flags, createAncestors, createdPath ); -} - -int64_t -ZooKeeperAdapter::createSequence(const string &path, - const string &value, - int flags, - bool createAncestors) - throw(ZooKeeperException) -{ - TRACE( LOG, "createSequence" ); - - string createdPath; - bool result = createNode( path, - value, - flags | ZOO_SEQUENCE, - createAncestors, - createdPath ); - if (!result) { - return -1; - } else { - //extract sequence number from the returned path - if (createdPath.find( path ) != 0) { - throw ZooKeeperException( string("Expecting returned path '") + - createdPath + - "' to start with '" + - path + - "'" ); - } - string seqSuffix = - createdPath.substr( path.length(), - createdPath.length() - path.length() ); - char *ptr = NULL; - int64_t seq = strtol( seqSuffix.c_str(), &ptr, 10 ); - if (ptr != NULL && *ptr != '\0') { - throw ZooKeeperException( string("Expecting a number but got ") + - seqSuffix ); - } - return seq; - } -} - -bool -ZooKeeperAdapter::deleteNode(const string &path, - bool recursive, - int version) - throw(ZooKeeperException) -{ - TRACE( LOG, "deleteNode" ); - - validatePath( path ); - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - rc = zoo_delete( mp_zkHandle, path.c_str(), version ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) { - if (rc == ZNONODE) { - LOG_WARN( LOG, "Error %d for %s", rc, path.c_str() ); - return false; - } - if (rc == ZNOTEMPTY && recursive) { - LOG_WARN( LOG, "Error %d for %s", rc, path.c_str() ); - //get all children and delete them recursively... - vector nodeList; - getNodeChildren( nodeList, path, false ); - for (vector::const_iterator i = nodeList.begin(); - i != nodeList.end(); - ++i) { - deleteNode( *i, true ); - } - //...and finally attempt to delete the node again - return deleteNode( path, false ); - } - LOG_ERROR( LOG, "Error %d for %s", rc, path.c_str() ); - throw ZooKeeperException( string("Unable to delete node ") + path, - rc ); - } else { - LOG_INFO( LOG, "%s has been deleted", path.c_str() ); - return true; - } -} - -bool -ZooKeeperAdapter::nodeExists(const string &path, - ZKEventListener *listener, - void *context, Stat *stat) - throw(ZooKeeperException) -{ - TRACE( LOG, "nodeExists" ); - - validatePath( path ); - - struct Stat tmpStat; - if (stat == NULL) { - stat = &tmpStat; - } - memset( stat, 0, sizeof(Stat) ); - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - if (context != NULL) { - m_zkContextsMutex.Acquire(); - rc = zoo_exists( mp_zkHandle, - path.c_str(), - (listener != NULL ? 1 : 0), - stat ); - if (rc == ZOK || rc == ZNONODE) { - registerContext( NODE_EXISTS, path, listener, context ); - } - m_zkContextsMutex.Release(); - } else { - rc = zoo_exists( mp_zkHandle, - path.c_str(), - (listener != NULL ? 1 : 0), - stat ); - } - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) { - if (rc == ZNONODE) { - LOG_TRACE( LOG, "Node %s does not exist", path.c_str() ); - return false; - } - LOG_ERROR( LOG, "Error %d for %s", rc, path.c_str() ); - throw ZooKeeperException( - string("Unable to check existence of node ") + path, - rc ); - } else { - return true; - } -} - -void -ZooKeeperAdapter::getNodeChildren(vector &nodeList, - const string &path, - ZKEventListener *listener, - void *context) - throw (ZooKeeperException) -{ - TRACE( LOG, "getNodeChildren" ); - - validatePath( path ); - - String_vector children; - memset( &children, 0, sizeof(children) ); - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - if (context != NULL) { - m_zkContextsMutex.Acquire(); - rc = zoo_get_children( mp_zkHandle, - path.c_str(), - (listener != NULL ? 1 : 0), - &children ); - if (rc == ZOK) { - registerContext( GET_NODE_CHILDREN, path, listener, context ); - } - m_zkContextsMutex.Release(); - } else { - rc = zoo_get_children( mp_zkHandle, - path.c_str(), - (listener != NULL ? 1 : 0), - &children ); - } - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) { - LOG_ERROR( LOG, "Error %d for %s", rc, path.c_str() ); - throw ZooKeeperException( string("Unable to get children of node ") + - path, - rc ); - } else { - for (int i = 0; i < children.count; ++i) { - //convert each child's path from relative to absolute - string absPath(path); - if (path != "/") { - absPath.append( "/" ); - } - absPath.append( children.data[i] ); - nodeList.push_back( absPath ); - } - //make sure the order is always deterministic - sort( nodeList.begin(), nodeList.end() ); - } -} - -string -ZooKeeperAdapter::getNodeData(const string &path, - ZKEventListener *listener, - void *context, Stat *stat) - throw(ZooKeeperException) -{ - TRACE( LOG, "getNodeData" ); - - validatePath( path ); - - const int MAX_DATA_LENGTH = 128 * 1024; - char buffer[MAX_DATA_LENGTH]; - memset( buffer, 0, MAX_DATA_LENGTH ); - struct Stat tmpStat; - if (stat == NULL) { - stat = &tmpStat; - } - memset( stat, 0, sizeof(Stat) ); - - int rc; - int len; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - len = MAX_DATA_LENGTH - 1; - if (context != NULL) { - m_zkContextsMutex.Acquire(); - rc = zoo_get( mp_zkHandle, - path.c_str(), - (listener != NULL ? 1 : 0), - buffer, &len, stat ); - if (rc == ZOK) { - registerContext( GET_NODE_DATA, path, listener, context ); - } - m_zkContextsMutex.Release(); - } else { - rc = zoo_get( mp_zkHandle, - path.c_str(), - (listener != NULL ? 1 : 0), - buffer, &len, stat ); - } - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) { - LOG_ERROR( LOG, "Error %d for %s", rc, path.c_str() ); - throw ZooKeeperException( - string("Unable to get data of node ") + path, rc - ); - } else { - return string( buffer, buffer + len ); - } -} - -void -ZooKeeperAdapter::setNodeData(const string &path, - const string &value, - int version) - throw(ZooKeeperException) -{ - TRACE( LOG, "setNodeData" ); - - validatePath( path ); - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - rc = zoo_set( mp_zkHandle, - path.c_str(), - value.c_str(), - value.length(), - version); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) { - LOG_ERROR( LOG, "Error %d for %s", rc, path.c_str() ); - throw ZooKeeperException( string("Unable to set data for node ") + - path, - rc ); - } -} - -} /* end of 'namespace zk' */ - diff --git a/src/contrib/zkfuse/src/zkadapter.h b/src/contrib/zkfuse/src/zkadapter.h deleted file mode 100644 index 8d4d1d57f0a..00000000000 --- a/src/contrib/zkfuse/src/zkadapter.h +++ /dev/null @@ -1,718 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ZKADAPTER_H__ -#define __ZKADAPTER_H__ - -#include -#include -#include - -extern "C" { -#include "zookeeper.h" -} - -#include "log.h" -#include "mutex.h" -#include "thread.h" -#include "blockingqueue.h" -#include "event.h" - -using namespace std; -using namespace zkfuse; - -namespace zk { - -/** - * \brief A cluster related exception. - */ -class ZooKeeperException : - public std::exception -{ - public: - - /** - * \brief Constructor. - * - * @param msg the detailed message associated with this exception - */ - ZooKeeperException(const string &msg) : - m_message(msg), m_zkErrorCode(0) - {} - - /** - * \brief Constructor. - * - * @param msg the detailed message associated with this exception - * @param errorCode the ZK error code associated with this exception - */ - ZooKeeperException(const string &msg, int errorCode) : - m_zkErrorCode(errorCode) - { - char tmp[100]; - sprintf( tmp, " (ZK error code: %d)", errorCode ); - m_message = msg + tmp; - } - - /** - * \brief Destructor. - */ - ~ZooKeeperException() throw() {} - - /** - * \brief Returns detailed description of the exception. - */ - const char *what() const throw() { - return m_message.c_str(); - } - - /** - * \brief Returns the ZK error code. - */ - int getZKErrorCode() const { - return m_zkErrorCode; - } - - private: - - /** - * The detailed message associated with this exception. - */ - string m_message; - - /** - * The optional error code received from ZK. - */ - int m_zkErrorCode; - -}; - -/** - * \brief This class encapsulates configuration of a ZK client. - */ -class ZooKeeperConfig -{ - public: - - /** - * \brief Constructor. - * - * @param hosts the comma separated list of host and port pairs of ZK nodes - * @param leaseTimeout the lease timeout (heartbeat) - * @param autoReconnect whether to allow for auto-reconnect - * @param connectTimeout the connect timeout, in milliseconds; - */ - ZooKeeperConfig(const string &hosts, - int leaseTimeout, - bool autoReconnect = true, - long long int connectTimeout = 15000) : - m_hosts(hosts), m_leaseTimeout(leaseTimeout), - m_autoReconnect(autoReconnect), m_connectTimeout(connectTimeout) {} - - /** - * \brief Returns the list of ZK hosts to connect to. - */ - string getHosts() const { return m_hosts; } - - /** - * \brief Returns the lease timeout. - */ - int getLeaseTimeout() const { return m_leaseTimeout; } - - /** - * \brief Returns whether {@link ZooKeeperAdapter} should attempt - * \brief to automatically reconnect in case of a connection failure. - */ - bool getAutoReconnect() const { return m_autoReconnect; } - - /** - * \brief Gets the connect timeout. - * - * @return the connect timeout - */ - long long int getConnectTimeout() const { return m_connectTimeout; } - - private: - - /** - * The host addresses of ZK nodes. - */ - const string m_hosts; - - /** - * The ZK lease timeout. - */ - const int m_leaseTimeout; - - /** - * True if this adapater should attempt to autoreconnect in case - * the current session has been dropped. - */ - const bool m_autoReconnect; - - /** - * How long to wait, in milliseconds, before a connection - * is established to ZK. - */ - const long long int m_connectTimeout; - -}; - -/** - * \brief A data value object representing a watcher event received from the ZK. - */ -class ZKWatcherEvent -{ - public: - - /** - * \brief The type representing the user's context. - */ - typedef void *ContextType; - - /** - * \brief Constructor. - * - * @param type the type of this event - * @param state the state of this event - * @param path the corresponding path, may be empty for some event types - * @param context the user specified context; possibly NULL - */ - ZKWatcherEvent() : - m_type(-1), m_state(-1), m_path(""), mp_context(NULL) {} - - /** - * \brief Constructor. - * - * @param type the type of this event - * @param state the state of this event - * @param path the corresponding path, may be empty for some event types - * @param context the user specified context; possibly NULL - */ - ZKWatcherEvent(int type, int state, const string &path, - ContextType context = NULL) : - m_type(type), m_state(state), m_path(path), mp_context(context) {} - - int getType() const { return m_type; } - int getState() const { return m_state; } - string const &getPath() const { return m_path; } - ContextType getContext() const { return mp_context; } - - bool operator==(const ZKWatcherEvent &we) const { - return m_type == we.m_type && m_state == we.m_state - && m_path == we.m_path && mp_context == we.mp_context; - } - - private: - - /** - * The type of this event. It can be either ZOO_CREATED_EVENT, ZOO_DELETED_EVENT, - * ZOO_CHANGED_EVENT, ZOO_CHILD_EVENT, ZOO_SESSION_EVENT or ZOO_NOTWATCHING_EVENT. - * See zookeeper.h for more details. - */ - const int m_type; - - /** - * The state of ZK at the time of sending this event. - * It can be either ZOO_CONNECTING_STATE, ZOO_ASSOCIATING_STATE, - * ZOO_CONNECTED_STATE, ZOO_EXPIRED_SESSION_STATE or AUTH_FAILED_STATE. - * See {@file zookeeper.h} for more details. - */ - const int m_state; - - /** - * The corresponding path of the node in subject. It may be empty - * for some event types. - */ - const string m_path; - - /** - * The pointer to the user specified context, possibly NULL. - */ - ContextType mp_context; - -}; - -/** - * \brief The type definition of ZK event source. - */ -typedef EventSource ZKEventSource; - -/** - * \brief The type definition of ZK event listener. - */ -typedef EventListener ZKEventListener; - -/** - * \brief This is a wrapper around ZK C synchrounous API. - */ -class ZooKeeperAdapter - : public ZKEventSource -{ - public: - /** - * \brief The global function that handles all ZK asynchronous notifications. - */ - friend void zkWatcher(zhandle_t *, int, int, const char *, void *watcherCtx); - - /** - * \brief The type representing the user's context. - */ - typedef void *ContextType; - - /** - * \brief The map type of ZK event listener to user specified context mapping. - */ - typedef map Listener2Context; - - /** - * \brief The map type of ZK path's to listener's contexts. - */ - typedef map Path2Listener2Context; - - /** - * \brief All possible states of this client, in respect to - * \brief connection to the ZK server. - */ - enum AdapterState { - //mp_zkHandle is NULL - AS_DISCONNECTED = 0, - //mp_zkHandle is valid but this client is reconnecting - AS_CONNECTING, - //mp_zkHandle is valid and this client is connected - AS_CONNECTED, - //mp_zkHandle is valid, however no more calls can be made to ZK API - AS_SESSION_EXPIRED - }; - - /** - * \brief Constructor. - * Attempts to create a ZK adapter, optionally connecting - * to the ZK. Note, that if the connection is to be established - * and the given listener is NULL, some events may be lost, - * as they may arrive asynchronously before this method finishes. - * - * @param config the ZK configuration - * @param listener the event listener to be used for listening - * on incoming ZK events; - * if NULL not used - * @param establishConnection whether to establish connection to the ZK - * - * @throw ZooKeeperException if cannot establish connection to the given ZK - */ - ZooKeeperAdapter(ZooKeeperConfig config, - ZKEventListener *listener = NULL, - bool establishConnection = false) - throw(ZooKeeperException); - - /** - * \brief Destructor. - */ - ~ZooKeeperAdapter(); - - /** - * \brief Returns the current config. - */ - const ZooKeeperConfig &getZooKeeperConfig() const { - return m_zkConfig; - } - - /** - * \brief Restablishes connection to the ZK. - * If this adapter is already connected, the current connection - * will be dropped and a new connection will be established. - * - * @throw ZooKeeperException if cannot establish connection to the ZK - */ - void reconnect() throw(ZooKeeperException); - - /** - * \brief Disconnects from the ZK and unregisters {@link #mp_zkHandle}. - */ - void disconnect(); - - /** - * \brief Creates a new node identified by the given path. - * This method will optionally attempt to create all missing ancestors. - * - * @param path the absolute path name of the node to be created - * @param value the initial value to be associated with the node - * @param flags the ZK flags of the node to be created - * @param createAncestors if true and there are some missing ancestor nodes, - * this method will attempt to create them - * - * @return true if the node has been successfully created; false otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool createNode(const string &path, - const string &value = "", - int flags = 0, - bool createAncestors = true) - throw(ZooKeeperException); - - /** - * \brief Creates a new sequence node using the give path as the prefix. - * This method will optionally attempt to create all missing ancestors. - * - * @param path the absolute path name of the node to be created; - * @param value the initial value to be associated with the node - * @param flags the ZK flags of the sequence node to be created - * (in addition to SEQUENCE) - * @param createAncestors if true and there are some missing ancestor - * nodes, this method will attempt to create them - * - * @return the sequence number associate with newly created node, - * or -1 if it couldn't be created - * @throw ZooKeeperException if the operation has failed - */ - int64_t createSequence(const string &path, - const string &value = "", - int flags = 0, - bool createAncestors = true) - throw(ZooKeeperException); - - /** - * \brief Deletes a node identified by the given path. - * - * @param path the absolute path name of the node to be deleted - * @param recursive if true this method will attempt to remove - * all children of the given node if any exist - * @param version the expected version of the node. The function will - * fail if the actual version of the node does not match - * the expected version - * - * @return true if the node has been deleted; false otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool deleteNode(const string &path, bool recursive = false, int version = -1) - throw(ZooKeeperException); - - /** - * \brief Checks whether the given node exists or not. - * - * @param path the absolute path name of the node to be checked - * @param listener the listener for ZK watcher events; - * passing non NULL effectively establishes - * a ZK watch on the given node - * @param context the user specified context that is to be passed - * in a corresponding {@link ZKWatcherEvent} at later time; - * not used if listener is NULL - * @param stat the optional node statistics to be filled in by ZK - * - * @return true if the given node exists; false otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool nodeExists(const string &path, - ZKEventListener *listener = NULL, - void *context = NULL, - Stat *stat = NULL) - throw(ZooKeeperException); - - /** - * \brief Retrieves list of all children of the given node. - * - * @param path the absolute path name of the node for which to get children - * @param listener the listener for ZK watcher events; - * passing non NULL effectively establishes - * a ZK watch on the given node - * @param context the user specified context that is to be passed - * in a corresponding {@link ZKWatcherEvent} at later time; - * not used if listener is NULL - * - * @return the list of absolute paths of child nodes, possibly empty - * @throw ZooKeeperException if the operation has failed - */ - void getNodeChildren(vector &children, - const string &path, - ZKEventListener *listener = NULL, - void *context = NULL) - throw(ZooKeeperException); - - /** - * \brief Gets the given node's data. - * - * @param path the absolute path name of the node to get data from - * @param listener the listener for ZK watcher events; - * passing non NULL effectively establishes - * a ZK watch on the given node - * @param context the user specified context that is to be passed - * in a corresponding {@link ZKWatcherEvent} at later time; - * not used if listener is NULL - * @param stat the optional node statistics to be filled in by ZK - * - * @return the node's data - * @throw ZooKeeperException if the operation has failed - */ - string getNodeData(const string &path, - ZKEventListener *listener = NULL, - void *context = NULL, - Stat *stat = NULL) - throw(ZooKeeperException); - - /** - * \brief Sets the given node's data. - * - * @param path the absolute path name of the node to get data from - * @param value the node's data to be set - * @param version the expected version of the node. The function will - * fail if the actual version of the node does not match - * the expected version - * - * @throw ZooKeeperException if the operation has failed - */ - void setNodeData(const string &path, const string &value, int version = -1) - throw(ZooKeeperException); - - /** - * \brief Validates the given path to a node in ZK. - * - * @param the path to be validated - * - * @throw ZooKeeperException if the given path is not valid - * (for instance it doesn't start with "/") - */ - static void validatePath(const string &path) throw(ZooKeeperException); - - /** - * Returns the current state of this adapter. - * - * @return the current state of this adapter - * @see AdapterState - */ - AdapterState getState() const { - return m_state; - } - - private: - - /** - * This enum defines methods from this class than can trigger an event. - */ - enum WatchableMethod { - NODE_EXISTS = 0, - GET_NODE_CHILDREN, - GET_NODE_DATA - }; - - /** - * \brief Creates a new node identified by the given path. - * This method is used internally to implement {@link createNode(...)} - * and {@link createSequence(...)}. On success, this method will set - * createdPath. - * - * @param path the absolute path name of the node to be created - * @param value the initial value to be associated with the node - * @param flags the ZK flags of the node to be created - * @param createAncestors if true and there are some missing ancestor nodes, - * this method will attempt to create them - * @param createdPath the actual path of the node that has been created; - * useful for sequences - * - * @return true if the node has been successfully created; false otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool createNode(const string &path, - const string &value, - int flags, - bool createAncestors, - string &createdPath) - throw(ZooKeeperException); - - /** - * Handles an asynchronous event received from the ZK. - */ - void handleEvent(int type, int state, const string &path); - - /** - * Handles an asynchronous event received from the ZK. - * This method iterates over all listeners and passes the event - * to each of them. - */ - void handleEvent(int type, int state, const string &path, - const Listener2Context &listeners); - - /** - * \brief Enqueues the given event in {@link #m_events} queue. - */ - void enqueueEvent(int type, int state, const string &path); - - /** - * \brief Processes all ZK adapter events in a loop. - */ - void processEvents(); - - /** - * \brief Processes all user events in a loop. - */ - void processUserEvents(); - - /** - * \brief Registers the given context in the {@link #m_zkContexts} - * \brief contexts map. - * - * @param method the method where the given path is being used - * @param path the path of interest - * @param listener the event listener to call back later on - * @param context the user specified context to be passed back to user - */ - void registerContext(WatchableMethod method, const string &path, - ZKEventListener *listener, ContextType context); - - /** - * \brief Attempts to find a listener to context map in the contexts' - * \brief map, based on the specified criteria. - * If the context is found, it will be removed the udnerlying map. - * - * @param method the method type identify Listener2Context map - * @param path the path to be used to search in the Listener2Context map - * - * @return the context map associated with the given method and path, - * or empty map if not found - */ - Listener2Context findAndRemoveListenerContext(WatchableMethod method, - const string &path); - - /** - * Sets the new state in case it's different then the current one. - * This method assumes that {@link #m_stateLock} has been already locked. - * - * @param newState the new state to be set - */ - void setState(AdapterState newState); - - /** - * Waits until this client gets connected. The total wait time - * is given by {@link getRemainingConnectTimeout()}. - * If a timeout elapses, this method will throw an exception. - * - * @throw ZooKeeperException if unable to connect within the given timeout - */ - void waitUntilConnected() - throw(ZooKeeperException); - - /** - * Verifies whether the connection is established, - * optionally auto reconnecting. - * - * @throw ZooKeeperConnection if this client is disconnected - * and auto-reconnect failed or was not allowed - */ - void verifyConnection() throw(ZooKeeperException); - - /** - * Returns the remaining connect timeout. The timeout resets - * to {@link #m_connectTimeout} on a successfull connection to the ZK. - * - * @return the remaining connect timeout, in milliseconds - */ - long long int getRemainingConnectTimeout() { - return m_remainingConnectTimeout; - } - - /** - * Resets the remaining connect timeout to {@link #m_connectTimeout}. - */ - void resetRemainingConnectTimeout() { - m_remainingConnectTimeout = m_zkConfig.getConnectTimeout(); - } - - /** - * Updates the remaining connect timeout to reflect the given wait time. - * - * @param time the time for how long waited so far on connect to succeed - */ - void waitedForConnect(long long time) { - m_remainingConnectTimeout -= time; - } - - private: - - /** - * The mutex use to protect {@link #m_zkContexts}. - */ - zkfuse::Mutex m_zkContextsMutex; - - /** - * The map of registered ZK paths that are being watched. - * Each entry maps a function type to another map of registered contexts. - * - * @see WatchableMethod - */ - map m_zkContexts; - - /** - * The current ZK configuration. - */ - const ZooKeeperConfig m_zkConfig; - - /** - * The current ZK session. - */ - zhandle_t *mp_zkHandle; - - /** - * The blocking queue of all events waiting to be processed by ZK adapter. - */ - BlockingQueue m_events; - - /** - * The blocking queue of all events waiting to be processed by users - * of ZK adapter. - */ - BlockingQueue m_userEvents; - - /** - * The thread that dispatches all events from {@link #m_events} queue. - */ - CXXThread m_eventDispatcher; - - /** - * The thread that dispatches all events from {@link #m_userEvents} queue. - */ - CXXThread m_userEventDispatcher; - - /** - * Whether {@link #m_eventDispatcher} is terminating. - */ - volatile bool m_terminating; - - /** - * Whether this adapter is connected to the ZK. - */ - volatile bool m_connected; - - /** - * The state of this adapter. - */ - AdapterState m_state; - - /** - * The lock used to synchronize access to {@link #m_state}. - */ - Lock m_stateLock; - - /** - * How much time left for the connect to succeed, in milliseconds. - */ - long long int m_remainingConnectTimeout; - -}; - -} /* end of 'namespace zk' */ - -#endif /* __ZKADAPTER_H__ */ diff --git a/src/contrib/zkfuse/src/zkfuse.cc b/src/contrib/zkfuse/src/zkfuse.cc deleted file mode 100644 index bcf662bd582..00000000000 --- a/src/contrib/zkfuse/src/zkfuse.cc +++ /dev/null @@ -1,4492 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define FUSE_USE_VERSION 26 - -#ifdef HAVE_CONFIG_H -#include -#endif - -#undef _GNU_SOURCE -#define _GNU_SOURCE - -extern "C" { -#include -#include -} -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SETXATTR -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "mutex.h" -#include "zkadapter.h" - -#define ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - -/** - Typedef for ZooKeeperAdapter::Data. -*/ -typedef std::string Data; -/** - Typedef for ZooKeeperAdapter::NodeNames. -*/ -typedef vector NodeNames; - -#define MAX_DATA_SIZE 1024; - -DEFINE_LOGGER(LOG, "zkfuse"); - -inline -uint64_t millisecsToSecs(uint64_t millisecs) -{ - return millisecs / 1000; -} -inline -uint64_t secsToMillisecs(uint64_t secs) -{ - return secs * 1000; -} -inline -uint64_t nanosecsToMillisecs(uint64_t nanosecs) -{ - return nanosecs * 1000000; -} -inline -uint64_t timespecToMillisecs(const struct timespec & ts) -{ - return secsToMillisecs(ts.tv_sec) + nanosecsToMillisecs(ts.tv_nsec); -} - -typedef boost::shared_ptr ZooKeeperAdapterSharedPtr; - -/** - * ZkFuseCommon - holds immutable configuration objects. - * - * No locks are required to access these objects. - * A ZkFuseCommon instance is considered to be a data object and may be copied. - */ -class ZkFuseCommon -{ - private: - /** - References the ZooKeeperAdapter instance to be used. - */ - ZooKeeperAdapterSharedPtr _zkAdapter; - /** - Path to the ZooKeeper root node. - */ - std::string _rootPathName; - /** - Name used to access data "file" when the ZK node has - children. - */ - std::string _dataFileName; - /** - Suffix added to path components to force interpretation of - path components as directory. This is usually only required - for the last component. For example, ZkFuse may consider - a leaf node a regular file, e.g. /a/b/c/leaf. The suffix - can be used to create child under this node, e.g. - mkdir /a/b/c/leaf{forceDirSuffix}/new_leaf. - */ - std::string _forceDirSuffix; - /** - Prefix common to all metadata nodes created by ZkFuse. - */ - std::string _metadataNamePrefix; - /** - Path component name that identifies a directory metadata node. - A directory metadata node is currently empty. It is used by ZkFuse - to create a child when mkdir is used. This prevents ZkFuse - from interpreting the new child as a regular file. - */ - std::string _dirMetadataName; - /** - Path component name that identifies a regular file metadata node. - A regular metadata node holds metadata required to implement - Posix regular file semantics, such as setting mtime. - */ - std::string _regMetadataName; - /** - Number of not-in-use nodes to cache. - */ - unsigned _cacheSize; - /** - Assume this userid owns all nodes. - */ - const uid_t _uid; - /** - Assume this groupid owns all nodes. - */ - const gid_t _gid; - /** - Blocksize used to calculate number of blocks used for stat. - */ - const unsigned _blkSize; - - public: - /** - Constructor. - */ - ZkFuseCommon() - : _zkAdapter(), - _rootPathName("/"), - _dataFileName(), - _forceDirSuffix(), - _metadataNamePrefix(".zkfuse."), - _dirMetadataName(_metadataNamePrefix + "dir"), - _regMetadataName(_metadataNamePrefix + "file"), - _cacheSize(256), - _uid(geteuid()), - _gid(getegid()), - _blkSize(8192) - { - } - /** - Get root path name. Always "/". - \see _rootPathName - */ - const std::string & getRootPathName() const - { - return _rootPathName; - } - /** - Get dataFileName - the name for synthesized files to access - ZooKeeper node data. - \see _dataFileName - */ - const std::string & getDataFileName() const - { - return _dataFileName; - } - /** - Set dataFileName. - \see getDataFileName - \see _dataFileName - */ - void setDataFileName(const std::string & dataFileName) - { - _dataFileName = dataFileName; - } - /** - Get metadataNamePrefix - the common prefix for all ZkFuse created - metadata ZooKeeper nodes. - \see _metadataNamePrefix - */ - const std::string & getMetadataNamePrefix() const - { - return _metadataNamePrefix; - } - /** - Get forceDirSuffix - the suffix added to a path component to force - the path component to be treated like a directory. - \see _forceDirSuffix - */ - const std::string & getForceDirSuffix() const - { - return _forceDirSuffix; - } - /** - Set forceDirSuffix. - \see getForceDirSuffix - \see _forceDirSuffix - */ - void setForceDirSuffix(const std::string & forceDirSuffix) - { - _forceDirSuffix = forceDirSuffix; - } - /** - Get dirMetadataName - path component name of all directory - metadata ZooKeeper nodes. - \see _dirMetadataname - */ - const std::string & getDirMetadataName() const - { - return _dirMetadataName; - } - /** - Get regMetadataName - path component name of all regular file - metadata ZooKeeper nodes. - \see _regMetadataname - */ - const std::string & getRegMetadataName() const - { - return _regMetadataName; - } - /** - Get number of not-in-use ZkFuseFile instances to to cache. - \see _cacheSize - */ - unsigned getCacheSize() const - { - return _cacheSize; - } - /** - Set cache size. - \see getCacheSize - \see _cacheSize - */ - void setCacheSize(unsigned v) - { - _cacheSize = v; - } - /** - Get userid. - \see _uid - */ - uid_t getUid() const - { - return _uid; - } - /** - Get groupid. - \see _gid - */ - gid_t getGid() const - { - return _gid; - } - /** - Get block size. - \see _blkSize - */ - unsigned getBlkSize() const - { - return _blkSize; - } - /** - Get ZooKeeperAdapter. - \see _zkAdapter. - */ - const ZooKeeperAdapterSharedPtr & getZkAdapter() const - { - return _zkAdapter; - } - /** - Set ZooKeeperAdapter. - \see _zkAdaptor - */ - void setZkAdapter(const ZooKeeperAdapterSharedPtr & zkAdapter) - { - _zkAdapter = zkAdapter; - } -}; - -/** - ZkFuseNameType - identifies the type of the ZkFuse path. - */ -enum ZkFuseNameType { - /** - ZkFuse path is not syntheiszed. - ZkFuse should use its default rules to determine the Posix representation - of the path. - */ - ZkFuseNameDefaultType = 0, - /** - ZkFuse path is synthesized and identifies the data part of a - ZooKeeper node, i.e. Posix regular file semantics is expected. - */ - ZkFuseNameRegType = 1, - /** - ZkFuse path is synthesized and identifies the chidlren part of a - ZooKeeper node, i.e. Posix directory semantics is expected. - */ - ZkFuseNameDirType = 2 -}; - -class ZkFuseFile; - -typedef ZkFuseFile * ZkFuseFilePtr; - -class ZkFuseHandleManagerFactory; - -/** - ZkFuseHandleManager - keeps track of all the ZkFuseFile instances - allocated by a ZkFuseHandleManager instance and provides them - with a handle that can be used by FUSE. - - It maps a ZooKeeper path to a handle and a handle to a ZkFuse instance. - It also implements the methods that takes path names as arguments, such - as open, mknod, rmdir, and rename. - - Memory management - - References ZkFuseFile instances using regular pointers - Smart pointer is not used because reference counts are needed to - determine how many time a node is opened as a regular file or - directory. This also avoids circular smart pointer references. - - Each ZkFuseFile instance holds a reference to its ZkFuseHandleManager - using a boost::shared_ptr. This ensures that the ZkFuseHandleManager - instance that has the handle for the ZkFuseFile instance does not - get garbage collected while the ZkFuseFile instance exists. - - Concurrency control - - Except for the immutable ZkFuseCommon, all other member variables - are protected by _mutex. - - A method in this class can hold _mutex when it directly or - indirectly invokes ZkFuseFile methods. A ZkFuseFile method that holds - a ZkFuseFile instance _mutex cannot invoke a ZkFuseHandleManager - method that acquires the ZkFuseHandleManager instance's _mutex. - Otherwise, this may cause a dead lock. - - Methods that with names that begin with "_" do not acquire _mutex. - They are usually called by public methods that acquire and hold _mutex. - */ -class ZkFuseHandleManager : boost::noncopyable -{ - private: - /** - Typedef of handle, which is an int. - */ - typedef int Handle; - /** - Typedef of std::map used to map path to handle. - */ - typedef std::map Map; - /** - Typedef of std::vector used to map handle to ZkFuseFile instances. - */ - typedef std::vector Files; - /** - Typedef of std::vector used to hold unused handles. - */ - typedef std::vector FreeList; - /** - Typedef of boost::weak_ptr to the ZkFuseHandleManager instance. - */ - typedef boost::weak_ptr WeakPtr; - - /* Only ZkFuseHandleManagerFactory can create instances of this class */ - friend class ZkFuseHandleManagerFactory; - - /** - Contains common configuration. - Immutable so that it can be accessed without locks. - */ - const ZkFuseCommon _common; - /** - Maps a path name to a Handle. - */ - Map _map; - /** - Maps a handle to a ZkFuseFile instances. - Also holds pointers to all known ZkFuseFile instances. - An element may point to an allocated ZkFuseFile instance or be NULL. - - An allocated ZkFuseFile instance may be in one of the following states: - - in-use - Currently open, i.e. the ZkFuseFile instance's reference count - greater than 0. - - in-cache - Not currently open, i.e. the ZkFuseFile instances's - reference count is 0. - */ - Files _files; - /** - List of free'ed handles. - */ - FreeList _freeList; - /** - Mutex used to protect this instance. - */ - mutable zkfuse::Mutex _mutex; - /** - Count of number of in-use entries. - It used to calculate number of cached nodes. - Number cached nodes is (_files.size() - _numInUse). - */ - unsigned _numInUse; - /** - WeakPtr to myself. - */ - WeakPtr _thisWeakPtr; - - /** - Obtain a handle for the given path. - - If path is not known, then allocate a new handle and increment - _numInUse, and set newFile to true. The allocated - ZkFuseFile instance's reference count should be 1. - - If path is known, increase the corresponding - ZkFuseFile instance's reference count. - - \return the allocated handle. - \param path the path to lookup. - \param newFile indicates whether a new handle has been allocated. - */ - Handle allocate(const std::string & path, bool & newFile); - - /** - Constructor. - - \param common the immutable common configuration. - \param reserve number of elements to pre-allocate for - _files and _freeList. - */ - ZkFuseHandleManager( - const ZkFuseCommon & common, - const unsigned reserve) - : _common(common), - _files(), - _freeList(), - _mutex(), - _numInUse(0) - { - _files.reserve(reserve); - _files[0] = NULL; /* 0 never allocated */ - _files.resize(1); - _freeList.reserve(reserve); - } - - public: - /** - Typedef for boost::shared_ptr for this ZkFuseHandleManager class. - */ - typedef boost::shared_ptr SharedPtr; - - /** - Destructor. - */ - ~ZkFuseHandleManager() - { - } - /** - Get the ZkFuseFile instance for a handle. - - \return the ZkFuseFile instance identified by the handle. - \param handle get ZkFuseFile instance for this handle. - */ - ZkFuseFilePtr getFile(Handle handle) const - { - AutoLock lock(_mutex); - return _files[handle]; - } - /** - Get the immutable common configuration. - - \return the common configuration instance. - */ - const ZkFuseCommon & getCommon() const - { - return _common; - } - /** - Deallocate a previously allocated handle. - This decrements the reference count of the corresponding - ZkFuseFile instance. If the reference count becomes zero, - decrement _numInUse. It may also cause the ZkFuseFile instance - to be reclaimed if there are too many cached ZkFuseFile instances. - - The ZkFuseFile instance should be reclaimed if the number of - unused ZkFuseFile instances exceeds the configured cache size, i.e. - (_files.size() - _numInUse) > _common.getCacheSize() - and the ZkFuseFile instance has a reference count of zero. - - Reclaiming a ZkFuseFile instance involves removing the ZkFuseFile - instance's path to handle mapping from _map and the handle to the - ZkFuseFile instance mapping from _files, adding the handle to - the _freeList, and finally deleting the ZkFuseFile instance. - - \param handle the handle that should be deallocated. - */ - void deallocate(Handle handle); - /** - Handles ZooKeeper session events. - It invokes the known ZkFuseFile instances to let them know - that their watches will no longer be valid. - */ - void eventReceived(const ZKWatcherEvent & event); - /** - Get data from the specified the ZooKeeper path. - - \return 0 if successful, otherwise return negative errno. - \param path the path of the ZooKeeper node. - \param data return data read. - */ - int getData(const std::string & path, Data & data); - /** - Set data into the specified ZooKeeper path. - - \return 0 if successful, otherwise return negative errno. - \param path the path of the ZooKeeper node. - \param data the data to be written. - \param exists set to true if this path exists. - \param doFlush set to true if new data should be flushed to ZooKeeper. - */ - int setData(const std::string & path, - const Data & data, - bool exists, - bool doFlush); - /** - Create a ZooKeeper node to represent a ZkFuse file or directory. - - \return handle if successful, otherwise return negative errno. - \param path to create. - \param mode should be either S_IFDIR for directory or - S_IFREG for regular file. - \param mayExist if set and the ZooKeeper node already exist, return - valid handle instead of -EEXIST. - \param created returns whether a new ZooKeeper node had been created. - */ - int mknod(const std::string & path, - mode_t mode, - bool mayExist, - bool & created); - /** - Open a ZooKeeper node. - - The justCreated argument is used to differentiate if the _deleted flag - of the ZkFuseFile instance is to be trusted (i.e. the path - does not exist in ZooKeeper.) The _deleted flag is trusted - if the ZkFuseFile instance is known to exist in ZooKeeper after - invoking ZooKeeper with the path. - - If justCreated is true, then the ZkFuseFile instance was just created. - The ZkFuseFile constructor sets the _deleted flag to true because - path is not known to exist and hence should not be accessed. - The justCreated flag will force the ZkFuseFile instance to invoke - ZooKeeper to determine if the path exists. - - \return handle if successful, otherwise return negative errno. - \param path the path to open. - \param justCreated indicates if this is newly created ZkFuseFile instance. - */ - int open(const std::string & path, bool justCreated); - /** - Remove a ZkFuse directory. - - If force is not set, then the ZooKeeper node will be removed only - if it has no data and no child nodes except ZkFuse metadata nodes. - - \return 0 if successful, otherwise return negative errno. - \param path the path to remove. - \param force force removal, i.e. bypass checks. - */ - int rmdir(const char * path, bool force = false); - /** - Make a ZkFuse directory. - - ZkFuse represents a ZooKeeper node with no data and no children - as a regular file. In order to differentiate a newly created - directory from an empty regular file, mkdir will create a directory - metadata node as a child of the directory. - - \return 0 if successful, otherwise return negative errno. - \param path the path of the directory to create. - \param mode create directory with this mode - (mode currently not implemented). - */ - int mkdir(const char * path, mode_t mode); - /** - Remove a ZkFuse regular file. - - A file is the abstraction for the data part of a ZooKeeper node. - - If ZkFuse represents a ZooKeeper node as a directory, the data part - of the node is represented by synthesizing a name for this file. This - synthesized name is visible through readdir if the ZooKeeper node's - data is not empty. Removing such a file is done by truncating - the ZooKeeper node's data to 0 length. - - If ZkFuse represents a ZooKeeper node as a file, then removing the - is done by removing the ZooKeeper node (and its metadata). - - \return 0 if successful, otherwise return negative errno. - \param path the path of the file to remove. - */ - int unlink(const char * path); - /** - Get attributes of a ZkFuse regular file or directory. - - \return 0 if successful, otherwise return negative errno. - \param path get attributes for this path - \param stbuf store attributes here. - */ - int getattr(const char * path, struct stat & stbuf); - /** - Rename a ZkFuse regular file. - - It creates a new ZooKeeper node at toPath, copies data and file - metadata from the ZooKeeper node at fromPath to the new node, - and deletes the current ZooKeeper node. If the current ZooKeeper - node is not deleted if the new ZooKeeper node cannot be created - or the data copy fails. - - It cannot be used to rename a directory. - - \return 0 if successful, otherwise return negative errno. - \param fromPath the current path. - \param toPath rename to this path. - */ - int rename(const char * fromPath, const char * toPath); - /** - Add a child ZooKeeper path to the children information cache - of the ZkFuseFile instance that caches the parent ZooKeeper node. - - This is used to add a child path after a new ZooKeeper node has - been created to the children information cache of the parent - ZooKeeper node. This is needed because waiting for the children - changed event to update the cache may result in inconsistent local - views of the changes. - \see removeChildFromParent - - \parama childPath the path of the child ZooKeeper node. - */ - void addChildToParent(const std::string & childPath) const; - /** - Remove a child ZooKeeper path from the children information cache - of the ZkFuseFile instance that caches the parent ZooKeeper node. - - For example, this should happen whenever a path is deleted. - This child information cache of the parent will eventually be - invalidated by watches. However, the delivery of the children - change event may come after the next access and thus provide - the client with an inconsistent view. One example is that - client deletes the last file in a directory, but the children - changed event is not delivered before the client invokes rmdir. - to remove the parent. In this case, the rmdir fails because - the cached children information of the parent indicates the - "directory" is not empty. - - \param childPath the path of the child ZooKeeper node. - */ - void removeChildFromParent(const std::string & childPath) const; - /** - Return the path for the parent of the specified ZooKeeper path. - - \return the parent path. - \param childPath the child path. - */ - std::string getParentPath(const std::string & childPath) const; - /** - Return the ZooKeeper path from a ZkFuse path. - - The ZkFuse path may be a synthesized path. For example, a synthesized - path is required to access the data part of a ZooKeeper node's - data when ZkFuse represents the ZooKeeper node as directory. - A synthesized path is also required to create a child ZooKeeper node - under a ZooKeeper node that is represented by a regular file. - - \return the ZooKeeper path for path. - \param path the ZkFuse path, which may be a synthesized path. - \param nameType indicate whether the ZkFuse path is synthesized and - whether the synthesized ZkFuse path identifies a - directory or a regular file. - */ - std::string getZkPath(const char * path, ZkFuseNameType & nameType) const; -}; - -/** - ZkFuseHandleManagerFactory - factory for ZkFuseHandleManager. - - This is the only way to create a ZkFuseHandleManager instance. - to make sure that _thisWeakPtr of the instance is intialized - after the instance is created. - */ -class ZkFuseHandleManagerFactory -{ - public: - /** - Create an instance of ZkFuseHandleManager. - - \return the created ZkFuseHandleManager instance. - \param common the common configuration. - \param reserve initially reserve space for this number of handles. - */ - static ZkFuseHandleManager::SharedPtr create( - const ZkFuseCommon & common, - unsigned reserve = 1000) - { - ZkFuseHandleManager::SharedPtr manager - (new ZkFuseHandleManager(common, reserve)); - manager->_thisWeakPtr = manager; - return manager; - } -}; - -/** - ZkFuseAutoHandle - automatically closes handle. - - It holds an opened handle and automatically closes this handle - when it is destroyed. This enables code that open a handle - to be exception safe. - */ -class ZkFuseAutoHandle -{ - private: - /** - Typedef for Handle which is an int. - */ - typedef int Handle; - /** - Holds a reference to the ZkFuseHandlerManager instance that - allocated the handle. - */ - ZkFuseHandleManager::SharedPtr _manager; - /** - The handle that should be closed when this instance is destroyed. - A valid handle has value that is equal or greater than 0. - A negative value indicates an error condition, usually the value - is a negative errno. - */ - Handle _handle; - /** - Caches a reference to the ZkFuseFile instance with this handle. - This is a performance optimization so that _manager.getFile(_handle) - is only called once when the handle is initialized. - */ - ZkFuseFilePtr _file; - - /** - Initialize reference to the ZkFuseFile instance with this handle. - */ - void _initFile() - { - if (_handle >= 0) { - _file = _manager->getFile(_handle); - } else { - _file = NULL; - } - } - - public: - /** - Constructor - takes an previously opened handle. - - \param manager the ZkFuseHandleManager instance who allocated the handle. - \param handle the handle. - */ - ZkFuseAutoHandle( - const ZkFuseHandleManager::SharedPtr & manager, - int handle) - : _manager(manager), - _handle(handle), - _file() - { - _initFile(); - } - /** - Constructor - open path and remember handle. - - \param manager the ZkFuseHandleManager instance who allocated the handle. - \param path open this path and remember its handle in this instance. - */ - ZkFuseAutoHandle( - const ZkFuseHandleManager::SharedPtr & manager, - const std::string & path) - : _manager(manager), - _handle(_manager->open(path, false)), - _file() - { - _initFile(); - } - /** - Constructor - create path and remember handle. - - The creation mode indicates whether the path identifies a regular file - or a directory. - - \param manager the ZkFuseHandleManager instance who allocated the handle. - \param path create this path and remember its handle in this instance. - \param mode the creation mode for the path, should be either - S_IFDIR or S_IFDIR. - \param mayExist, if set and the path already exists, - then the ZkFuseAutoHandle will hold the handle - for the path instead of -EEXIST. - If not set and the path does not exist, then the handle - be -EEXIST. - */ - ZkFuseAutoHandle( - const ZkFuseHandleManager::SharedPtr & manager, - const std::string & path, - mode_t mode, - bool mayExist) - : _manager(manager), - _handle(-1), - _file() - { - bool created; - _handle = _manager->mknod(path, mode, mayExist, created); - _initFile(); - } - /** - Destructor - closes the handle. - */ - ~ZkFuseAutoHandle() - { - reset(); - } - /** - Get the handle. - \see _handle - */ - int get() const - { - return _handle; - } - /** - Get the ZkFuseFile instance of the handle. - \see _file - */ - ZkFuseFilePtr getFile() const - { - return _file; - } - /** - Forget the handle, don't close the handle. - */ - void release() - { - _handle = -1; - _file = NULL; - } - /** - Change the remembered handle. - - It will close the current handle (if valid). - */ - void reset(int handle = -1); -}; - -/** - ZkFuseStat - C++ wrapper for ZooKeeper Stat. - - This wrapper provides ZooKeeper Stat will constructors that - initializes the instance variables of Stat. - */ -class ZkFuseStat : public Stat -{ - public: - /** - Constructor - clear instance variables. - */ - ZkFuseStat() - { - clear(); - } - /** - Destructor - do nothing. - */ - ~ZkFuseStat() - { - } - /** - Clear instance variables. - */ - void clear() - { - czxid = 0; - mzxid = 0; - ctime = 0; - mtime = 0; - version = 0; - cversion = 0; - aversion = 0; - } -}; - -/** - ZkFuseFile - an instance encapsulates the runtime state of an allocated - ZooKeeper node. - - Memory management - - Referenced by the ZkFuseHandleManager that created this instance. - - Uses boost::shared_ptr to reference the ZkFuseHandleManager that - created this instance. This makes sure that this ZkFuseHandleManager - instance cannot be deleted when it has allocated ZkFuseFile instances. - - A ZkFuseHandleManager deletes itself if it can be reclaimed. - It can be reclaimed if it has no watches, its reference count is zero, - and the ZkFuseHandleManager instance would have more than the - configured number of cached ZkFuseFile instances. - - A ZkFuseFile instance cannot be deleted if it has active watches on - its ZooKeeper node. When one of its watches fires, the ZkFuseFile - instance must exist because one of its methods will be invoked - to process the event. If the ZkFuseFile instance has been deleted, - the method will access previously freed memory. - - Concurrency control - - _mutex protects the instance variables of an instance. - - Callers should assume that a public method will acquire _mutex. - - Methods of this class may not hold _mutex while invoking an - ZkFuseHandleManager instance. - - Methods that with names that begin with "_" do not acquire _mutex. - They are usually called by public methods that acquire and hold _mutex. -*/ -class ZkFuseFile : boost::noncopyable -{ - public: - /** - Maximum size for the data part of a ZooKeeper node. - */ - static const unsigned maxDataFileSize = MAX_DATA_SIZE; - - private: - /** - Mode returned by getattr for a ZkFuse directory. - */ - static const mode_t dirMode = (S_IFDIR | 0777); - /** - Mode returned by getattr for a ZkFuse regular file. - */ - static const mode_t regMode = (S_IFREG | 0777); - - /** - References the ZkFuseHandleManager that created this instance. - */ - ZkFuseHandleManager::SharedPtr _manager; - /** - Handle for this instance. - */ - const int _handle; - /** - Path of the ZooKeeper node represented by this instance. - */ - const std::string _path; - /** - Mutex that protects the instance variables of this instance. - */ - mutable zkfuse::Mutex _mutex; - /** - Reference count for this instance, i.e. the number of opens - minus the number of closes. - */ - int _refCount; - /** - Indicates whether the ZooKeeper node exist. - This flag allows caching of deleted ZooKeeper node to avoid - repeated ZooKeeper lookups for a non-existent path, and avoid - using cached information. - - Its value is true if - - it is verified to exist (by calling ZooKeeper), or - - it is existence is unknown because ZooKeeper has not been - invoked to verify its path's existence. - */ - bool _deleted; - /** - Count of current number directory opens minus directory closes. - */ - int _openDirCount; - /** - Indicates whether cached children information is valid. - - It is true if the cached children information is valid. - */ - bool _initializedChildren; - /** - Indicates whether there is an outstanding children watch. - - It is true if it has an outstanding children watch. - */ - bool _hasChildrenListener; - /** - Cached children information. - - The cache is valid if _initializedChildren is true. - */ - NodeNames _children; - - /** - Indicates whether the cached data is valid. - - It is true if the cached data and ZooKeeper Stat are valid. - */ - bool _initializedData; - /** - Indicates whether there is an outstanding data watch. - - It is true if it has an outstanding data watch. - */ - bool _hasDataListener; - /** - Indicates whether the cached data (_activeData) has been modified. - - It is true if the cached data has been modified. - */ - bool _dirtyData; - /** - Currently active data. - - To maintain atomicity of updates and emulate Posix semantics, - when a ZkFuse file remains open, the same data will be accessed - by the file's clients. The data will be flushed to ZooKeeper when - the flush method is called. The flush method may be called - explicitly by a client or implicitly when the ZkFuse file is no - longer currently open. - - _activeData and _activeStat stores the data and ZooKeeper Stat - that will be accessed by the file's clients. - - If there are changes when the ZkFuse file is open, new data is - cached as latest data (by _latestData and _latestStat). - */ - Data _activeData; - /** - Currently active ZooKeeper Stat. - \see _activeData - */ - ZkFuseStat _activeStat; - /** - Latest data. - This is either the same as _activeData or it is newer. It is newer - is it has been updated by event triggered by a data watch. - */ - Data _latestData; - /** - Latest ZooKeeper data. - This is either the same as _activeStat or it is newer. It is newer - is it has been updated by event triggered by a data watch. - */ - ZkFuseStat _latestStat; - - /** - Get userid. - - \return the userid. - */ - uid_t _getUid() const - { - return _manager->getCommon().getUid(); - } - /** - Get groupid. - - \return the groupid. - */ - gid_t _getGid() const - { - return _manager->getCommon().getGid(); - } - /** - Get block size. - - \return the block size. - */ - unsigned _getBlkSize() const - { - return _manager->getCommon().getBlkSize(); - } - /** - Get number of children, include metadata children in the count. - - \return the number of children including metadata children. - */ - unsigned _numChildrenIncludeMeta() const - { - unsigned count = _children.size(); - LOG_DEBUG(LOG, "numChildrenIncludeMeta() returns %u", count); - return count; - } - /** - Get number of children, exclude metadata children in the count. - - \return the number of children excluding metadata children. - */ - unsigned _numChildrenExcludeMeta() const - { - unsigned count = 0; - for (NodeNames::const_iterator it = _children.begin(); - it != _children.end(); - it++) { - if (!_isMeta(*it)) { - count++; - } - } - LOG_DEBUG(LOG, "numChildrenExcludeMeta() returns %u", count); - return count; - } - /** - Whether the ZooKeeper node has children, include metadata - children. - - \return true if it has children including metadata children. - */ - bool _hasChildrenIncludeMeta() const - { - return _numChildrenIncludeMeta() != 0; - } - /** - Return true if the ZooKeeper node has children, include metadata - children. - - \return true if it has children excluding metadata children. - */ - bool _hasChildrenExcludeMeta() const - { - return _numChildrenExcludeMeta() != 0; - } - /** - Whether the ZooKeeper node has data. - - \return true if _activeData is not empty. - */ - bool _hasData() const - { - return _activeData.empty() == false; - } - /** - Whether the ZooKeeper node has child with the specified path. - - \return true if the ZooKeeper node has a child with the specified path. - \param childPath the path of the child. - */ - bool _hasChildPath(const std::string & childPath) const - { - bool hasChild = - std::find(_children.begin(), _children.end(), childPath) - != _children.end(); - LOG_DEBUG(LOG, "hasChild(childPath %s) returns %d", - childPath.c_str(), hasChild); - return hasChild; - } - /** - Whether the given path component is a ZkFuse synthesized path - component. - - A ZkFuse synthesized path component will begin with - the metadataNamePrefix obtained from the common configuration. - \see _metadataNamePrefix - - \return true if the path component is a ZkFuse synthesized path - component. - \param childName the path component to check if it is synthesized by - ZkFuse. - */ - bool _isMeta(const std::string & childName) const - { - bool isMeta; - const std::string & prefix = - _manager->getCommon().getMetadataNamePrefix(); - unsigned offset = - (_path.length() > 1 ? - _path.length() + 1 : - 1 /* special case for root dir */ ); - unsigned minLength = offset + prefix.length(); - if (childName.length() < minLength || - childName.compare(offset, prefix.length(), prefix) != 0) { - isMeta = false; - } else { - isMeta = true; - } - LOG_DEBUG(LOG, "isMeta(childName %s) returns %d", - childName.c_str(), isMeta); - return isMeta; - } - /** - Build a path for a specific child of the ZooKeeper node. - - This is done by appending "/" (unless it is the ZooKeeper node - is the root node) and the name of the child. - - \return the path for the specified child of the ZooKeeper node. - \param name the name of the child. - */ - std::string _getChildPath(const std::string & name) const - { - return buildChildPath(_path, name); - } - /** - Whether the ZooKeeper node has a regular file metadata child node. - - \return true if the ZooKeeper node has a regular file metadata child - node. - */ - bool _hasRegMetadata() const - { - bool res = _hasChildPath( - _getChildPath(_manager->getCommon().getRegMetadataName())); - LOG_DEBUG(LOG, "hasRegMetadata() returns %d", res); - return res; - } - /** - Whether the ZooKeeper node has a directory metadata child node. - - \return true if the ZooKeeper node has a directory metadata child - node. - */ - bool _hasDirMetadata() const - { - bool res = _hasChildPath( - _getChildPath(_manager->getCommon().getDirMetadataName())); - LOG_DEBUG(LOG, "hasDirMetadata() returns %d", res); - return res; - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse regular - file. - - It should be a ZkFuse regular file it has no children or its - only children is its regular file metadata child node. - - \return true if the Zookeeper node should be presented as a ZkFuse - regular file. - */ - bool _isReg() const - { - unsigned numChildrenIncludeMeta = _numChildrenIncludeMeta(); - bool res = - (numChildrenIncludeMeta == 0) || - (numChildrenIncludeMeta == 1 && _hasRegMetadata() == true); - LOG_DEBUG(LOG, "isReg() returns %d", res); - return res; - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse directory. - - It should be a ZkFuse directory if it should not be presented as - a ZkFuse regular directory. - \see _isReg - - \return true if the Zookeeper node should be presented as a ZkFuse - directory. - */ - bool _isDir() const - { - return !_isReg(); - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse regular - file by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - regular file. - \param nameType specifies the ZkFuseNameType. - \param doLock whether _mutex should be acquired, it should be true - if the caller did not acquire _mutex. - */ - bool _isRegNameType(ZkFuseNameType nameType, bool doLock = false) const - { - bool res; - switch (nameType) { - case ZkFuseNameRegType: - res = true; - break; - case ZkFuseNameDirType: - res = false; - break; - case ZkFuseNameDefaultType: - default: - if (doLock) { - AutoLock lock(_mutex); - res = _isReg(); - } else { - res = _isReg(); - } - break; - } - LOG_DEBUG(LOG, "isRegNameType(nameType %d) returns %d", - int(nameType), res); - return res; - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse - directory by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - directory. - \param nameType specifies the ZkFuseNameType. - \param doLock whether _mutex should be acquired, it should be true - if the caller did not acquire _mutex. - */ - bool _isDirNameType(ZkFuseNameType nameType, bool doLock = false) const - { - bool res; - switch (nameType) { - case ZkFuseNameRegType: - res = false; - break; - case ZkFuseNameDirType: - res = true; - break; - case ZkFuseNameDefaultType: - default: - if (doLock) { - AutoLock lock(_mutex); - res = _isDir(); - } else { - res = _isDir(); - } - break; - } - LOG_DEBUG(LOG, "isDirNameType(nameType %d) returns %d", - int(nameType), res); - return res; - } - /** - ZkFuse regular file metadata. - */ - struct Metadata { - /** - Version of the ZooKeeper node data that this metadata is good for. - */ - uint32_t version; - /** - Acces time in milliseconds. - */ - uint64_t atime; - /** - Modified time in milliseconds. - */ - uint64_t mtime; - - /** - Constructor. - */ - Metadata() - : version(0), - atime(0), - mtime(0) - { - } - }; - /** - Encode Metadata into Data so that it can be stored in a metadata - ZooKeeper node. - - Each Metadata attribute is encoded as ": " on single line - terminated by newline. - - \param meta the input Metadata. - \param data the output Data after encoding. - */ - void _encodeMetadata(const Metadata & meta, Data & data) const - { - LOG_DEBUG(LOG, "encodeMetadata()"); - std::ostringstream oss; - oss << "version: " << meta.version << endl - << "atime: " << meta.atime << endl - << "mtime: " << meta.mtime << endl; - data = oss.str(); - } - /** - Decode Data from a metadata child ZooKeeper node into Metadata. - - Data is a stream of ": " records separated by newline. - - \param data the input Data. - \param meta the output Metadata after decoding. - */ - void _decodeMetadata(const Data & data, Metadata & meta) const - { - LOG_DEBUG(LOG, "decodeMetadata(data %s)", data.c_str()); - std::istringstream iss(data); - char key[128]; - char value[1024]; - while (!iss.eof()) { - key[0] = 0; - value[0] = 0; - iss.get(key, sizeof(key), ' '); - if (iss.eof()) { - break; - } - iss.ignore(32, ' '); - iss.getline(value, sizeof(value)); - LOG_DEBUG(LOG, "key %s value %s", key, value); - if (strcmp(key, "version:") == 0) { - unsigned long long v = strtoull(value, NULL, 0); - LOG_DEBUG(LOG, "version: %llu", v); - meta.version = v; - } - else if (strcmp(key, "atime:") == 0) { - unsigned long long v = strtoull(value, NULL, 0); - LOG_DEBUG(LOG, "atime: %llu", v); - meta.atime = v; - } - else if (strcmp(key, "mtime:") == 0) { - unsigned long long v = strtoull(value, NULL, 0); - LOG_DEBUG(LOG, "mtime: %llu", v); - meta.mtime = v; - } - else { - LOG_WARN(LOG, "decodeMetadata: path %s unknown key %s %s\n", - _path.c_str(), key, value); - } - } - LOG_DEBUG(LOG, "decodeMetadata done"); - } - /** - Flush data to the ZooKeeper node. - - If cached active data has been modified, flush it to the ZooKeeper node. - Returns -EIO if the data cannot be written because the cached active - data is not the expected version, i.e. ZooKeeper returns ZBADVERSION. - -EIO may also indicate a more general failure, such as unable to - communicate with ZooKeeper. - - \return 0 if successful, otherwise negative errno. - */ - int _flush() - { - LOG_DEBUG(LOG, "flush() path %s", _path.c_str()); - - int res = 0; - try { - if (_dirtyData) { - LOG_DEBUG(LOG, "is dirty, active version %d", - _activeStat.version); - _manager->getCommon().getZkAdapter()-> - setNodeData(_path, _activeData, _activeStat.version); - /* assumes version always increments by one if successful */ - _deleted = false; - _activeStat.version++; - _dirtyData = false; - res = 0; - } - else { - LOG_DEBUG(LOG, "not dirty"); - res = 0; - } - } catch (const ZooKeeperException & e) { - if (e.getZKErrorCode() == ZBADVERSION) { - LOG_ERROR(LOG, "flush %s bad version, was %d", - _path.c_str(), _activeStat.version); - res = -EIO; - } - else { - LOG_ERROR(LOG, "flush %s exception %s", - _path.c_str(), e.what()); - res = -EIO; - } - } - - LOG_DEBUG(LOG, "flush returns %d", res); - return res; - } - /** - Truncate or expand the size of the cached active data. - - This method only changes the size of the cached active data. - This change is committed to ZooKeeper when the cached data - is written to the ZooKeeper node by flush(). - - Return -EFBIG is the requested size exceeds the maximum. - - \return 0 if successful, otherwise negative errno. - \param size the requested size. - */ - int _truncate(off_t size) - { - LOG_DEBUG(LOG, "truncate(size %zu) path %s", size, _path.c_str()); - - int res = 0; - - if (!_isInitialized()) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else if (size > _activeData.size()) { - if (size > maxDataFileSize) { - LOG_DEBUG(LOG, "size > maxDataFileSize"); - res = -EFBIG; - } else { - LOG_DEBUG(LOG, "increase to size"); - _activeData.insert(_activeData.begin() + - (size - _activeData.size()), 0); - _dirtyData = true; - res = 0; - } - } - else if (size < _activeData.size()) { - LOG_DEBUG(LOG, "decrease to size"); - _activeData.resize(size); - _dirtyData = true; - res = 0; - } - else { - LOG_DEBUG(LOG, "do nothing, same size"); - } - - LOG_DEBUG(LOG, "truncate returns %d", res); - return res; - } - /** - Remove a ZkFuse directory. - - If force is true, then the ZooKeeper node and its decendants - will be deleted. - - If force is false, then this method implements the semantics - of removing a ZkFuse directory. It will delete the ZooKeeper node - only if the ZooKeeper node have no data and no non-metadata - children. - - Return -ENOTDIR if the ZooKeeper node is not considered - to be a directory (after taking into consideration the specified - ZkFuseNameType). - - Return -ENOTEMPTY if the ZooKeeper node has data or it has - non-metadata children. - - Return -ENOENT if the ZooKeeper cannot be deleted, usually this - is because it does not exist. - - \return 0 if successful, otherwise negative errno. - \param nameType the ZkFuseNameType of the path used to specify the - directory to be removed. It influences whether ZkFuse - considers the ZooKeeper node to be a regular file or - directory. \see ZkFuseNameType - \param force set to true to bypass ZkFuse rmdir semantic check. - */ - int _rmdir(ZkFuseNameType nameType, bool force) - { - LOG_DEBUG(LOG, "rmdir(nameType %d, force %d) path %s", - int(nameType), force, _path.c_str()); - - int res = 0; - try { - if (!force && !_isDirNameType(nameType)) { - LOG_DEBUG(LOG, "failed because not directory"); - res = -ENOTDIR; - } - else if (!force && _hasData()) { - /* rmdir cannot occur if there non-empty "data file" */ - LOG_DEBUG(LOG, "failed because node has data"); - res = -ENOTEMPTY; - } - else if (!force && _hasChildrenExcludeMeta()) { - /* rmdir cannot occur if there are "subdirs" */ - LOG_DEBUG(LOG, "failed because node has children"); - res = -ENOTEMPTY; - } - else { - LOG_DEBUG(LOG, "delete node"); - bool deleted = _manager->getCommon().getZkAdapter()-> - deleteNode(_path, true); - if (deleted) { - _deleted = true; - _clearChildren(); - res = 0; - } else { - /* TODO: differentiate delete error conditions, - * e.g. access permission, not exists, ... ? - */ - LOG_DEBUG(LOG, "delete failed"); - res = -ENOENT; - } - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "rmdir %s exception %s", _path.c_str(), e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "rmdir returns %d", res); - return res; - } - /** - Remove a ZkFuse regular file. - - This method implements the semantics of removing a ZkFuse regular file. - - If the ZkFuse regular file represents the data part of the - ZooKeeper node which is presented as a ZkFuse directory, - the regular file is virtually deleted by truncating the - ZooKeeper node's data. Readdir will not synthesize a regular - file entry for the data part of a ZooKeeper node if - the ZooKeeper node has no data. - - If the ZkFuse regular file represents the data part of the - ZooKeeper node which is presented as a ZkFuse regular file, - the ZooKeeper node and its decendants are deleted. - - Returns -EISDIR if the ZkFuse regular file cannot be deleted - because ZkFuse consider it to be a directory. - - \return 0 if successful, otherwise negative errno. - \param nameType the ZkFuseNameType of the path used to specify the - directory to be removed. It influences whether ZkFuse - considers the ZooKeeper node to be a regular file or - directory. \see ZkFuseNameType - */ - int _unlink(ZkFuseNameType nameType) - { - LOG_DEBUG(LOG, "unlink(nameType %d) path %s", - int(nameType), _path.c_str()); - - int res = 0; - switch (nameType) { - case ZkFuseNameRegType: - if (_isDir()) { - res = _truncate(0); - } else { - res = _rmdir(nameType, true); - } - break; - case ZkFuseNameDirType: - res = -EISDIR; - break; - case ZkFuseNameDefaultType: - default: - if (_isReg()) { - res = _rmdir(nameType, true); - } else { - res = -EISDIR; - } - break; - } - - LOG_DEBUG(LOG, "unlink returns %d", res); - return res; - } - /** - Whether cached children and data are valid. - - \return true if cached children and data are valid. - */ - bool _isInitialized() const - { - return _initializedChildren && _initializedData; - } - /** - Clear and invalidate cached children information. - */ - void _clearChildren() - { - _initializedChildren = false; - _children.clear(); - } - /** - Clear and invalidate cached data. - */ - void _clearData() - { - _initializedData = false; - _dirtyData = false; - _activeData.clear(); - _activeStat.clear(); - _latestData.clear(); - _latestStat.clear(); - } - /** - Whether the ZkFuseFile instance is a zombie. - - It is a zombie if it is not currently open, i.e. its reference count - is 0. - */ - bool _isZombie() const - { - return (_refCount == 0); - } - /** - Whether the ZkFuseFile instance is currently opened as a regular file - only once. - - It is used to determine when the cached data can be replaced with - the latest data. \see _activeData. - - \return true if its currently opened as a regular file only once. - */ - bool _isOnlyRegOpen() const - { - return ((_refCount - _openDirCount) == 1); - } - /** - Get attributes without accessing metadata. - - The atime and mtime returned does not take into consideration - overrides present in a matadata file. - - \return 0 if successful, otherwise negative errno. - \param stbuf return attributes here. - \param nameType specifies the ZkFuseNameType of the ZkFuse path used - to get attributes. It influences whether the directory - or regular file attributes are returned. - */ - int _getattrNoMetaAccess(struct stat & stbuf, ZkFuseNameType nameType) const - { - int res = 0; - if (_deleted) { - LOG_DEBUG(LOG, "deleted"); - res = -ENOENT; - } - else if (!_isInitialized()) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else { - assert(_isInitialized()); - bool isRegular = _isRegNameType(nameType); - if (isRegular) { - LOG_DEBUG(LOG, "regular"); - stbuf.st_mode = regMode; - stbuf.st_nlink = 1; - stbuf.st_size = _activeData.size(); - } else { - LOG_DEBUG(LOG, "directory"); - stbuf.st_mode = dirMode; - stbuf.st_nlink = - _children.size() + (_activeData.empty() ? 0 : 1); - stbuf.st_size = stbuf.st_nlink; - } - stbuf.st_uid = _getUid(); - stbuf.st_gid = _getGid(); - /* IMPORTANT: - * Conversion to secs from millisecs must occur before - * assigning to st_atime, st_mtime, and st_ctime. Otherwise - * truncating from 64-bit to 32-bit will cause lost of - * most significant 32-bits before converting to secs. - */ - stbuf.st_atime = millisecsToSecs(_activeStat.mtime); - stbuf.st_mtime = millisecsToSecs(_activeStat.mtime); - stbuf.st_ctime = millisecsToSecs(_activeStat.ctime); - stbuf.st_blksize = _getBlkSize(); - stbuf.st_blocks = - (stbuf.st_size + stbuf.st_blksize - 1) / stbuf.st_blksize; - res = 0; - } - return res; - } - /** - Get the context that should be registered with the data and - children watches. - - The returned context is a pointer to the ZkFuseFile instance - cast to the desired ContextType. - - \return the context. - */ - ZooKeeperAdapter::ContextType _getZkContext() const - { - return (ZooKeeperAdapter::ContextType) NULL; - } - - /** - DataListener - listener that listens for ZooKeeper data events - and calls dataEventReceived on the ZkFuseFile instance - identified by the event context. - \see dataEventReceived - */ - class DataListener : public ZKEventListener { - public: - /** - Received a data event and invoke ZkFuseFile instance obtained from - event context to handle the event. - */ - virtual void eventReceived(const ZKEventSource & source, - const ZKWatcherEvent & event) - { - assert(event.getContext() != 0); - ZkFuseFile * file = static_cast(event.getContext()); - file->dataEventReceived(event); - } - }; - - /** - DataListener - listener that listens for ZooKeeper children events - and calls childrenEventReceived on the ZkFuseFile instance - identified by the event context. - \see childrenEventReceived - */ - class ChildrenListener : public ZKEventListener { - public: - /** - Received a children event and invoke ZkFuseFile instance obtained from - event context to handle the event. - */ - virtual void eventReceived(const ZKEventSource & source, - const ZKWatcherEvent & event) - { - assert(event.getContext() != 0); - ZkFuseFile * file = static_cast(event.getContext()); - file->childrenEventReceived(event); - } - }; - - /** - Globally shared DataListener. - */ - static DataListener _dataListener; - /** - Globally shared ChildrenListener. - */ - static ChildrenListener _childrenListener; - - public: - /** - Constructor. - - Sets reference count to one, i.e. it has been constructed because - a client is trying to open the path. \see _refCount. - Sets deleted to true. \see _deleted. - Sets number of currently directory opens to zero. \see _openDirCount. - Invalidate cach for children information and data. - - \param manager the ZkFuseHandleManager instance who is creating this - ZkFuseFile instance. - \param handle the handle assigned by the ZkFuseHandleManager instance - for this ZkFuseFile instance. - \param path the ZooKeeper path represented by this ZkFuseFile instance. - */ - ZkFuseFile(const ZkFuseHandleManager::SharedPtr & manager, - const int handle, - const std::string & path) - : _manager(manager), - _handle(handle), - _path(path), - _mutex(), - _refCount(1), - _deleted(true), - /* children stuff */ - _openDirCount(0), - _initializedChildren(false), - _hasChildrenListener(false), - _children(), - /* data stuff */ - _initializedData(false), - _hasDataListener(false), - _dirtyData(false), - _activeData(), - _activeStat(), - _latestData(), - _latestStat() - { - LOG_DEBUG(LOG, "constructor() path %s", _path.c_str()); - } - /** - Destructor. - */ - ~ZkFuseFile() - { - LOG_DEBUG(LOG, "destructor() path %s", _path.c_str()); - - assert(_isZombie()); - _clearChildren(); - _clearData(); - } - /** - Whether the ZooKeeper node represented by this ZkFuseFile instance - has been deleted. - \see _deleted - - \return true if it is deleted. - */ - bool isDeleted() const - { - AutoLock lock(_mutex); - return _deleted; - } - /** - Return the path of the ZooKeeper node represented by this ZkFuseFile - instance. - \see _path. - - \return the ZooKeeper node's path. - */ - const string & getPath() const - { - return _path; - } - /** - Add a childPath to the children information cache. - - \return 0 if successful, otherwise return negative errno. - \param childPath the ZooKeeper path of the child. - */ - int addChild(const std::string & childPath) - { - LOG_DEBUG(LOG, "addChild(childPath %s) path %s", - childPath.c_str(), _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - if (_initializedChildren) { - NodeNames::iterator it = - std::find(_children.begin(), _children.end(), childPath); - if (it == _children.end()) { - LOG_DEBUG(LOG, "child not found, adding child path"); - _children.push_back(childPath); - res = 0; - } - else { - LOG_DEBUG(LOG, "child found"); - res = -EEXIST; - } - } - } - - LOG_DEBUG(LOG, "addChild returns %d", res); - return res; - } - /** - Remove a childPath from the children information cache. - - \return 0 if successful, otherwise return negative errno. - \param childPath the ZooKeeper path of the child. - */ - int removeChild(const std::string & childPath) - { - LOG_DEBUG(LOG, "removeChild(childPath %s) path %s", - childPath.c_str(), _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - if (_initializedChildren) { - NodeNames::iterator it = - std::find(_children.begin(), _children.end(), childPath); - if (it != _children.end()) { - LOG_DEBUG(LOG, "child found"); - _children.erase(it); - res = 0; - } - else { - LOG_DEBUG(LOG, "child not found"); - res = -ENOENT; - } - } - } - - LOG_DEBUG(LOG, "removeChild returns %d", res); - return res; - } - /** - Invalidate the cached children information and cached data. - \see _clearChildren - \see _clearData - - \param clearChildren set to true to invalidate children information cache. - \param clearData set to true to invalidate data cache. - */ - void clear(bool clearChildren = true, bool clearData = true) - { - LOG_DEBUG(LOG, "clear(clearChildren %d, clearData %d) path %s", - clearChildren, clearData, _path.c_str()); - - { - AutoLock lock(_mutex); - if (clearChildren) { - _clearChildren(); - } - if (clearData) { - _clearData(); - } - } - } - /** - Whether reference count is zero. - \see _refCount - - \return true if reference count is zero. - */ - bool isZombie() const - { - AutoLock lock(_mutex); - - return (_refCount == 0); - } - /** - Increment the reference count of the ZkFuseFile instance. - - This method may be called by a ZkFuseFileManager instance while - holding the ZkFuseFileManager's _mutex. To avoid deadlocks, - this methods must never invoke a ZkFuseFileManager instance - directly or indirectly while holding the ZkFuseFile instance's - _mutex. - \see _refCount - - \return the post-increment reference count. - \param count value to increment the reference count by. - */ - int incRefCount(int count = 1) - { - LOG_DEBUG(LOG, "incRefCount(count %d) path %s", count, _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - _refCount += count; - assert(_refCount >= 0); - res = _refCount; - } - - LOG_DEBUG(LOG, "incRefCount returns %d", res); - return res; - } - /** - Decrement the reference count of the ZkFuseFile instance. - - This method may be called by a ZkFuseFileManager instance while - holding the ZkFuseFileManager's _mutex. To avoid deadlocks, - this methods must never invoke a ZkFuseFileManager instance - directly or indirectly while holding the ZkFuseFile instance's - _mutex. - \see _refCount - - \return the post-decrement reference count. - \param count value to decrement the reference count by. - */ - int decRefCount(int count = 1) - { - return incRefCount(-count); - } - /** - Increment the count of number times the ZkFuseFile instance has - been opened as a directory. - - This count is incremented by opendir and decremented by releasedir. - \see _openDirCount. - - \return the post-increment count. - \param count the value to increment the count by. - */ - int incOpenDirCount(int count = 1) - { - LOG_DEBUG(LOG, "incOpenDirCount(count %d) path %s", - count, _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - _openDirCount += count; - assert(_openDirCount >= 0); - res = _openDirCount; - assert(_openDirCount <= _refCount); - } - - LOG_DEBUG(LOG, "incOpenDirCount returns %d", res); - return res; - - } - /** - Decrement the count of number times the ZkFuseFile instance has - been opened as a directory. - - This count is incremented by opendir and decremented by releasedir. - \see _openDirCount. - - \return the post-decrement count. - \param count the value to decrement the count by. - */ - int decOpenDirCount(int count = 1) - { - return incOpenDirCount(-count); - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse - directory by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - \see _isDirNameType - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - directory. - \param nameType specifies the ZkFuseNameType. - */ - bool isDirNameType(ZkFuseNameType nameType) const - { - return _isDirNameType(nameType, true); - } - /** - Whether ZkFuse should present the ZooKeeper node as a ZkFuse - regular file by taking into account the specified ZkFuseNameType. - - The ZkFuseNameType may override the default ZkFuse presentation of - a ZooKeeper node. - \see _isRegNameType - - \return true if ZkFuse should present the ZooKeeper node as a ZkFuse - regular file. - \param nameType specifies the ZkFuseNameType. - */ - bool isRegNameType(ZkFuseNameType nameType) const - { - return _isRegNameType(nameType, true); - } - /** - Get the active data. - \see _activeData - - \param data return data here. - */ - void getData(Data & data) const - { - AutoLock lock(_mutex); - - data = _activeData; - } - /** - Set the active data. - \see _activeData - - Return -EFBIG is the data to be written is bigger than the maximum - permitted size (and no data is written). - - \return 0 if successful, otherwise return negative errno. - \param data set to this data. - \param doFlush whether to flush the data to the ZooKeeper node. - */ - int setData(const Data & data, bool doFlush) - { - LOG_DEBUG(LOG, "setData(doFlush %d) path %s", doFlush, _path.c_str()); - int res = 0; - - if (data.size() > maxDataFileSize) { - res = -EFBIG; - } - else { - AutoLock lock(_mutex); - _activeData = data; - _dirtyData = true; - if (doFlush) { - res = _flush(); - } - } - - LOG_DEBUG(LOG, "setData() returns %d", res); - return res; - } - /** - Update the children information and the data caches as needed. - - This method is invoked when a ZkFuse regular file or directory - implemented by this ZkFuseFile instance is opened, e.g. - using open or opendir. It attempts to: - - make sure that the cache has valid children information - - register for watches for changes if no previous watches have - been registered. - - The newFile flag indicates if the ZkFuseFile instance has just - been constructed and that ZooKeeper has not been contacted to - determine if the ZooKeeper path for this file really exist. - When a ZkFuseFile instance is created, the _deleted flag is set to - true because it is safer to assume that the ZooKeeper node does - not exist. The newFile flag causes the _deleted flag to be - ignored and ZooKeeper to be contacted to update the caches. - - If the newFile flag is false, then the ZkFuseFile instance is - currently open and have been opened before. Hence, these previous - opens should have contacted ZooKeeper and would like learned from - ZooKeeper whether the ZooKeeper path exists. Therefore, - the _deleted flag should be trustworthy, i.e. it has accurate - information on whether the ZooKeeper path actually exists. - - \return 0 if successful, otherwise return negative errno. - \param newFile set to true if the ZkFuseFile instance is newly created. - */ - int update(bool newFile) - { - LOG_DEBUG(LOG, "update(newFile %d) path %s", newFile, _path.c_str()); - - int res = 0; - { - AutoLock lock(_mutex); - - /* At this point, cannot be zombie. - */ - assert(!_isZombie()); - if (!newFile && _deleted) { - /* Deleted file, don't bother to update caches */ - LOG_DEBUG(LOG, "deleted, not new file"); - res = -ENOENT; - } - else { - try { - LOG_DEBUG(LOG, "initialized children %d, data %d", - _initializedChildren, _initializedData); - LOG_DEBUG(LOG, "has children watch %d, data watch %d", - _hasChildrenListener, _hasDataListener); - /* - * Children handling starts here. - * If don't have children listener, - * then must establish listener. - * If don't have cached children information, - * then must get children information. - * It just happens, that the same ZooKeeper API - * is used for both. - */ - if (_initializedChildren == false || - _hasChildrenListener == false -#ifdef ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - /* HACK for root node because changes to children - * on a root node does not cause children watches to - * fire. - */ - || _path.length() == 1 -#endif // ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - ) { - LOG_DEBUG(LOG, "update children"); - NodeNames children; - _manager->getCommon().getZkAdapter()-> - getNodeChildren( children, _path, - &_childrenListener, _getZkContext()); - _hasChildrenListener = true; - LOG_DEBUG(LOG, "update children done"); - _children.swap(children); - _initializedChildren = true; - /* Since getNodeChildren is successful, the - * path must exist */ - _deleted = false; - } - else { - /* Children information is fresh since - * it is initialized and and have been - * updated by listener. - */ - } - /* - * Data handling starts here. - */ - assert(newFile == false || _isOnlyRegOpen()); - if (!_isOnlyRegOpen()) { - /* If is already currently opened by someone, - * then don't update data with latest from ZooKeeper, - * use current active data (which may be initialized - * or not). - * \see _activeData - */ - LOG_DEBUG(LOG, "node currently in-use, no data update"); - } - else { - /* If not opened/reopened by someone else, - * then perform more comprehensive checks of - * to make data and listener is setup correctly. - * If don't have data listener, - * then must establish listener. - * If don't have cached data, - * then must get data. - * It just happens, that the same ZooKeeper API - * is used for both. - */ - LOG_DEBUG(LOG, "node first use or reuse"); - if (_initializedData == false || - _hasDataListener == false) { - /* Don't have any data for now or need to register - * for callback */ - LOG_DEBUG(LOG, "update data"); - _latestData = - _manager->getCommon().getZkAdapter()-> - getNodeData(_path, &_dataListener, - _getZkContext(), - &_latestStat); - _hasDataListener = true; - LOG_DEBUG(LOG, - "update data done, latest version %d", - _latestStat.version); - /* Since getNodeData is successful, the - * path must exist. */ - _deleted = false; - } - else { - /* Data is fresh since it is initialized and - * and have been updated by listener. - */ - } - /* Update active data to the same as the most - * recently acquire data. - */ - _activeData = _latestData; - _activeStat = _latestStat; - _initializedData = true; - _dirtyData = false; - LOG_DEBUG(LOG, "update set active version %d", - _activeStat.version); - } - res = 0; - } catch (const ZooKeeperException & e) { - /* May have ZNONODE exception if path does exist. */ - if (e.getZKErrorCode() == ZNONODE) { - LOG_DEBUG(LOG, "update %s exception %s", - _path.c_str(), e.what()); - /* Path does not exist, set _deleted, - * clear children information cache - */ - _deleted = true; - _clearChildren(); - res = -ENOENT; - } else { - LOG_ERROR(LOG, "update %s exception %s", - _path.c_str(), e.what()); - res = -EIO; - } - } - } - } - - LOG_DEBUG(LOG, "update returns %d", res); - return res; - } - /** - Process a data event. - - This method may: - - Invalidate the data cache. - - Invoke ZooKeeper to update the data cache and register a new - data watch so that the cache can be kept in-sync with the - ZooKeeper node's data. - - This method does not change the active data. Active data will be - changed to a later version by update() at the appropriate time. - \see update. - */ - void dataEventReceived(const ZKWatcherEvent & event) - { - bool reclaim = false; - int eventType = event.getType(); - int eventState = event.getState(); - - /* - IMPORTANT: - - Do not mark ZkFuseFile instance as deleted when a ZOO_DELETED_EVENT - is received without checking with ZooKeeper. An example of - problematic sequence would be: - - 1. Create node. - 2. Set data and watch. - 3. Delete node. - 4. Create node. - 5. Deleted event received. - - It is a bug to mark the ZkFuseFile instance as deleted after - step 5 because the node exists. - - Therefore, this method should always contact ZooKeeper to keep the - data cache (and deleted status) up-to-date if necessary. - */ - LOG_DEBUG(LOG, "dataEventReceived() path %s, type %d, state %d", - _path.c_str(), eventType, eventState); - { - AutoLock lock(_mutex); - - _hasDataListener = false; - /* If zombie, then invalidate cached data. - * This clears _initializedData and eliminate - * the need to get the latest data from ZooKeeper and - * re-register data watch. - */ - if (_isZombie() && _initializedData) { - LOG_DEBUG(LOG, "invalidate data"); - _clearData(); - } - else if ((_refCount - _openDirCount) > 0) { - /* Don't invalidate cached data because clients of currently - * open files don't expect the data to change from under them. - * If data acted upon by these clients have become stale, - * then the clients will get an error when ZkFuse attempts to - * flush dirty data. The clients will not get error - * notification if they don't modify the stale data. - * - * If data cache is cleared here, then the following code - * to update data cache and re-register data watch will not - * be executed and may result in the cached data being - * out-of-sync with ZooKeeper. - */ - LOG_WARN(LOG, - "%s data has changed while in-use, " - "type %d, state %d, refCount %d", - _path.c_str(), eventType, eventState, _refCount); - } - /* If cache was valid and still connected - * then get the latest data from ZooKeeper - * and re-register data watch. This is required to keep - * the data cache in-sync with ZooKeeper. - */ - if (_initializedData && - eventState == ZOO_CONNECTED_STATE - ) { - try { - LOG_DEBUG(LOG, "register data watcher"); - _latestData = - _manager->getCommon().getZkAdapter()-> - getNodeData(_path, &_dataListener, _getZkContext(), - &_latestStat); - _hasDataListener = true; - LOG_DEBUG(LOG, - "get data done, version %u, cversion %u done", - _latestStat.version, _latestStat.cversion); - _deleted = false; - } catch (const ZooKeeperException & e) { - if (e.getZKErrorCode() == ZNONODE) { - _deleted = true; - _clearChildren(); - } - LOG_ERROR(LOG, "dataEventReceived %s exception %s", - _path.c_str(), e.what()); - } - } - } - LOG_DEBUG(LOG, "dataEventReceived return %d", reclaim); - } - /** - Process a children event. - - This method may: - - Invalidate the children information cache. - - Invoke ZooKeeper to update the children cache and register a new - data watch so that the cache can be kept in-sync with the - ZooKeeper node's children information. - */ - void childrenEventReceived(const ZKWatcherEvent & event) - { - bool reclaim = false; - int eventType = event.getType(); - int eventState = event.getState(); - - LOG_DEBUG(LOG, "childrenEventReceived() path %s, type %d, state %d", - _path.c_str(), eventType, eventState); - { - AutoLock lock(_mutex); - - _hasChildrenListener = false; - /* If zombie or disconnected, then invalidate cached children - * information. This clears _initializedChildren and eliminate - * the need to get the latest children information and - * re-register children watch. - */ - if (_initializedChildren && - (_isZombie() || eventState != ZOO_CONNECTED_STATE)) { - LOG_DEBUG(LOG, "invalidate children"); - _clearChildren(); - } - else if (_initializedChildren) { - /* Keep cached children information so that we have some - * children information if get new children information - * fails. If there is failure, then on next open, - * update() will attempt again to get children information - * again because _hasChildrenListener will be false. - * - * If children information cache is cleared here, then - * the following code to update children information cache - * and re-register children watch will not be executed - * and may result in the cached children information being - * out-of-sync with ZooKeeper. - * - * The children cache will be cleared if unable to - * get children and re-establish watch. - */ - LOG_WARN(LOG, - "%s children has changed while in-use, " - "type %d, state %d, refCount %d", - _path.c_str(), eventType, eventState, _refCount); - } - /* If children cache was valid and still connected, - * then get the latest children information from ZooKeeper - * and re-register children watch. This is required to - * keep the children information cache in-sync with ZooKeeper. - */ - if (_initializedChildren && - eventState == ZOO_CONNECTED_STATE - ) { - /* Should try to keep the cache in-sync, register call - * callback again and get current children. - */ - try { - LOG_DEBUG(LOG, "update children"); - NodeNames children; - _manager->getCommon().getZkAdapter()-> - getNodeChildren(children, _path, - &_childrenListener, _getZkContext()); - _hasChildrenListener = true; - LOG_DEBUG(LOG, "update children done"); - _children.swap(children); - _deleted = false; - } catch (const ZooKeeperException & e) { - if (e.getZKErrorCode() == ZNONODE) { - _deleted = true; - _clearChildren(); - } - LOG_ERROR(LOG, "childrenEventReceived %s exception %s", - _path.c_str(), e.what()); - _children.clear(); - } - } - } - LOG_DEBUG(LOG, "childrenEventReceived returns %d", reclaim); - } - /** - Truncate or expand the size of the cached active data. - - This method only changes the size of the cached active data. - This change is committed to ZooKeeper when the cached data - is written to the ZooKeeper node by flush(). - - Return -EFBIG is the requested size exceeds the maximum. - - \return 0 if successful, otherwise negative errno. - \param size the requested size. - */ - int truncate(off_t size) - { - int res = 0; - - { - AutoLock lock(_mutex); - res = _truncate(size); - } - - return res; - } - /** - Copy range of active data into specified output buffer. - - \return if successful, return number of bytes copied, otherwise - return negative errno. - \param buf address of the output buffer. - \param size size of the output buffer and desired number of bytes to copy. - \param offset offset into active data to start copying from. - */ - int read(char *buf, size_t size, off_t offset) const - { - LOG_DEBUG(LOG, "read(size %zu, off_t %zu) path %s", - size, offset, _path.c_str()); - - int res = 0; - - { - AutoLock lock(_mutex); - if (!_initializedData) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else { - off_t fileSize = _activeData.size(); - if (offset > fileSize) { - LOG_DEBUG(LOG, "offset > fileSize %zu", fileSize); - res = 0; - } - else { - if (offset + size > fileSize) { - size = fileSize - offset; - LOG_DEBUG(LOG, - "reducing read size to %zu for fileSize %zu", - size, fileSize); - } - copy(_activeData.begin() + offset, - _activeData.begin() + offset + size, - buf); - res = size; - } - } - } - - LOG_DEBUG(LOG, "read returns %d", res); - return res; - } - /** - Copy buffer content to active data. - - \return if successful, return number of bytes copied, otherwise - return negative errno. - \param buf address of the buffer. - \param size size of the input buffer and desired number of bytes to copy. - \param offset offset into active data to start copying to. - */ - int write(const char *buf, size_t size, off_t offset) - { - LOG_DEBUG(LOG, "write(size %zu, off_t %zu) path %s", - size, offset, _path.c_str()); - - int res = 0; - - { - AutoLock lock(_mutex); - if (!_initializedData) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else if (offset >= maxDataFileSize) { - LOG_DEBUG(LOG, "offset > maxDataFileSize %u", maxDataFileSize); - res = -ENOSPC; - } - else { - if (offset + size > maxDataFileSize) { - LOG_DEBUG(LOG, - "reducing write size to %zu " - "for maxDataFileSize %u", - size, maxDataFileSize); - size = maxDataFileSize - offset; - } - off_t fileSize = _activeData.size(); - if (offset + size > fileSize) { - LOG_DEBUG(LOG, "resizing to %zu", offset + size); - _activeData.resize(offset + size); - } - copy(buf, buf + size, _activeData.begin() + offset); - memcpy(&_activeData[offset], buf, size); - _dirtyData = true; - res = size; - } - } - - LOG_DEBUG(LOG, "write returns %d", res); - return res; - } - /** - Flush data to the ZooKeeper node. - - If cached active data has been modified, flush it to the ZooKeeper node. - Returns -EIO if the data cannot be written because the cached active - data is not the expected version, i.e. ZooKeeper returns ZBADVERSION. - -EIO may also indicate a more general failure, such as unable to - communicate with ZooKeeper. - - \return 0 if successful, otherwise negative errno. - */ - int flush() - { - int res = 0; - { - AutoLock lock(_mutex); - res = _flush(); - } - return res; - } - /** - Close of the ZkFuse regular file represented by the ZkFuseFile instance. - - This may: - - Flush dirty data to the ZooKeeper node, and return the result of the - flush operation. - - Reclaim the ZkFuseFile instance. - \see ZkFuseHandleManaer::reclaimIfNecessary - - \return result of flush operation - 0 if successful, - otherwise negative errno. - */ - int close() - { - LOG_DEBUG(LOG, "close() path %s", _path.c_str()); - int res = 0; - - bool reclaim = false; - { - AutoLock lock(_mutex); - res = _flush(); - if (_deleted) { - _clearData(); - _clearChildren(); - } - } - _manager->deallocate(_handle); - - LOG_DEBUG(LOG, "close returns %d", res); - return res; - } - /** - Get ZkFuse regular file or directory attributes. - - \return 0 if successful, otherwise negative errno. - \param stbuf return attributes here. - \param nameType specifies the ZkFuseNameType of the ZkFuse path used - to get attributes. It influences whether the directory - or regular file attributes are returned. - */ - int getattr(struct stat & stbuf, ZkFuseNameType nameType) const - { - LOG_DEBUG(LOG, "getattr(nameType %d) path %s", - int(nameType), _path.c_str()); - - int res = 0; - int version = 0; - std::string metaPath; - { - AutoLock lock(_mutex); - - res = _getattrNoMetaAccess(stbuf, nameType); - if (res == 0) { - version = _activeStat.version; - metaPath = _getChildPath( - ((stbuf.st_mode & S_IFMT) == S_IFREG) ? - _manager->getCommon().getRegMetadataName() : - _manager->getCommon().getDirMetadataName()); - if (_hasChildPath(metaPath) == false) { - metaPath.clear(); - } - } - } - if (res == 0 && metaPath.empty() == false) { - Data data; - int metaRes = _manager->getData(metaPath, data); - LOG_DEBUG(LOG, "metaRes %d dataSize %zu", - metaRes, data.size()); - if (metaRes == 0 && data.empty() == false) { - Metadata metadata; - _decodeMetadata(data, metadata); - LOG_DEBUG(LOG, "metadata version %u active version %u", - metadata.version, version); - if (metadata.version == version) { - /* IMPORTANT: - * Must convert from millisecs to secs before setting - * st_atime and st_mtime to avoid truncation error - * due to 64-bit to 32-bit conversion. - */ - stbuf.st_atime = millisecsToSecs(metadata.atime); - stbuf.st_mtime = millisecsToSecs(metadata.mtime); - } - } - } - - LOG_DEBUG(LOG, "getattr returns %d", res); - return res; - } - /** - Read directory entries. - This interface is defined by FUSE. - - \return 0 if successful, otherwise negative errno. - \param buf output buffer to store output directory entries. - \param filler function used to fill the output buffer. - \param offset start filling from a specific offset. - */ - int readdir(void *buf, fuse_fill_dir_t filler, off_t offset) const - { - LOG_DEBUG(LOG, "readdir(offset %zu) path %s", offset, _path.c_str()); - int res = 0; - - int dataFileIndex = -1; - unsigned leftTrim = 0; - typedef std::pair DirEntry; - typedef std::vector DirEntries; - DirEntries dirEntries; - - /* Get directory entries in two phase to avoid invoking - * ZkFuseHandleManager while holding _mutex. - * In first phase, get all the names of child nodes starting - * at offset. Also remember their index for use in second phase. - * The first phase hold _mutex. - */ - { - AutoLock lock(_mutex); - if (!_isInitialized()) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else { - leftTrim = (_path.length() == 1 ? 1 : _path.length() + 1); - unsigned start = offset; - unsigned i; - for (i = start; i < _children.size(); i++) { - const std::string & childName = _children[i]; - if (_isMeta(childName)) { - continue; - } - dirEntries.push_back(DirEntry(childName, i)); - } - if (i == _children.size() && !_activeData.empty()) { - dataFileIndex = i + 1; - } - res = 0; - } - } - - /* Second phase starts here. - * DONOT hold _mutex as this phase invokes ZkFuseHandleManager to - * get attributes for the directory entries. - */ - if (res == 0) { - bool full = false; - for (DirEntries::const_iterator it = dirEntries.begin(); - it != dirEntries.end(); - it++) { - - ZkFuseAutoHandle childAutoHandle(_manager, it->first); - int childRes = childAutoHandle.get(); - if (childRes >= 0) { - struct stat stbuf; - int attrRes = childAutoHandle.getFile()-> - getattr(stbuf, ZkFuseNameDefaultType); - if (attrRes == 0) { - if (filler(buf, it->first.c_str() + leftTrim, - &stbuf, it->second + 1)) { - LOG_DEBUG(LOG, "filler full"); - full = true; - break; - } - } - } - } - if (full == false && dataFileIndex != -1) { - LOG_DEBUG(LOG, "include data file name"); - struct stat stbuf; - int attrRes = getattr(stbuf, ZkFuseNameRegType); - if (attrRes == 0) { - filler(buf, - _manager->getCommon().getDataFileName().c_str(), - &stbuf, dataFileIndex + 1); - } - } - } - - LOG_DEBUG(LOG, "readdir returns %d", res); - return res; - } - /** - Set the access time and modified time. - - Set the access and modifieds times on the ZkFuse regular file - or directory represented by this ZkFuseFile instance. - - Since there is no interface to change these times on a - ZooKeeper node, ZkFuse simulates this by writing to a - metadata node which is a child node of the ZooKeeper node. - ZkFuse writes the current version, the specified access - and modified times to the metadata node. - - When get attributes is invoked, get attributes will check - for the presence of this metadata node and if the version - number matches the current data version, then get attributes - will return the access and modified times stored in the - metadata node. - - \return 0 if successful, otherwise negative errno. - \param atime access time in milliseconds. - \param mtime modified time in milliseconds. - \param nameType specifies the ZkFuseNameType of the ZkFuse path used - to set access and modified times. It influences - whether the directory or regular file access and - modified times are set. - */ - int utime(uint64_t atime, uint64_t mtime, ZkFuseNameType nameType) - { - LOG_DEBUG(LOG, - "utime(atime %llu, mtime %llu, nameType %d) path %s", - (unsigned long long) atime, - (unsigned long long) mtime, - (int) nameType, _path.c_str()); - - int res = 0; - std::string metaPath; - bool exists = false; - Data data; - { - AutoLock lock(_mutex); - - if (!_isInitialized()) { - LOG_DEBUG(LOG, "not initialized"); - res = -EIO; - } - else { - bool isRegular = _isRegNameType(nameType); - Metadata metadata; - metadata.version = _activeStat.version; - metadata.atime = atime; - metadata.mtime = mtime; - metaPath = _getChildPath( - isRegular ? - _manager->getCommon().getRegMetadataName() : - _manager->getCommon().getDirMetadataName()); - exists = _hasChildPath(metaPath); - _encodeMetadata(metadata, data); - res = 0; - } - } - if (res == 0 && metaPath.empty() == false) { - res = _manager->setData(metaPath, data, exists, true); - } - - LOG_DEBUG(LOG, "utime returns %d", res); - return res; - } - /** - Remove a ZkFuse directory. - - If force is true, then the ZooKeeper node and its decendants - will be deleted. - - If force is false, then this method implements the semantics - of removing a ZkFuse directory. It will delete the ZooKeeper node - only if the ZooKeeper node have no data and no non-metadata - children. - - Return -ENOTDIR if the ZooKeeper node is not considered - to be a directory (after taking into consideration the specified - ZkFuseNameType). - - Return -ENOTEMPTY if the ZooKeeper node has data or it has - non-metadata children. - - Return -ENOENT if the ZooKeeper cannot be deleted, usually this - is because it does not exist. - - \return 0 if successful, otherwise negative errno. - \param nameType the ZkFuseNameType of the path used to specify the - directory to be removed. It influences whether ZkFuse - considers the ZooKeeper node to be a regular file or - directory. \see ZkFuseNameType - \param force set to true to bypass ZkFuse rmdir semantic check. - */ - int rmdir(ZkFuseNameType nameType, bool force) - { - int res = 0; - - { - AutoLock lock(_mutex); - res = _rmdir(nameType, force); - } - if (res == 0) { - _manager->removeChildFromParent(_path); - } - return res; - } - /** - Remove a ZkFuse regular file. - - This method implements the semantics of removing a ZkFuse regular file. - - If the ZkFuse regular file represents the data part of the - ZooKeeper node which is presented as a ZkFuse directory, - the regular file is virtually deleted by truncating the - ZooKeeper node's data. Readdir will not synthesize a regular - file entry for the data part of a ZooKeeper node if - the ZooKeeper node has no data. - - If the ZkFuse regular file represents the data part of the - ZooKeeper node which is presented as a ZkFuse regular file, - the ZooKeeper node and its decendants are deleted. - - Returns -EISDIR if the ZkFuse regular file cannot be deleted - because ZkFuse consider it to be a directory. - - \return 0 if successful, otherwise negative errno. - \param nameType the ZkFuseNameType of the path used to specify the - directory to be removed. It influences whether ZkFuse - considers the ZooKeeper node to be a regular file or - directory. \see ZkFuseNameType - */ - int unlink(ZkFuseNameType nameType) - { - int res = 0; - { - AutoLock lock(_mutex); - res = _unlink(nameType); - } - if (res == 0) { - _manager->removeChildFromParent(_path); - } - return res; - } - /** - Utility function to construct a ZooKeeper path for a child - of a ZooKeeper node. - - \return the full path of the child. - \param parent the parent's full path. - \param child the child's parent component. - */ - static std::string buildChildPath(const std::string & parent, - const std::string & child) - { - std::string s; - s.reserve(parent.length() + child.length() + 32); - if (parent.length() > 1) { - // special case for root dir - s += parent; - } - s += "/"; - s += child; - return s; - } -}; - -ZkFuseFile::DataListener ZkFuseFile::_dataListener; -ZkFuseFile::ChildrenListener ZkFuseFile::_childrenListener; - -void ZkFuseAutoHandle::reset(int handle) -{ - int old = _handle; - ZkFuseFilePtr oldFile = _file; - _handle = handle; - _initFile(); - if (old >= 0) { - assert(oldFile != NULL); - oldFile->close(); - } -} - -ZkFuseHandleManager::Handle -ZkFuseHandleManager::allocate(const std::string & path, bool & newFile) -{ - LOG_DEBUG(LOG, "allocate(path %s)", path.c_str()); - - Handle handle; - { - AutoLock lock(_mutex); - Map::iterator it = _map.find(path); - if (it == _map.end()) { - LOG_DEBUG(LOG, "not found"); - if (_freeList.empty()) { - handle = _files.size(); - _files.resize(handle + 1); - LOG_DEBUG(LOG, "free list empty, resize handle %d", handle); - } else { - handle = _freeList.back(); - _freeList.pop_back(); - LOG_DEBUG(LOG, "get from free list, handle %d", handle); - } - assert(_files[handle] == NULL); - _files[handle] = - new ZkFuseFile(SharedPtr(_thisWeakPtr), handle, path); - /* Not really supposed to invoke the new ZkFuseFile instance - * because this method is not supposed to invoke ZkFuseFile - * methods that while holding _mutex. However, it is safe - * to do without casuing deadlock because these methods - * are known not to invoke other methods, especially one - * that invoke this ZkFuseHandleManager instance. - */ - assert(_files[handle]->incRefCount(0) == 1); - _map[path] = handle; - _numInUse++; - LOG_DEBUG(LOG, "numInUse %u", _numInUse); - newFile = true; - } else { - LOG_DEBUG(LOG, "found"); - handle = it->second; - assert(_files[handle] != NULL); - int refCount = _files[handle]->incRefCount(); - if (refCount == 1) { - _numInUse++; - LOG_DEBUG(LOG, "resurrecting zombie, numInUse %u", _numInUse); - } - newFile = false; - } - } - - LOG_DEBUG(LOG, "allocate returns %d, newFile %d", handle, newFile); - return handle; -} - -void ZkFuseHandleManager::deallocate(Handle handle) -{ - LOG_DEBUG(LOG, "deallocate(handle %d)", handle); - - if (handle >= 0) { - bool reclaim = false; - ZkFuseFilePtr file; - { - AutoLock lock(_mutex); - file = _files[handle]; - assert(file != NULL); - int refCount = file->decRefCount(); - const std::string & path = file->getPath(); - LOG_DEBUG(LOG, "path %s ref count %d", path.c_str(), refCount); - if (refCount == 0) { - _numInUse--; - unsigned numCached = _files.size() - _numInUse; - if (numCached > _common.getCacheSize()) { - LOG_TRACE(LOG, - "reclaim path %s, cacheSize %u, filesSize %zu, " - "numInUse %u", - path.c_str(), - _common.getCacheSize(), _files.size(), _numInUse); - _map.erase(path); - _files[handle] = NULL; - _freeList.push_back(handle); - reclaim = true; - } - } - } - if (reclaim) { - delete file; - } - } - else { - LOG_DEBUG(LOG, "handle invalid"); - } - - LOG_DEBUG(LOG, "deallocate done"); -} - -void ZkFuseHandleManager::eventReceived(const ZKWatcherEvent & event) -{ - int eventType = event.getType(); - int eventState = event.getState(); - const std::string & path = event.getPath(); - LOG_DEBUG(LOG, "eventReceived() eventType %d, eventState %d, path %s", - eventType, eventState, path.c_str()); - - if (eventType == ZOO_DELETED_EVENT || - eventType == ZOO_CHANGED_EVENT || - eventType == ZOO_CHILD_EVENT) { - { - AutoLock lock(_mutex); - Map::iterator it = _map.find(path); - if (it != _map.end()) { - LOG_DEBUG(LOG, "path found"); - Handle handle = it->second; - ZkFuseFilePtr file = _files[handle]; - assert(file != NULL); - /* Prevent the ZkFuseFile instance from being - * deleted while handling the event. - */ - int refCount = file->incRefCount(); - if (refCount == 1) { - _numInUse++; - } - /* Pretent to be dir open. - */ - int dirCount = file->incOpenDirCount(); - { - /* _mutex is unlocked in this scope */ - AutoUnlockTemp autoUnlockTemp(lock); - if (eventType == ZOO_CHILD_EVENT) { - file->childrenEventReceived(event); - } - else if (eventType == ZOO_CHANGED_EVENT) { - file->dataEventReceived(event); - } - else { - assert(eventType == ZOO_DELETED_EVENT); - file->dataEventReceived(event); - // file->childrenEventReceived(event); - } - file->decOpenDirCount(); - deallocate(handle); - } - } - else { - LOG_WARN(LOG, - "path %s not found for event type %d, event state %d", - path.c_str(), eventType, eventState); - } - } - } - else if (eventType == ZOO_SESSION_EVENT) { - if (eventState == ZOO_CONNECTING_STATE) { - LOG_TRACE(LOG, "*** CONNECTING ***"); - { - AutoLock lock(_mutex); - for (int handle = 0; handle < _files.size(); handle++) { - ZkFuseFilePtr file = _files[handle]; - if (file != NULL) { - /* prevent the ZkFuseFile instance from being - * deleted while handling the event. - */ - int refCount = file->incRefCount(); - if (refCount == 1) { - _numInUse++; - } - /* Pretent to be dir open. - */ - int dirCount = file->incOpenDirCount(); - { - /* _mutex is unlocked in this scope */ - AutoUnlockTemp autoUnlockTemp(lock); - file->dataEventReceived(event); - file->childrenEventReceived(event); - file->decOpenDirCount(); - deallocate(handle); - } - /* this will eventually call decrement ref count */ - } - } - } - } - else if (eventState == ZOO_CONNECTED_STATE) { - LOG_TRACE(LOG, "*** CONNECTED ***"); - } - } - else { - LOG_WARN(LOG, - "eventReceived ignoring event type %d, event state %d, " - "path %s", eventType, eventState, path.c_str()); - } -} - -int ZkFuseHandleManager::getData(const std::string & path, - Data & data) -{ - LOG_DEBUG(LOG, "getData(path %s)", path.c_str()); - - int res = 0; - data.clear(); - ZkFuseAutoHandle autoHandle(SharedPtr(_thisWeakPtr), path); - res = autoHandle.get(); - if (res >= 0) { - autoHandle.getFile()->getData(data); - res = 0; - } - - LOG_DEBUG(LOG, "getData returns %d", res); - return res; -} - -int ZkFuseHandleManager::setData(const std::string & path, - const Data & data, - bool exists, - bool doFlush) -{ - LOG_DEBUG(LOG, "setData(path %s, exists %d)\n%s", - path.c_str(), exists, data.c_str()); - - int res = 0; - if (exists) { - res = open(path, false); - } else { - bool created; - res = mknod(path, S_IFREG, true, created); - } - if (res >= 0) { - ZkFuseAutoHandle autoHandle(SharedPtr(_thisWeakPtr), res); - res = autoHandle.getFile()->setData(data, doFlush); - } - - LOG_DEBUG(LOG, "setData returns %d", res); - return res; -} - -int ZkFuseHandleManager::mknod(const std::string & path, - mode_t mode, - bool mayExist, - bool & created) -{ - LOG_DEBUG(LOG, "mknod(path %s, mode %o, mayExist %d)", - path.c_str(), mode, mayExist); - - int res = 0; - created = false; - try { - if (S_ISREG(mode) == false && S_ISDIR(mode) == false) { - LOG_DEBUG(LOG, "bad mode %o", mode); - res = -EINVAL; - } - else { - Data data; - LOG_DEBUG(LOG, "create %s", path.c_str()); - created = - _common.getZkAdapter()->createNode(path, data, 0, false); - if (created) { - LOG_DEBUG(LOG, "created"); - if (S_ISDIR(mode)) { - /* is mkdir - create directory marker */ - std::string dirMetaPath = ZkFuseFile::buildChildPath - (path, _common.getDirMetadataName()); - LOG_DEBUG(LOG, "create %s", dirMetaPath.c_str()); - bool created; - int metaRes = mknod(dirMetaPath, S_IFREG, true, created); - if (metaRes >= 0) { - getFile(metaRes)->close(); - } - } - addChildToParent(path); - LOG_DEBUG(LOG, "open after create"); - res = open(path, true); - } else { - LOG_DEBUG(LOG, "create failed"); - int openRes = open(path, false); - if (openRes >= 0) { - if (mayExist == false) { - LOG_DEBUG(LOG, "create failed because already exist"); - getFile(openRes)->close(); - res = -EEXIST; - } else { - res = openRes; - } - } else { - LOG_DEBUG(LOG, "create failed but does not exist"); - res = -ENOENT; - } - } - } - } catch (const ZooKeeperException & e) { - LOG_ERROR(LOG, "mknod %s exception %s", path.c_str(), e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "mknod returns %d created %d", res, created); - return res; -} - -int ZkFuseHandleManager::mkdir(const char * path, mode_t mode) -{ - LOG_DEBUG(LOG, "mkdir(path %s, mode %o)", path, mode); - - int res = 0; - try { - ZkFuseNameType nameType; - std::string zkPath = getZkPath(path, nameType); - mode = (mode & ~S_IFMT) | S_IFDIR; - ZkFuseAutoHandle autoHandle - (SharedPtr(_thisWeakPtr), zkPath, mode, false); - res = autoHandle.get(); - if (res >= 0) { - res = 0; - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "mkdir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "mkdir returns %d", res); - return res; -} - -int ZkFuseHandleManager::open(const std::string & path, bool justCreated) -{ - LOG_DEBUG(LOG, "open(path %s, justCreated %d)", - path.c_str(), justCreated); - - int res = 0; - try { - bool newFile; - Handle handle = allocate(path, newFile); - ZkFuseAutoHandle autoHandle(SharedPtr(_thisWeakPtr), handle); - res = getFile(handle)->update(newFile || justCreated); - if (res == 0) { - res = handle; - autoHandle.release(); - } - } catch (const ZooKeeperException & e) { - LOG_ERROR(LOG, "open %s exception %s", path.c_str(), e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "open returns %d", res); - return res; -} - -int ZkFuseHandleManager::rmdir(const char * path, bool force) -{ - LOG_DEBUG(LOG, "rmdir(path %s, force %d)", path, force); - - int res = 0; - - try { - ZkFuseNameType nameType; - std::string zkPath = getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(SharedPtr(_thisWeakPtr), zkPath); - res = autoHandle.get(); - if (res >= 0) { - res = autoHandle.getFile()->rmdir(nameType, force); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "rmdir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "rmdir returns %d", res); - return res; -} - - -int -ZkFuseHandleManager::unlink(const char * path) -{ - LOG_DEBUG(LOG, "unlink(path %s)", path); - - ZkFuseNameType nameType; - std::string zkPath = getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(SharedPtr(_thisWeakPtr), zkPath); - int res = autoHandle.get(); - if (res >= 0) { - res = autoHandle.getFile()->unlink(nameType); - } - - LOG_DEBUG(LOG, "unlink returns %d", res); - return res; -} - -int ZkFuseHandleManager::getattr(const char *path, struct stat &stbuf) -{ - LOG_DEBUG(LOG, "getattr(path %s)", path); - - int res = 0; - try { - ZkFuseNameType nameType; - std::string zkPath = getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(SharedPtr(_thisWeakPtr), zkPath); - res = autoHandle.get(); - if (res >= 0) { - res = autoHandle.getFile()->getattr(stbuf, nameType); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "getattr %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "getattr returns %d", res); - return res; -} - -int -ZkFuseHandleManager::rename(const char * fromPath, const char * toPath) -{ - LOG_DEBUG(LOG, "rename(fromPath %s, toPath %s)", fromPath, toPath); - - ZkFuseNameType fromNameType; - std::string fromZkPath = getZkPath(fromPath, fromNameType); - ZkFuseAutoHandle fromAutoHandle(SharedPtr(_thisWeakPtr), fromZkPath); - int res = fromAutoHandle.get(); - if (res >= 0) { - LOG_DEBUG(LOG, "good fromPath"); - if (fromAutoHandle.getFile()->isDirNameType(fromNameType)) { - LOG_DEBUG(LOG, "fromPath is directory"); - res = -EISDIR; - } - } - if (res >= 0) { - ZkFuseNameType toNameType; - std::string toZkPath = getZkPath(toPath, toNameType); - bool created; - res = mknod(toZkPath.c_str(), S_IFREG, true, created); - if (res >= 0) { - ZkFuseAutoHandle toAutoHandle(SharedPtr(_thisWeakPtr), res); - if (toAutoHandle.getFile()->isDirNameType(toNameType)) { - LOG_DEBUG(LOG, "toPath is directory"); - res = -EISDIR; - } - if (res >= 0) { - LOG_DEBUG(LOG, "copy data"); - Data data; - fromAutoHandle.getFile()->getData(data); - toAutoHandle.getFile()->setData(data, true); - LOG_DEBUG(LOG, "copy metadata"); - struct stat stbuf; - int metaRes = - fromAutoHandle.getFile()->getattr(stbuf, fromNameType); - if (metaRes < 0) { - LOG_DEBUG(LOG, "get metadata failed"); - } - else { - metaRes = toAutoHandle.getFile()-> - utime(secsToMillisecs(stbuf.st_atime), - secsToMillisecs(stbuf.st_mtime), - toNameType); - if (metaRes < 0) { - LOG_DEBUG(LOG, "set metadata failed"); - } - } - } - if (created && res < 0) { - LOG_DEBUG(LOG, "undo create because copy data failed"); - int rmRes = toAutoHandle.getFile()->rmdir(toNameType, true); - } - } - } - if (res >= 0) { - LOG_DEBUG(LOG, "copy successful, unlink fromPath"); - res = fromAutoHandle.getFile()->unlink(fromNameType); - } - - LOG_DEBUG(LOG, "rename returns %d", res); - return res; -} - -void -ZkFuseHandleManager::addChildToParent(const std::string & childPath) const -{ - LOG_DEBUG(LOG, "addChildToParent(childPath %s)", childPath.c_str()); - - std::string parentPath = getParentPath(childPath); - if (!parentPath.empty()) { - AutoLock lock(_mutex); - Map::const_iterator it = _map.find(parentPath); - if (it != _map.end()) { - Handle handle = it->second; - assert(_files[handle] != NULL); - _files[handle]->addChild(childPath); - } - } - - LOG_DEBUG(LOG, "addChildToParent done"); -} - -void -ZkFuseHandleManager::removeChildFromParent(const std::string & childPath) const -{ - LOG_DEBUG(LOG, "removeChildFromParent(childPath %s)", childPath.c_str()); - - std::string parentPath = getParentPath(childPath); - if (!parentPath.empty()) { - AutoLock lock(_mutex); - Map::const_iterator it = _map.find(parentPath); - if (it != _map.end()) { - Handle handle = it->second; - assert(_files[handle] != NULL); - _files[handle]->removeChild(childPath); - } - } - - LOG_DEBUG(LOG, "removeChildFromParent done"); -} - -std::string -ZkFuseHandleManager::getParentPath(const std::string & childPath) const -{ - std::string::size_type lastPos = childPath.rfind('/'); - if (lastPos > 0) { - return std::string(childPath, 0, lastPos); - } - else { - assert(childPath[0] == '/'); - return std::string(); - } -} - -std::string -ZkFuseHandleManager::getZkPath(const char * path, ZkFuseNameType & nameType) - const -{ - LOG_DEBUG(LOG, "getZkPath(path %s)", path); - - std::string res; - unsigned pathLen = strlen(path); - const std::string & dataFileName = _common.getDataFileName(); - unsigned dataSuffixLen = dataFileName.length(); - const char * dataSuffix = dataFileName.c_str(); - unsigned dataSuffixIncludeSlashLen = dataSuffixLen + 1; - const std::string & forceDirSuffix = _common.getForceDirSuffix(); - unsigned forceDirSuffixLen = _common.getForceDirSuffix().length(); - /* Check if path is "/". If so, it is always a directory. - */ - if (pathLen == 1) { - assert(path[0] == '/'); - res = _common.getRootPathName(); - nameType = ZkFuseNameDirType; - } - /* Check if path ends of /{dataSuffix}, e.g. /foo/bar/{dataSuffix}. - * If so remove dataSuffix and nameType is ZkFuseNameRegType. - */ - else if ( - (pathLen >= dataSuffixIncludeSlashLen) && - (path[pathLen - dataSuffixIncludeSlashLen] == '/') && - (strncmp(path + (pathLen - dataSuffixLen), - dataSuffix, dataSuffixLen) == 0) - ) { - if ((pathLen - dataSuffixIncludeSlashLen) == 0) { - res = _common.getRootPathName(); - } else { - res.assign(path, pathLen - dataSuffixIncludeSlashLen); - } - nameType = ZkFuseNameRegType; - } - /* If not ZkFuseNameRegType, then check if path ends of - * {forceDirSuffix}, e.g. /foo/bar{forceDirSuffix}. - * If so remove forceDirSuffix and nameType is ZkFuseNameDirType. - */ - else if (forceDirSuffixLen > 0 && - pathLen >= forceDirSuffixLen && - strncmp(path + (pathLen - forceDirSuffixLen), - forceDirSuffix.c_str(), forceDirSuffixLen) == 0) { - res.assign(path, pathLen - forceDirSuffixLen); - nameType = ZkFuseNameDirType; - } - /* If not ZkFuseNameRegType and not ZkFuseNameDirType, then - * it is ZkFuseNameDefaultType. ZkFuse will infer type from - * ZooKeeper node's content. - */ - else { - res = path; - nameType = ZkFuseNameDefaultType; - } - /* Intermediate components of the path name may have - * forceDirSuffix, e.g. /foo/bar{forceDirSuffix}/baz. - * If so, remove the intermediate {forceDirSuffix}es. - */ - if (forceDirSuffixLen > 0) { - /* pos is an optimization to avoid always scanning from - * beginning of path - */ - unsigned pos = 0; - while ((res.length() - pos) > forceDirSuffixLen + 1) { - const char * found = - strstr(res.c_str() + pos, forceDirSuffix.c_str()); - if (found == NULL) { - break; - } - if (found[forceDirSuffixLen] == '/' || - found[forceDirSuffixLen] == '\0') { - pos = found - res.c_str(); - res.erase(pos, forceDirSuffixLen); - } - else { - pos += forceDirSuffixLen; - } - } - } - - LOG_DEBUG(LOG, "getZkPath returns %s, nameType %d", - res.c_str(), int(nameType)); - return res; -} - -static ZkFuseHandleManager::SharedPtr singletonZkFuseHandleManager; - -inline const ZkFuseHandleManager::SharedPtr & zkFuseHandleManager() -{ - return singletonZkFuseHandleManager; -} - -static -int zkfuse_getattr(const char *path, struct stat *stbuf) -{ - LOG_DEBUG(LOG, "zkfuse_getattr(path %s)", path); - - int res = 0; - try { - res = zkFuseHandleManager()->getattr(path, *stbuf); - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_getattr %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_getattr returns %d", res); - return res; -} - -static -int zkfuse_fgetattr(const char *path, struct stat *stbuf, - struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_fgetattr(path %s)", path); - - int res = 0; - int handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - res = zkFuseHandleManager()->getFile(handle)-> - getattr(*stbuf, ZkFuseNameDefaultType); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_fgetattr %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_fgetattr returns %d", res); - return res; -} - -static -int zkfuse_access(const char *path, int mask) -{ - /* not implemented */ - return -1; -} - -static -int zkfuse_readlink(const char *path, char *buf, size_t size) -{ - /* not implemented */ - return -1; -} - -static -int zkfuse_opendir(const char *path, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_opendir(path %s)", path); - - int res = 0; - try { - ZkFuseNameType nameType; - std::string zkPath = zkFuseHandleManager()->getZkPath(path, nameType); - if (nameType == ZkFuseNameRegType) { - res = -ENOENT; - } - else { - ZkFuseAutoHandle autoHandle(zkFuseHandleManager(), zkPath); - res = autoHandle.get(); - if (res >= 0) { - autoHandle.getFile()->incOpenDirCount(); - autoHandle.release(); - fi->fh = res; - res = 0; - } - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_opendir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_opendir returns %d", res); - return res; -} - -static int -zkfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_readdir(path %s, offset %zu)", path, offset); - - int res = 0; - int handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - res = zkFuseHandleManager()->getFile(handle)-> - readdir(buf, filler, offset); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_readdir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_readdir returns %d", res); - return res; -} - -static -int zkfuse_releasedir(const char *path, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_releasedir(path %s)", path); - - int res = 0; - unsigned handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - zkFuseHandleManager()->getFile(handle)->decOpenDirCount(); - zkFuseHandleManager()->getFile(handle)->close(); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_releasedir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_releasedir returns %d", res); - return res; -} - -static -int zkfuse_mknod(const char *path, mode_t mode, dev_t rdev) -{ - LOG_DEBUG(LOG, "zkfuse_mknod(path %s, mode %o)", path, mode); - - int res = 0; - try { - ZkFuseNameType nameType; - std::string zkPath = zkFuseHandleManager()->getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(zkFuseHandleManager(), zkPath, mode, false); - res = autoHandle.get(); - if (res >= 0) { - res = 0; - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_mknod %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_mknod returns %d", res); - return res; -} - -static int zkfuse_mkdir(const char *path, mode_t mode) -{ - LOG_DEBUG(LOG, "zkfuse_mkdir(path %s, mode %o", path, mode); - - int res = 0; - try { - res = zkFuseHandleManager()->mkdir(path, mode); - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_mkdir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_mkdir returns %d", res); - return res; -} - -static int zkfuse_unlink(const char *path) -{ - LOG_DEBUG(LOG, "zkfuse_unlink(path %s)", path); - - int res = 0; - try { - res = zkFuseHandleManager()->unlink(path); - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_unlink %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_unlink returns %d", res); - return res; -} - -static int zkfuse_rmdir(const char *path) -{ - LOG_DEBUG(LOG, "zkfuse_rmdir(path %s)", path); - - int res = 0; - try { - res = zkFuseHandleManager()->rmdir(path); - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_rmdir %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_rmdir returns %d", res); - - return res; -} - -static int zkfuse_symlink(const char *from, const char *to) -{ - /* not implemented */ - return -1; -} - -static int zkfuse_rename(const char *from, const char *to) -{ - LOG_DEBUG(LOG, "zkfuse_rename(from %s, to %s)", from, to); - - int res = 0; - try { - res = zkFuseHandleManager()->rename(from, to); - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_rename %s %s exception %s", from, to, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_rename returns %d", res); - - return res; -} - -static int zkfuse_link(const char *from, const char *to) -{ - /* not implemented */ - return -1; -} - -static int zkfuse_chmod(const char *path, mode_t mode) -{ - LOG_DEBUG(LOG, "zkfuse_chmod(path %s, mode %o)", path, mode); - int res = 0; - - LOG_DEBUG(LOG, "zkfuse_chmod returns %d", res); - return res; -} - -static int zkfuse_chown(const char *path, uid_t uid, gid_t gid) -{ - LOG_DEBUG(LOG, "zkfuse_chown(path %s, uid %d, gid %d)", path, uid, gid); - - int res = 0; - - if (zkFuseHandleManager()->getCommon().getUid() == uid && - zkFuseHandleManager()->getCommon().getGid() == gid) { - res = 0; - } - else { - res = -EPERM; - } - - LOG_DEBUG(LOG, "zkfuse_chown returns %d", res); - return 0; -} - -static int zkfuse_truncate(const char *path, off_t size) -{ - LOG_DEBUG(LOG, "zkfuse_truncate(path %s, size %zu)", path, size); - - int res = 0; - try { - ZkFuseNameType nameType; - std::string zkPath = zkFuseHandleManager()->getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(zkFuseHandleManager(), zkPath); - res = autoHandle.get(); - if (res >= 0) { - res = autoHandle.getFile()->truncate(size); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_truncate %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_truncate returns %d", res); - return res; -} - -static -int zkfuse_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_ftruncate(path %s, size %zu)", path, size); - - int res = 0; - unsigned handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - res = zkFuseHandleManager()->getFile(handle)->truncate(size); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_ftruncate %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_ftruncate returns %d", res); - return res; -} - -static -int zkfuse_utimens(const char *path, const struct timespec ts[2]) -{ - LOG_DEBUG(LOG, "zkfuse_utimens(path %s)", path); - - int res = 0; - try { - uint64_t atime = timespecToMillisecs(ts[0]); - uint64_t mtime = timespecToMillisecs(ts[1]); - ZkFuseNameType nameType; - std::string zkPath = zkFuseHandleManager()->getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(zkFuseHandleManager(), zkPath); - res = autoHandle.get(); - if (res >= 0) { - res = autoHandle.getFile()->utime(atime, mtime, nameType); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_utimens %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_utimens returns %d", res); - return res; -} - -static -int zkfuse_create(const char *path, mode_t mode, struct fuse_file_info *fi) -{ - int fd; - - fd = open(path, fi->flags, mode); - if (fd == -1) - return -errno; - - fi->fh = fd; - return 0; -} - -static -int zkfuse_open(const char *path, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_open(path %s, flags %o)", path, fi->flags); - - int res = 0; - try { - ZkFuseNameType nameType; - std::string zkPath = zkFuseHandleManager()->getZkPath(path, nameType); - ZkFuseAutoHandle autoHandle(zkFuseHandleManager(), zkPath); - res = autoHandle.get(); - if (res >= 0) { - if (autoHandle.getFile()->isDirNameType(nameType)) { - res = -ENOENT; - } - } - if (res >= 0) { - autoHandle.release(); - fi->fh = res; - res = 0; - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_open %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_open returns %d", res); - return res; -} - -static -int zkfuse_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_read(path %s, size %zu, offset %zu)", - path, size, offset); - - int res = 0; - unsigned handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - res = zkFuseHandleManager()->getFile(handle)-> - read(buf, size, offset); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_read %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_read returns %d", res); - return res; -} - -static -int zkfuse_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_write(path %s, size %zu, offset %zu)", - path, size, offset); - - int res = 0; - unsigned handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - res = zkFuseHandleManager()->getFile(handle)-> - write(buf, size, offset); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_write %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_write returns %d", res); - return res; -} - -static int zkfuse_statfs(const char *path, struct statvfs *stbuf) -{ - /* not implemented */ - return -1; -} - -static -int zkfuse_flush(const char *path, struct fuse_file_info *fi) -{ - /* This is called from every close on an open file, so call the - close on the underlying filesystem. But since flush may be - called multiple times for an open file, this must not really - close the file. This is important if used on a network - filesystem like NFS which flush the data/metadata on close() */ - - LOG_DEBUG(LOG, "zkfuse_flush(path %s)", path); - - int res = 0; - unsigned handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - res = zkFuseHandleManager()->getFile(handle)->flush(); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_flush %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_flush returns %d", res); - return res; -} - -static -int zkfuse_release(const char *path, struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_release(path %s)", path); - - int res = 0; - unsigned handle = fi->fh; - try { - if (handle <= 0) { - res = -EINVAL; - } - else { - zkFuseHandleManager()->getFile(handle)->close(); - } - } catch (const std::exception & e) { - LOG_ERROR(LOG, "zkfuse_release %s exception %s", path, e.what()); - res = -EIO; - } - - LOG_DEBUG(LOG, "zkfuse_release returns %d", res); - return res; -} - -static -int zkfuse_fsync(const char *path, int isdatasync, - struct fuse_file_info *fi) -{ - LOG_DEBUG(LOG, "zkfuse_fsync(path %s, isdatasync %d)", path, isdatasync); - - (void) isdatasync; - int res = zkfuse_flush(path, fi); - - LOG_DEBUG(LOG, "zkfuse_fsync returns %d", res); - return res; -} - -#ifdef HAVE_SETXATTR -/* xattr operations are optional and can safely be left unimplemented */ -static int zkfuse_setxattr(const char *path, const char *name, const char *value, - size_t size, int flags) -{ - int res = lsetxattr(path, name, value, size, flags); - if (res == -1) - return -errno; - return 0; -} - -static int zkfuse_getxattr(const char *path, const char *name, char *value, - size_t size) -{ - int res = lgetxattr(path, name, value, size); - if (res == -1) - return -errno; - return res; -} - -static int zkfuse_listxattr(const char *path, char *list, size_t size) -{ - int res = llistxattr(path, list, size); - if (res == -1) - return -errno; - return res; -} - -static int zkfuse_removexattr(const char *path, const char *name) -{ - int res = lremovexattr(path, name); - if (res == -1) - return -errno; - return 0; -} -#endif /* HAVE_SETXATTR */ - -static -int zkfuse_lock(const char *path, struct fuse_file_info *fi, int cmd, - struct flock *lock) -{ - (void) path; - return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner, - sizeof(fi->lock_owner)); -} - - -static -void init_zkfuse_oper(fuse_operations & fo) -{ - memset(&fo, 0, sizeof(fuse_operations)); - fo.getattr = zkfuse_getattr; - fo.fgetattr = zkfuse_fgetattr; - // fo.access = zkfuse_access; - // fo.readlink = zkfuse_readlink; - fo.opendir = zkfuse_opendir; - fo.readdir = zkfuse_readdir; - fo.releasedir = zkfuse_releasedir; - fo.mknod = zkfuse_mknod; - fo.mkdir = zkfuse_mkdir; - // fo.symlink = zkfuse_symlink; - fo.unlink = zkfuse_unlink; - fo.rmdir = zkfuse_rmdir; - fo.rename = zkfuse_rename; - // fo.link = zkfuse_link; - fo.chmod = zkfuse_chmod; - fo.chown = zkfuse_chown; - fo.truncate = zkfuse_truncate; - fo.ftruncate = zkfuse_ftruncate; - fo.utimens = zkfuse_utimens; - // fo.create = zkfuse_create; - fo.open = zkfuse_open; - fo.read = zkfuse_read; - fo.write = zkfuse_write; - fo.statfs = zkfuse_statfs; - fo.flush = zkfuse_flush; - fo.release = zkfuse_release; - fo.fsync = zkfuse_fsync; -#ifdef HAVE_SETXATTR - // fo.setxattr = zkfuse_setxattr; - // fo.getxattr = zkfuse_getxattr; - // fo.listxattr = zkfuse_listxattr; - // fo.removexattr = zkfuse_removexattr; -#endif - fo.lock = zkfuse_lock; -}; - - -/** - * The listener of ZK events. - */ -class SessionEventListener : public ZKEventListener -{ - private: - /** - References the ZkFuseHandleManager instance that should be - invoked to service events. - */ - ZkFuseHandleManager::SharedPtr _manager; - - public: - /** - Sets the ZkFuseHandleManager instance that should be invoked - to service events. - */ - void setManager(const ZkFuseHandleManager::SharedPtr & manager) - { - _manager = manager; - } - /** - Received an event and invoke ZkFuseHandleManager instance to handle - received event. - */ - virtual void eventReceived(const ZKEventSource & source, - const ZKWatcherEvent & event) - { - _manager->eventReceived(event); - } -}; - -void -usage(int argc, char *argv[]) -{ - cout - << argv[0] - << " usage: " - << argv[0] - << " [args-and-values]+" << endl - << "nodepath == a complete path to a ZooKeeper node" << endl - << "\t--cachesize= or -c :" << endl - << " number of ZooKeeper nodes to cache." << endl - << "\t--debug or -d: " << endl - << "\t enable fuse debug mode." << endl - << "\t--help or -h: " << endl - << "\t print this message." << endl - << "\t--mount= or -m : " << endl - << "\t specifies where to mount the zkfuse filesystem." << endl - << "\t--name or -n: " << endl - << "\t name of file for accessing node data." << endl - << "\t--zookeeper= or -z : " << endl - << "\t specifies information needed to connect to zeekeeper." << endl; -} - -int -main(int argc, char *argv[]) -{ - /** - * Initialize log4cxx - */ - const std::string file("log4cxx.properties"); - PropertyConfigurator::configureAndWatch( file, 5000 ); - LOG_INFO(LOG, "Starting zkfuse"); - - /** - * Supported operations. - */ - enum ZkOption { - ZkOptionCacheSize = 1000, - ZkOptionDebug = 1001, - ZkOptionForceDirSuffix = 1002, - ZkOptionHelp = 1003, - ZkOptionMount = 1004, - ZkOptionName = 1005, - ZkOptionZookeeper = 1006, - ZkOptionInvalid = -1 - }; - - static const char *shortOptions = "c:df:hm:n:z:"; - static struct option longOptions[] = { - { "cachesize", 1, 0, ZkOptionCacheSize }, - { "debug", 0, 0, ZkOptionDebug }, - { "forcedirsuffix", 1, 0, ZkOptionForceDirSuffix }, - { "help", 0, 0, ZkOptionHelp }, - { "mount", 1, 0, ZkOptionMount }, - { "name", 1, 0, ZkOptionName }, - { "zookeeper", 1, 0, ZkOptionZookeeper }, - { 0, 0, 0, 0 } - }; - - /** - * Parse arguments - */ - bool debugFlag = false; - std::string mountPoint = "/tmp/zkfuse"; - std::string nameOfFile = "_data_"; - std::string forceDirSuffix = "._dir_"; - std::string zkHost; - unsigned cacheSize = 256; - - while (true) { - int c; - - c = getopt_long(argc, argv, shortOptions, longOptions, 0); - if (c == -1) { - break; - } - - switch (c) { - case ZkOptionInvalid: - cerr - << argv[0] - << ": ERROR: Did not specify legal argument!" - << endl; - return 99; - case 'c': - case ZkOptionCacheSize: - cacheSize = strtoul(optarg, NULL, 0); - break; - case 'd': - case ZkOptionDebug: - debugFlag = true; - break; - case 'f': - case ZkOptionForceDirSuffix: - forceDirSuffix = optarg; - break; - case 'h': - case ZkOptionHelp: - usage(argc, argv); - return 0; - case 'm': - case ZkOptionMount: - mountPoint = optarg; - break; - case 'n': - case ZkOptionName: - nameOfFile = optarg; - break; - case 'z': - case ZkOptionZookeeper: - zkHost = optarg; - break; - } - } - - /** - * Check that zkHost has a value, otherwise abort. - */ - if (zkHost.empty()) { - cerr - << argv[0] - << ": ERROR: " - << "required argument \"--zookeeper \" was not given!" - << endl; - return 99; - } - /** - * Check that zkHost has a value, otherwise abort. - */ - if (forceDirSuffix.empty()) { - cerr - << argv[0] - << ": ERROR: " - << "required argument \"--forcedirsuffix \" " - "not cannot be empty!" - << endl; - return 99; - } - /** - * Check nameOfFile has no forward slash - */ - if (nameOfFile.find_first_of('/') != std::string::npos) { - cerr - << argv[0] - << ": ERROR: " - << "'/' present in name which is not allowed" - << endl; - return 99; - } - - if (debugFlag) { - cout - << "cacheSize = " - << cacheSize - << ", debug = " - << debugFlag - << ", forceDirSuffix = \"" - << forceDirSuffix - << "\", mount = \"" - << mountPoint - << "\", name = \"" - << nameOfFile - << "\", zookeeper = \"" - << zkHost - << "\", optind = " - << optind - << ", argc = " - << argc - << ", current arg = \"" - << (optind >= argc ? "NULL" : argv[optind]) - << "\"" - << endl; - } - - SessionEventListener listener; - SynchronousEventAdapter eventAdapter; - LOG_INFO(LOG, "Create ZK adapter"); - try { - /** - * Create an instance of ZK adapter. - */ - std::string h(zkHost); - ZooKeeperConfig config(h, 1000, true, 10000); - ZkFuseCommon zkFuseCommon; - ZooKeeperAdapterSharedPtr zkPtr( - new ZooKeeperAdapter( - config, - &listener, - false - ) - ); - zkFuseCommon.setZkAdapter(zkPtr); - zkFuseCommon.setDataFileName(nameOfFile); - zkFuseCommon.setForceDirSuffix(forceDirSuffix); - zkFuseCommon.setCacheSize(cacheSize); - singletonZkFuseHandleManager = - ZkFuseHandleManagerFactory::create(zkFuseCommon); - listener.setManager(singletonZkFuseHandleManager); - zkPtr->reconnect(); - - } catch (const ZooKeeperException & e) { - cerr - << argv[0] - << ": ERROR: ZookKeeperException caught: " - << e.what() - << endl; - } catch (std::exception & e) { - cerr - << argv[0] - << ": ERROR: std::exception caught: " - << e.what() - << endl; - } - -#ifdef ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG - cerr << "ZOOKEEPER_ROOT_CHILDREN_WATCH_BUG enabled" << endl; -#endif - /** - * Initialize fuse - */ - LOG_INFO(LOG, "Initialize fuse"); - umask(0); - fuse_operations zkfuse_oper; - init_zkfuse_oper(zkfuse_oper); - int fakeArgc = debugFlag ? 3 : 2; - char * fakeArgv[] = { - argv[0], - strdup(mountPoint.c_str()), - debugFlag ? strdup("-d") : NULL, - NULL - }; - int res = fuse_main(fakeArgc, fakeArgv, &zkfuse_oper, NULL); - for (unsigned i = 1; i <= 2; i++) { - if (fakeArgv[i] != NULL) { - free(fakeArgv[i]); - } - } - - return res; -} diff --git a/src/contrib/zkperl/Changes b/src/contrib/zkperl/Changes deleted file mode 100644 index 78dff453bac..00000000000 --- a/src/contrib/zkperl/Changes +++ /dev/null @@ -1,61 +0,0 @@ -Net::ZooKeeper - Perl extension for Apache ZooKeeper - -Revision history -================ - -0.01 Dec 5, 2008 - - initial version - -0.02 Dec 16, 2008 - - support connection to ZooKeeper and get() method - -0.03 Jan 9, 2009 - - implemented watch mechanism for get() - -0.04 Jan 15, 2009 - - all basic ZooKeeper methods supported - -0.05 Jan 21, 2009 - - converted from T_PTROBJ to T_ZK_HASH with PERL_MAGIC_ext, - allows DESTROY() to be called repeatedly - -0.06 Jan 27, 2009 - - converted from attribute accessor methods to inner and outer hashes - with PERL_MAGIC_tied - -0.07 Jan 29, 2009 - - all tied hash methods completed - -0.08 Jan 30, 2009 - - simple thread safety enforced with CLONE_SKIP - -0.09 Feb 12, 2009 - - ACL constants - -0.10 Feb 18, 2009 - - ACL support - -0.11 Feb 21, 2009 - - ZooKeeper version check - -0.20 Feb 25, 2009 - - refactored watches as subclass - -0.30 Feb 27, 2009 - - refactored stats as subclass - -0.31 Mar 6, 2009 - - test suite completed - -0.32 Mar 25, 2009 - - initial documentation completed, first public release - -0.33 Apr 20, 2009 - - copyright donated to ASF - -0.34 Jul 14, 2009 - - support ZooKeeper 3.2.0 release - -0.35 Jul 15, 2009 - - support multiple include and library locations - diff --git a/src/contrib/zkperl/LICENSE b/src/contrib/zkperl/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/src/contrib/zkperl/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/contrib/zkperl/MANIFEST b/src/contrib/zkperl/MANIFEST deleted file mode 100644 index 1b57436d4c4..00000000000 --- a/src/contrib/zkperl/MANIFEST +++ /dev/null @@ -1,23 +0,0 @@ -Changes -LICENSE -Makefile.PL -MANIFEST -NOTICE -README -typemap -ZooKeeper.pm -ZooKeeper.xs -build/check_zk_version.c -build/check_zk_version.h -t/10_invalid.t -t/15_thread.t -t/20_tie.t -t/22_stat_tie.t -t/24_watch_tie.t -t/30_connect.t -t/35_log.t -t/40_basic.t -t/45_class.t -t/50_access.t -t/60_watch.t -t/util.pl diff --git a/src/contrib/zkperl/Makefile.PL b/src/contrib/zkperl/Makefile.PL deleted file mode 100644 index d2b202e9f2d..00000000000 --- a/src/contrib/zkperl/Makefile.PL +++ /dev/null @@ -1,62 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use 5.008_008; - -use Config; -use ExtUtils::MakeMaker; -use Getopt::Long; - -my @zk_inc_paths; -my @zk_lib_paths; - -GetOptions( - 'zookeeper-include=s' => \@zk_inc_paths, - 'zookeeper-lib=s' => \@zk_lib_paths -); - -my $zk_inc_paths = join(' ', map("-I$_", @zk_inc_paths)); -my $zk_lib_paths = join(' ', map("-L$_", @zk_lib_paths)); - -$zk_inc_paths .= ' ' unless ($zk_inc_paths eq ''); -$zk_lib_paths .= ' ' unless ($zk_lib_paths eq ''); - -my $cc = $Config{'cc'}; -my $check_file = 'build/check_zk_version'; - -my $check_out = - qx($cc -c $zk_inc_paths -I. -c $check_file.c -o $check_file.o 2>&1); - -if ($?) { - if ($check_out =~ /zookeeper_version\.h/) { - die("Could not determine ZooKeeper version:\n\n$check_out"); - } - else { - ## keep in sync with build/check_zk_version.h - die("Net::ZooKeeper requires at least ZooKeeper version 3.1.1\n"); - } -} - -WriteMakefile( - 'INC' => "$zk_inc_paths-I.", - 'LIBS' => [ "$zk_lib_paths-lzookeeper_mt" ], - 'NAME' => 'Net::ZooKeeper', - 'VERSION_FROM' => 'ZooKeeper.pm', - 'clean' => { 'FILES' => 'build/check_zk_version.o' } -); - diff --git a/src/contrib/zkperl/NOTICE b/src/contrib/zkperl/NOTICE deleted file mode 100644 index b68fdac518e..00000000000 --- a/src/contrib/zkperl/NOTICE +++ /dev/null @@ -1,6 +0,0 @@ -Net::ZooKeeper - Perl extension for Apache ZooKeeper -Copyright 2009 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - diff --git a/src/contrib/zkperl/README b/src/contrib/zkperl/README deleted file mode 100644 index e2400189298..00000000000 --- a/src/contrib/zkperl/README +++ /dev/null @@ -1,80 +0,0 @@ -Net::ZooKeeper - Perl extension for Apache ZooKeeper -==================================================== - -Net::ZooKeeper provides a Perl interface to the synchronous C API -of Apache ZooKeeper. ZooKeeper is coordination service for -distributed applications and is a sub-project of the Apache Hadoop -project. For details see the ZooKeeper home page at: - -http://hadoop.apache.org/zookeeper/ - -INSTALLATION - -To install this module type the following: - - perl Makefile.PL \ - --zookeeper-include=/path/to/zookeeper/client/include \ - --zookeeper-lib=/path/to/zookeeper/client/lib - make - ZK_TEST_HOSTS=host:port,... make test - make install - -The path supplied to the --zookeeper-include option should -identify the directory that contains the zookeeper.h and other -ZooKeeper C include files. - -The path supplied to the --zookeeper-lib option should identify -the directory that contains the libzookeeper_mt library. - -When running "make test", if no ZK_TEST_HOSTS environment -variable is set, many tests will be skipped because no connection -to a ZooKeeper server is available. To execute these tests, -the ZK_TEST_HOSTS variable may be assigned a list of one or more -ZooKeeper host:port pairs, e.g., "localhost:7100,otherhost:7200". - -The ZK_TEST_PATH environment variable, if defined, specifies -the ZooKeeper path under which all test nodes should be created. -The tests expect to have full read/write/create/delete/admin -ZooKeeper permissions under this path. If no ZK_TEST_PATH -variable is defined, the root ZooKeeper path ("/") is used. - -DEPENDENCIES - -Version 3.1.1 of ZooKeeper is required at a minimum. - -For version 3.1.1, you may also want to apply some of these -additional patches to the ZooKeeper C API code: - -https://issues.apache.org/jira/browse/ZOOKEEPER-262 -https://issues.apache.org/jira/browse/ZOOKEEPER-318 - -For version 3.1.1, you may also want to apply some of these -additional patches to the ZooKeeper C API code: - -https://issues.apache.org/jira/browse/ZOOKEEPER-262 -https://issues.apache.org/jira/browse/ZOOKEEPER-466 - -This module requires that the multi-threaded version of the -ZooKeeper C API client library be available on your system. - -This in turn implies that the POSIX pthread library is available -as well. - -COPYRIGHT AND LICENCE - -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/src/contrib/zkperl/ZooKeeper.pm b/src/contrib/zkperl/ZooKeeper.pm deleted file mode 100644 index eeaa468e1da..00000000000 --- a/src/contrib/zkperl/ZooKeeper.pm +++ /dev/null @@ -1,1258 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use 5.008_008; - -use strict; -use warnings; - -package Net::ZooKeeper; - -require Exporter; -require XSLoader; - -our $VERSION = '0.35'; - -our @ISA = qw(Exporter); - -our %EXPORT_TAGS = ( - 'errors' => [qw( - ZOK - ZSYSTEMERROR - ZRUNTIMEINCONSISTENCY - ZDATAINCONSISTENCY - ZCONNECTIONLOSS - ZMARSHALLINGERROR - ZUNIMPLEMENTED - ZOPERATIONTIMEOUT - ZBADARGUMENTS - ZINVALIDSTATE - ZAPIERROR - ZNONODE - ZNOAUTH - ZBADVERSION - ZNOCHILDRENFOREPHEMERALS - ZNODEEXISTS - ZNOTEMPTY - ZSESSIONEXPIRED - ZINVALIDCALLBACK - ZINVALIDACL - ZAUTHFAILED - ZCLOSING - ZNOTHING - )], - 'node_flags' => [qw( - ZOO_EPHEMERAL - ZOO_SEQUENCE - )], - 'acl_perms' => [qw( - ZOO_PERM_READ - ZOO_PERM_WRITE - ZOO_PERM_CREATE - ZOO_PERM_DELETE - ZOO_PERM_ADMIN - ZOO_PERM_ALL - )], - 'acls' => [qw( - ZOO_OPEN_ACL_UNSAFE - ZOO_READ_ACL_UNSAFE - ZOO_CREATOR_ALL_ACL - )], - 'events' => [qw( - ZOO_CREATED_EVENT - ZOO_DELETED_EVENT - ZOO_CHANGED_EVENT - ZOO_CHILD_EVENT - ZOO_SESSION_EVENT - ZOO_NOTWATCHING_EVENT - )], - 'states' => [qw( - ZOO_EXPIRED_SESSION_STATE - ZOO_AUTH_FAILED_STATE - ZOO_CONNECTING_STATE - ZOO_ASSOCIATING_STATE - ZOO_CONNECTED_STATE - )], - 'log_levels' => [qw( - ZOO_LOG_LEVEL_OFF - ZOO_LOG_LEVEL_ERROR - ZOO_LOG_LEVEL_WARN - ZOO_LOG_LEVEL_INFO - ZOO_LOG_LEVEL_DEBUG - )] -); - -{ - my %tags; - - push @{$EXPORT_TAGS{'all'}}, - grep {!$tags{$_}++} @{$EXPORT_TAGS{$_}} foreach (keys(%EXPORT_TAGS)); -} - -our @EXPORT_OK = ( @{$EXPORT_TAGS{'all'}} ); - -XSLoader::load('Net::ZooKeeper', $VERSION); - -1; - -__END__ - -=head1 NAME - -Net::ZooKeeper - Perl extension for Apache ZooKeeper - -=head1 SYNOPSIS - - use Net::ZooKeeper qw(:node_flags :acls); - - my $zkh = Net::ZooKeeper->new('localhost:7000'); - - $zkh->create('/foo', 'bar', - 'flags' => ZOO_EPHEMERAL, - 'acl' => ZOO_OPEN_ACL_UNSAFE) or - die("unable to create node /foo: " . $zkh->get_error() . "\n"); - - print "node /foo has value: " . $zkh->get('/foo') . "\n"; - - $zkh->set('/foo', 'baz'); - - print "node / has child nodes:\n"; - foreach my $path ($zkh->get_children('/')) { - print " /$path\n"; - } - - my $stat = $zkh->stat(); - if ($zkh->exists('/foo', 'stat' => $stat)) { - print "node /foo has stat info:\n"; - while (my($key,$value) = each(%{$stat})) { - print " $key: $value\n"; - } - } - - foreach my $acl_entry ($zkh->get_acl('/foo')) { - print "node /foo has ACL entry:\n"; - print " perms: $acl_entry->{perms}\n"; - print " scheme: $acl_entry->{scheme}\n"; - print " id: $acl_entry->{id}\n"; - } - - my $watch = $zkh->watch('timeout' => 10000); - $zkh->exists('/foo', 'watch' => $watch); - - if ($watch->wait()) { - print "watch triggered on node /foo:\n"; - print " event: $watch->{event}\n"; - print " state: $watch->{state}\n"; - } - else { - print "watch timed out after 10 seconds\n"; - } - - $zkh->delete('/foo'); - -=head1 DESCRIPTION - -Net::ZooKeeper provides a Perl interface to the synchronous C API -of Apache ZooKeeper. ZooKeeper is coordination service for -distributed applications and is a sub-project of the Apache Hadoop -project. - -Each connection to ZooKeeper is represented as a handle object -of the class Net::ZooKeeper, similar to the manner in which database -connections are represented in the DBI module. - -To disconnect from ZooKeeper, simply destroy the Net::ZooKeeper -handle object by undefining it or by explicitly calling the -C method. - -The methods which may be invoked on Net::ZooKeeper handles -correspond to the functions of the synchronous ZooKeeper C API; -e.g., the Net::ZooKeeper method C calls the ZooKeeper -C function C, C calls C, -and so forth. - -The synchronous API functions wait for a response from the ZooKeeper -cluster before returning a result to the caller. Using these -functions permits Net::ZooKeeper to provide an interface similar -to that of a DBI driver module. - -=head2 Internal POSIX Threads - -The use of the synchronous ZooKeeper C API still requires that -the ZooKeeper C client code create several POSIX threads which run -concurrently with the main thread containing the Perl interpreter. - -The synchronous API functions are wrappers of the asynchronous -functions in the ZooKeeper C API. When a request is made by the -caller's thread (i.e., the one with the running Perl interpreter), -it is enqueued for delivery at a later time by the ZooKeeper C client -code's IO thread. The caller's thread then waits for notification -before returning from the synchronous API function. - -The IO thread dequeues the request and sends it to the ZooKeeper -cluster, while also ensuring that a regular "heartbeat" is maintained -with the cluster so that the current session does not time out. -When the IO thread receives a response from -the ZooKeeper cluster, it enqueues the response for delivery to the -client by the second thread of the ZooKeeper client code, the -completion thread. - -If the caller is using the asynchronous API, the completion thread -invokes the appropriate callback function provided by the caller -for the given request. In the case of Net::ZooKeeper, it is not -viable for the completion thread to invoke a Perl callback function -at arbitrary times; this could interfere with the state of the -Perl interpreter. - -For this reason Net::ZooKeeper uses the synchronous API only. After -enqueuing requests the synchronous API functions wait for notification -of the corresponding response. The completion thread delivers these -notifications, at which point the synchronous functions return to -their caller. - -Note that the IO and completion threads are POSIX threads, not -Perl ithreads. Net::ZooKeeper defined a C function so -that if Perl ithreads are spawned while a Net::ZooKeeper connection -is active, the Net::ZooKeeper handle objects inherited by the -spawned ithread contain undefined values so that they can not be used. -Thus each ithread will need to create its own private connections to a -ZooKeeper cluster. - -Note also that before invoking C to spawn a new process, -all Net::ZooKeeper handles should be destroyed so that all -connections to ZooKeeper are closed and all internal POSIX threads -have exited. If a child process needs to communicate with -ZooKeeper it should open its own private connections after it is -created by C. - -=head2 Signals - -The ZooKeeper C API uses TCP connections to communicate with -the ZooKeeper cluster. These connections may generate SIGPIPE -signals when they encounter errors, such as when a connection -is terminated by a ZooKeeper server. Therefore most applications -will want to trap or ignore SIGPIPE signals, e.g.: - - local $SIG{'PIPE'} = 'IGNORE'; - -Ignoring SIGPIPE signals (or providing a signal handler that returns -control to the interrupted program after receiving the signal) -will allow the ZooKeeper C client code to detect the connection error -and report it upon return from the next Net::ZooKeeper method. - -=head2 Error Handling - -Net::ZooKeeper methods return different values in the case of an -error depending on their purpose and context. For example, -C returns true if the node exists and false otherwise, -which may indicate either that the node does not exist or that -an error occurred. - -After any method returns a false, empty, or undefined value which -might indicate an error has occurred, the C method -may be called to examine the specific error code, if any. - -If C returns C, no error has occurred. If the -error code is less than C, it indicates a normal error -condition reported by the ZooKeeper server, such as C -(node does not exist) or C (node already exists). - -If the error code is greater than C, then a connection -error or server error has occurred and the client should probably -close the connection by undefining the Net::ZooKeeper handle object -and, if necessary, attempt to create a new connection to the -ZooKeeper cluster. - -=head2 Access Control - -If the ZooKeeper cluster is not configured with C then -it will respect the access controls set for each node in the -ZooKeeper hierarchy. These access controls are defined using ACLs -(Access Control Lists); see the ZooKeeper documentation for compete -details. - -In Net::ZooKeeper, ACLs are represented as arrays of hashes, where -each hash is an ACL entry that must contain three attributes, -C, C, and C. The C attribute's value -should be composed by combining ACL permission flags using the -bitwise OR operator. See C<:acl_perms> for a list of the -available ACL permission flags. - -The ACL for a node may be read using the C method. A -node's ACL may be set when the node is created by passing an ACL -array as the value of the C<'acl'> option to the C method, -and may be updated by passing an ACL array to the C method. - -When a client connects to a ZooKeeper cluster it is automatically -assigned authentication credentials based on its IP address. -Additional authentication credentials may be added using -the C method. Once a credential has been added for -the current session, there is no way to disable it. - -As an example, digest authentication may be enabled for a session -by calling C as follows: - - $zkh->add_auth('digest', "$username:$password"); - -Note that the username and password are transmitted in cleartext -to the ZooKeeper cluster. - -Such authentication credentials would enable access to a node -whose ACL contained an entry with a C attribute of -C<'digest'> and an C attribute containing a Base64-encoded -SHA1 digest of the string C<"$username:$password">. The -Perl modules Digest and MIME::Base64 may be used to create -such ACL ID values as follows: - - use Digest qw(); - use MIME::Base64 qw(); - - my $ctx = Digest->new('SHA-1')->add("$username:$password"); - my $digest = MIME::Base64::encode($ctx->digest()); - -Note that using the C method of the Digest module -will not result in digest strings with the "=" suffix characters -required by ZooKeeper. - -=head2 Logging - -As of ZooKeeper version 3.1.1, logging in the C client code is -implemented with a single, shared file handle to which all -of the internal POSIX threads write log messages; by default, -this file handle is attached to STDERR. - -Moreover, this file handle is shared by all active ZooKeeper -connections (each of which has its own private IO and completion -threads; see L above). - -Net::ZooKeeper therefore does not provide per-connection handle -attributes related to logging. The global function -C may be used to set the current -log level. See C<:log_levels> for a list of the available log -levels. The default log level is C. - -To capture ZooKeeper log messages to a file instead of STDERR, -redirect STDERR to a new file handle in the normal Perl manner: - - open(OLDERR, '>&', fileno(STDERR)) or - die("unable to dup STDERR: $!"); - open(STDERR, '>', $log_file) or - die("unable to redirect STDERR: $!"); - -=head2 Connection Order - -ZooKeeper clusters are typically made up of an odd number of -ZooKeeper servers. When connecting to such a cluster, the -C method should be passed a comma-separated list of -the hostnames and ports for each of the servers in the cluster, -e.g., C<'host1:7000,host2:7000,host2:7100'>. - -The default behaviour of the ZooKeeper client code is to -reorder this list randomly before making any connections. -A connection is then made to the first server in the reordered -list. If that connection fails, the IO thread will -automatically attempt to reconnect to the cluster, this time -to the next server in the list; when the last server in the list -is reached, the IO thread will continue again with the first -server. - -For certain purposes it may be necessary for ZooKeeper clients -to know the exact order in which the IO thread will attempt to -connect to the servers of a cluster. To do so, call -C. Note, -however, that this will affect all Net::ZooKeeper object -handles created by the current process. - -=head1 ATTRIBUTES - -=head2 Net::ZooKeeper - -The Net::ZooKeeper class provides the main interface to the -ZooKeeper client API. The following attributes are available -for each Net::ZooKeeper handle object and are specific to -that handle and the method calls invoked on it. As with DBI -handle objects, attributes may be read and written through -a hash interface, e.g.: - - print sprintf("Session timeout is %.2f seconds.\n", - $zkh->{session_timeout} / 1000); - - $zkh->{watch_timeout} = 10000; - -=over 4 - -=item hosts - -The comma-separated list of ZooKeeper server hostnames and ports -as passed to the C method. Note that by default the -ZooKeeper C client code will reorder this list before attempting -to connect for the first time; see L for details. - -This attribute is B and may not be modified. - -=item session_timeout - -The session timeout value, in milliseconds, as set by the -ZooKeeper server after connection. This value may not be -exactly the same as what was requested in the C<'session_timeout'> -option of the C method; the server will adjust the -requested timeout value so that it is within a certain range -of the server's C setting. See the ZooKeeper -documentation for details. - -Because the actual connection to the ZooKeeper server is -not made during the C method call but shortly -thereafter by the IO thread, note that this value may not -be initialized to its final value until at least one -other method which requires communication with the server -(such as C) has succeeded. - -This attribute is B and may not be modified. - -=item session_id - -The client's session ID value as set by the ZooKeeper server -after connection. This is a binary data string which may -be passed to subsequent C calls as the value of -the C<'session_id'> option, if the user wishes to attempt to -continue a session after a failure. Note that the server -may not honour such an attempt. - -Because the actual connection to the ZooKeeper server is -not made during the C method call but shortly -thereafter by the IO thread, note that this value may not -be initialized to its final value until at least one -other method which requires communication with the server -(such as C) has succeeded. - -This attribute is B and may not be modified. - -=item data_read_len - -The maximum length of node data that will be returned to -the caller by the C method. If a node's data exceeds -this length, the returned value will be shorter than the -actual node data as stored in the ZooKeeper cluster. - -The default maximum length of the node data returned by -C is 1023 bytes. This may be changed by setting -the C attribute to a different value. - -Passing a value for the C<'data_read_len'> option when calling -the C method will temporarily override the per-handle -maximum. - -=item path_read_len - -The maximum length of a newly created node's path that will -be returned to the caller by the C method. If the path -of the newly created node exceeds this length, the returned -value will be shorter than the actual path of the node as stored -in the ZooKeeper cluster. - -The default maximum length of the node path returned by -C is 1023 bytes. This may be changed by setting -the C attribute to a different value. - -Passing a value for the C<'path_read_len'> option when calling -the C method will temporarily override the current -value of this attribute. - -=item watch_timeout - -The C attribute value, in milliseconds, inherited by -all watch objects (of class Net::ZooKeeper::Watch) created by -calls to the C method. When a watch object's -C method is invoked without a C<'timeout'> option, -it waits for an event notification from the ZooKeeper cluster -for no longer than the timeout period specified by the value of -the watch object's C attribute. - -The default C attribute value for all watch objects -created by the C method is 1 minute (60000 -milliseconds). This may be changed for a particular handle -object by setting this attribute to a different value; afterwards, -the new value will be inherited by any watch objects created -by the handle object's C method. Previously -created watch objects will not be affected. - -Passing a value for the C<'timeout'> option when calling -the C method will temporarily override the current -value of this attribute and cause the newly created watch object -to inherit a different value. - -See also the C method, and the C attribute -and C method of the Net::ZooKeeper::Watch class. - -=item pending_watches - -The number of internal ZooKeeper watches created for this handle -object that are still awaiting an event notification from the -ZooKeeper cluster. - -Note that this number may be different than the number of -extant watch objects created by the handle object's C -method, not only because some event notifications may have -occurred, but also if any watch objects have been reassigned -by reusing them in more than one call to any of the C, -C, or C methods. - -This attribute is B and may not be modified. - -=back - -=head2 Net::ZooKeeper::Stat - -The Net::ZooKeeper::Stat class provides a hash interface to -the individual pieces of information which together compose the -state of a given ZooKeeper node. Net::ZooKeeper::Stat objects -are created by calling the C method on a Net::ZooKeeper -handle object, and may then be passed to any methods which accept -a C<'stat'> option value, such as C. - -Net::ZooKeeper::Stat objects may be reused multiple times. -If the Net::ZooKeeper method to which the stat object is -passed succeeds, then the stat object is updated with the newly -retrieved node state information, and any state information -previously stored in the stat object is overwritten. - -All of the attributes of stat objects are B. - -=over 4 - -=item ctime - -The creation time of the node in milliseconds since the epoch. - -=item mtime - -The time of the last modification of the node's data in -milliseconds since the epoch. - -=item data_len - -The length of the node's data in bytes. - -=item num_children - -The number of child nodes beneath of the current node. - -=item ephemeral_owner - -If the node was created with the C flag, -this attribute holds the session ID of the ZooKeeper client -which created the node. If the node was not created with -the C flag, this attribute is set to zero. - -=item version - -The number of revisions of the node's data. The ZooKeeper -cluster will increment this version number whenever the -node's data is changed. When the node is first created this -version number is initialized to zero. - -=item acl_version - -The number of revisions of the node's ACL. The ZooKeeper -cluster will increment this version number whenever the -node's ACL is changed. When the node is first created this -version number is initialized to zero. - -=item children_version - -The number of revisions of the node's list of child nodes. -The ZooKeeper cluster will increment this version number -whenever the list of child nodes is changed. When the node -is first created this version number is initialized to zero. - -=item czxid - -The ZooKeeper transaction ID (ZXID) of the transaction which -created the node. - -=item mzxid - -The ZooKeeper transaction ID (ZXID) of the transaction which -last modified the node's data. This is initially set to -the same transaction ID as the C attribute by the -C method. - -=item children_zxid - -The ZooKeeper transaction ID (ZXID) of the transaction which -last modified the node's list of child nodes. This is -initially set to the same transaction ID as the C -attribute by the C method. - -=back - -=head2 Net::ZooKeeper::Watch - -The Net::ZooKeeper::Watch class provides a hash interface -to the data returned by event notifications from the ZooKeeper -cluster. Net::ZooKeeper::Watch objects are created by calling -the C method on a Net::ZooKeeper handle object, and -may then be passed to any methods which accept a C<'watch'> -option value, such as C. - -Net::ZooKeeper::Watch objects may be reused multiple times. -Regardless of whether the Net::ZooKeeper method to which the -watch object is passed succeeds, the watch object will be -updated to receive an event notification exclusively for the -node referenced in that method call. In the case of an error, -however, the watch object may never receive any event -notification. - -=over 4 - -=item timeout - -The default timeout value, in milliseconds, for all -invocations of the C method made on the watch object. -When the C method is invoked without a -C<'timeout'> option value, it waits for an -event notification from the ZooKeeper cluster for no longer -than the timeout period specified by this attribute. -This default timeout period may be altered by setting this -attribute to a different value. - -Passing a value for the C<'timeout'> option when calling -the C method will temporarily override the current -value of this attribute and cause the C method to -use a different timeout period. - -When a Net::ZooKeeper handle object's C method is -invoked without a C<'timeout'> option, it returns a newly -created watch object whose C attribute value -is initialized to the current value of the handle object's -C attribute. When the C method is invoked -with a C<'timeout'> option, the new watch object's C -attribute value is initialized to the value specified by -the C<'timeout'> option. - -See also the C method, and the C attribute -and C method of the Net::ZooKeeper class. - -=item event - -The type of event which triggered the notification, such -as C if the node's data was changed. -See C<:events> for a list of the possible event types. -If zero, no event notification has occurred yet. - -Note that the events which will trigger a notification -will depend on the Net::ZooKeeper method to which -the watch object was passed. Watches set through the -C and C methods will report events relating -to the node's data, while watches set through the -C method will report events relating to the -creation or deletion of child nodes of the watched node. - -This attribute is B and may not be modified. - -=item state - -The state of the Net::ZooKeeper connection at the time of -the event notification. See C<:states> for a list of -the possible connection states. If zero, no event -notification has occurred yet. - -This attribute is B and may not be modified. - -=back - -=head1 METHODS - -=head2 Net::ZooKeeper - -The following methods are defined for the Net::ZooKeeper class. - -=over 4 - -=item new() - - $zkh = Net::ZooKeeper->new('host1:7000,host2:7000'); - $zkh = Net::ZooKeeper->new('host1:7000,host2:7000', - 'session_timeout' => $session_timeout, - 'session_id' => $session_id); - -Creates a new Net::ZooKeeper handle object and attempts to -connect to the one of the servers of the given ZooKeeper -cluster. As described in the L and -L sections, the ZooKeeper client code will -create an IO thread which maintains the connection with a -regular "heartbeat" request. In the event of a connection error -the IO thread will also attempt to reconnect to another one of -the servers using the same session ID. In general, these actions -should be invisible to the user, although Net::ZooKeeper methods -may return transient errors while the IO thread -reconnects with another server. - -To disconnect, undefine the Net::ZooKeeper handle object -or call the C method. (After calling C -the handle object can not be reused.) - -The ZooKeeper client code will send a "heartbeat" message -if a third of the session timeout period has elapsed without -any communication with the ZooKeeper server. A specific -session timeout period may be requested when creating a -Net::ZooKeeper handle object by supplying a value, in -milliseconds, for the C<'session_timeout'> option. The -ZooKeeper server adjust the requested timeout value so that -it is within a certain range of the server's C setting; -the actual session timeout value will be available as the -value of the handle's C attribute after at -least one method call has succeeded. See the C -attribute for more information. - -If no C<'session_timeout'> option is provided, the default -value of 10 seconds (10000 milliseconds) will be used in the -initial connection request; again, the actual timeout period to -which the server agrees will be available subsequently as the -value of the C attribute. - -Upon successful connection (i.e., after the success of a method -which requires communication with the server), the C -attribute will hold a short binary string which represents the -client's session ID as set by the server. All ephemeral nodes -created by the session are identified by this ID in the -C attribute of any Net::ZooKeeper::Stat objects -used to query their state. - -The ZooKeeper client code will use this session ID internally -whenever it tries to reconnect to another server in the ZooKeeper -cluster after detecting a failed connection. If it successfully -reconnects with the same session ID, the session will continue -and ephemeral nodes belonging to it will not be deleted. - -However, if the server determines that the session has timed -out (for example because no "heartbeat" requests have been -received within the agreed-upon session timeout period), the -session will be terminated by the cluster and all ephemeral nodes -owned by the current session automatically deleted. - -On occasion the ZooKeeper client code may not be able to quickly -reconnect to a live server and the caller may want to destroy -the existing Net::ZooKeeper handle object and attempt a -fresh connection using the same session ID as before with a -new Net::ZooKeeper object. To do so, save the C -attribute value before undefining the old handle object -and then pass that binary string as the value of the -C<'session_id'> option to the C method when creating the -next handle object. After the successful completion of a -method which requires communication with the server, if the -new handle object's C attribute value matches the -old session ID then the session has been successfully maintained; -otherwise, the old session was expired by the cluster. - -=item get_error() - - $code = $zkh->get_error(); - -Returns the ZooKeeper error code, if any, from the most -recent Net::ZooKeeper method invocation. The returned value -will be zero (equivalent to C) if no error occurred, -otherwise non-zero. Non-zero values may be compared to -the error code names exported by the C<:errors> tagset. - -See L for more details. - -=item add_auth() - - $zkh->add_auth('digest', "$username:$password"); - -The C method may be used to add authentication -credentials to a session. Once a credential has been added for -the current session, there is no way to disable it. - -When using the digest authentication scheme, note that the -username and password are transmitted in cleartext -to the ZooKeeper cluster. - -See L for additional details. - -=item create() - - $path = $zkh->create($req_path, $data); - $path = $zkh->create($req_path, $data, - 'flags' => (ZOO_EPHEMERAL | ZOO_SEQUENCE), - 'acl' => ZOO_OPEN_ACL_UNSAFE, - 'path_read_len' => 100); - -Requests that a node be created in the ZooKeeper cluster's -hierarchy with the given path and data. Upon success, -the returns the node's path, otherwise undef. - -The path returned by a successful C method call -may not be the new node's full path as it appears in the -ZooKeeper hierarchy, depending on the length of the actual -path and the value of the handle object's C -attribute. If the length of the actual path exceeds the -current value of the C attribute, the path -returned by the C method will be truncated; note -that the node's path in the ZooKeeper hierarchy is not -affected by this truncation. - -Specifying a value for the C<'path_read_len'> option will -temporarily override the value of the C -attribute for the duration of the C method. - -The flag values available for use with the C<'flags'> option -are C and C; both are -included in the C<:flags> tagset. The flags should be -combined with the bitwise OR operator if more than one -is required. - -The C flag causes the node to be marked as -ephemeral, meaning it will be automatically deleted if it -still exists when the client's session ends. The -C flag causes a unique integer to be appended -to the node's final path component. See the ZooKeeper -documentation for additional advice on how to use these flags. - -When creating a node it may be important to define an ACL -for it; to do this, pass a reference to an ACL array (as -described in L) using the C<'acl'> option. -See also the C<:acl_perms> and C<:acls> tagsets for lists -of the available ACL permission flags and pre-defined ACLs. - -=item delete() - - $ret = $zkh->delete($path); - $ret = $zkh->delete($path, 'version' => $version); - -Requests that a node be deleted from the ZooKeeper hierarchy. -Returns true upon success, false otherwise. - -If a value for the C<'version'> option is supplied, the node -will only be deleted if its version number matches the given -value. See the C attribute of the Net::ZooKeeper::Stat -class for details on node version numbering. - -=item exists() - - $ret = $zkh->exists($path); - $ret = $zkh->exists($path, 'stat' => $stat, 'watch' => $watch); - -Tests whether a given node exists. Returns true if the node -exists, otherwise false. When the C method is successful -but the node does not exist, it returns false, and C -will return C until another method is called on the -handle object. - -The C<'stat'> option may be used to request that a -Net::ZooKeeper::Stat object be updated with the node's -current state information. The stat object will only be -updated if the node exists and the C method -succeeds. The stat object must first have been created -using the C method. - -The C<'watch'> option may be used to request that a -Net::ZooKeeper::Watch object be assigned to receive -notification of an event which alters the node's data. -The watch object must first have been created using the -C method. If the watch object was previously -assigned to receive notifications for another node, it -will be reassigned even if the C method fails. - -=item get_children() - - @child_names = $zkh->get_children($path); - $num_children = $zkh->get_children($path, 'watch' => $watch); - -Queries the names or number of the child nodes stored beneath -a given node in the ZooKeeper hierarchy. In a list context, -returns a list of the child nodes' names upon success, otherwise -an empty list. When the C method is successful -but there are no child nodes, it returns an empty list, and -C will return C until another method is called -on the handle object. - -In a scalar context, C returns the number -of child nodes upon success, otherwise undef. - -The names of the child nodes are simply the final component -of the nodes' paths, i.e., the portion of their path which -follows the path of the given parent node, excluding the -"/" delimiter. - -The C<'watch'> option may be used to request that a -Net::ZooKeeper::Watch object be assigned to receive -notification of an event which alters the node's list of -child nodes. The watch object must first have been created -using the C method. If the watch object was -previously assigned to receive notifications for another node, -it will be reassigned even if the C method fails. - -=item get() - - $data = $zkh->get($path); - $data = $zkh->get($path, 'data_read_len' => 100, - 'stat' => $stat, 'watch' => $watch); - -Queries the data stored in a given node. Returns the -data as a string upon success, otherwise undef. Note -that the data may contain nulls if the node's data is -not a text string. - -If the length of the node's data exceeds the current value -of the handle object's C attribute, the -string returned by the C method will be truncated; -note that the node's data in the ZooKeeper cluster is not -affected by this truncation. - -Specifying a value for the C<'data_read_len'> option will -temporarily override the value of the C -attribute for the duration of the C method. - -The C<'stat'> option may be used to request that a -Net::ZooKeeper::Stat object be updated with the node's -current state information. The stat object will only be -updated if the C method succeeds. The stat object -must first have been created using the C method. - -The C<'watch'> option may be used to request that a -Net::ZooKeeper::Watch object be assigned to receive -notification of an event which alters the node's data. -The watch object must first have been created using the -C method. If the watch object was previously -assigned to receive notifications for another node, it -will be reassigned even if the C method fails. - -=item set() - - $ret = $zkh->set($path, $data); - $ret = $zkh->set($path, $data, 'version' => $version, - 'stat' => $stat); - -Requests that a node's data be updated in the ZooKeeper -hierarchy. Returns true upon success, false otherwise. - -If a value for the C<'version'> option is supplied, the node's -data will only be updated if its version number matches the -given value. See the C attribute of the -Net::ZooKeeper::Stat class for details on node version numbering. - -The C<'stat'> option may be used to request that a -Net::ZooKeeper::Stat object be updated with the node's -current state information. The stat object will only be -updated if the C method succeeds. The stat object -must first have been created using the C method. - -=item get_acl() - - @acl = $zkh->get_acl($path); - $num_acl_entries = $zkh->get_acl($path, 'stat' => $stat); - -Queries the ACL associated with a node in the ZooKeeper -hierarchy, if any. In a list context, returns an array with -the node's ACL entries upon success, otherwise -an empty list. When the C method is successful -but there are no ACL entries, it returns an empty list, and -C will return C until another method is called -on the handle object. - -The elements of the returned array are hashes, each of which -represents one ACL entry. Each hash contains C, -C, and C elements. See the L -section for additional details, and the -C<:acl_perms> and C<:acls> tagsets for lists of the -available ACL permission flags and pre-defined ACLs. - -In a scalar context, C returns the number -of ACL entries upon success, otherwise undef. - -The C<'stat'> option may be used to request that a -Net::ZooKeeper::Stat object be updated with the node's -current state information. The stat object will only be -updated if the C method succeeds. The stat object -must first have been created using the C method. - -=item set_acl() - - $acl = [{ - 'perms' => (ZOO_PERM_READ | ZOO_PERM_WRITE), - 'scheme' => 'digest', - 'id' => "$username:$digest" - }]; - $ret = $zkh->set_acl($path, $acl); - $ret = $zkh->set_acl($path, ZOO_OPEN_ACL_UNSAFE, - 'version' => $version); - -Requests that a node's ACL be updated in the ZooKeeper -hierarchy. Returns true upon success, false otherwise. - -The ACL should be passed as a reference to an array of -hashes, where each hash represents one ACL entry. Each -hash should contain C, C, and C elements -as described in the L section. -See also the C<:acl_perms> and C<:acls> tagsets for lists -of the available ACL permission flags and pre-defined ACLs. - -If a value for the C<'version'> option is supplied, the node's -ACL will only be updated if its version number matches the -given value. See the C attribute of the -Net::ZooKeeper::Stat class for details on node version numbering. - -=item stat() - - $stat = $zkh->stat(); - -Creates a new Net::ZooKeeper::Stat object which may be used -with the C<'stat'> option of the C, C, -C, and C methods. When the stat object -is passed to any of these methods, upon success its attribute -values are updated to reflect the current state of the -node specified in the method call. The stat object is not -updated if the method call does not succeed. - -=item watch() - - $watch = $zkh->watch(); - $watch = $zkh->watch('timeout' => $timeout); - -Creates a new Net::ZooKeeper::Watch object which may be -used to wait for event notifications from the ZooKeeper -cluster. Each time the watch object is passed to any -of the C, C, or C methods, -its attribute values are immediately reset to zero, and will -later be updated upon receipt of an appropriate event -notification for the node specified in the method call. - -The specific types of events which cause notifications to be -sent by the ZooKeeper cluster depend on the method call used. -After use with the C and C methods, the -watch object will be set to receive an event notification -caused by a modification of the node's data or the node itself -(e.g., deletion of the node). After use with the -C method, the watch object will be set to -receive an event notification caused by a modification -of the node's list of child nodes. - -Watch objects receive at most one event notification after -their assignment to a node by one of the C, -C, or C methods. Note that in the -case of an error, the watch object may never receive any -event notification. However, when the parent Net::ZooKeeper -handle object experiences a connection error, the ZooKeeper -client code will notify all pending watches with an event of -type C. See C for more information -regarding the watch object's attribute values after a -connection error. - -A watch object may be reused with another C, -C, or C method call at any time, -in which case the watch object's attribute values -are reset to zero and the watch object will no longer be updated -by any event notification relevant to the previous method call. - -When the C method is invoked without a C<'timeout'> -option, it returns a newly created watch object whose C -attribute value is initialized to the current value of the -Net::ZooKeeper handle object's C attribute. -Otherwise, when the C method is invoked with a -C<'timeout'> option, the new watch object's C attribute -value is initialized to the value specified by the -C<'timeout'> option. - -See also the C attribute, and the C -attribute and C method of the Net::ZooKeeper::Watch -class. - -=back - -=head2 Net::ZooKeeper::Stat - -No methods are defined for the Net::ZooKeeper::Stat class. - -=head2 Net::ZooKeeper::Watch - -Only one method is defined for the Net::ZooKeeper::Watch class. - -=over 4 - -=item wait() - - $ret = $watch->wait(); - $ret = $watch->wait('timeout' => $timeout); - -Waits for an event notification from the ZooKeeper cluster -for the node most recently associated with the watch object. -Nodes are associated with a watch object by passing the -watch object as the value of a C<'watch'> option to a -Net::ZooKeeper method; methods which accept a C<'watch'> option -are C, C, and C. - -When the C method is invoked with a C<'timeout'> -option, it waits for no more than the number of milliseconds -specified by the C<'timeout'> option. -Otherwise, when the C method is invoked without a -C<'timeout'> option, it waits for no more than the timeout -period specified by the value of the watch object's C -attribute. - -The C method returns true if an event notification -was received, otherwise false. When C returns true, -the C and C attributes of the watch object -will be updated with the event's type and the current -connection state. - -When the parent Net::ZooKeeper handle object experiences a -connection error, the ZooKeeper client code will notify all -pending watches with an event of type C. -In this case, the C attribute will report the current -state of the connection to the ZooKeeper cluster. - -See also the C attribute, and the C method -and C attribute of the Net::ZooKeeper class. - -=back - -=head1 FUNCTIONS - -The following functions have global scope and affect all -Net::ZooKeeper handle objects. - -=over 4 - -=item set_log_level() - - Net::ZooKeeper::set_log_level($level); - -The C function may be called to -alter the number and type of messages written to the current log -file handle (if any). The default value is C -which disables all logging. - -See the L section for more details and C<:log_levels> -for a list of the available log levels. - -=item set_deterministic_conn_order() - - Net::ZooKeeper::set_deterministic_conn_order(1); - -The C function -may be called to indicate whether or not the list of ZooKeeper -servers passed to the C method should be randomly permuted. -If set to a true value, the list of servers will not be altered. -The default false value indicates the list of servers will -be randomly reordered prior to connection. - -See the L section for more details. - -=back - -=head1 EXPORTS - -Nothing is exported by default. Various tagsets exist which -group the tags available for export into different categories: - -=over 4 - -=item :errors - -ZooKeeper error codes. These may be compared to the values -returned by the C method. - -=item :node_flags - -The ZooKeeper node flags C and C, -which may be passed in the C<'flags'> option to the C -method. When more than node flag is required they -should be combined using the bitwise OR operator. - -=item :acl_perms - -The ZooKeeper ACL permission flags which may be used in -the value of the C attribute of an ACL entry hash. -When more than one ACL permission flag is required they -should be combined using the bitwise OR operator. - -The available ACL permission flags are C, -C, C, C, -and C. For convenience, C is -defined as the bitwise OR of all of these flags. - -=item :acls - -Common ZooKeeper ACLs which may be useful. C -specifies a node which is entirely open to all users with no -restrictions at all. C specifies -a node which is readable by all users; permissions for other actions -are not defined in this ACL. C specifies a node -for which all actions require the same authentication credentials as -held by the session which created the node; this implies that a -session should authenticate with an appropriate scheme before -creating a node with this ACL. - -=item :events - -The ZooKeeper event types which are returned in value of -the C attribute a Net::ZooKeeper::Watch object after -an event occurs on a watched node. - -=item :states - -The ZooKeeper connection states which are returned in value of -the C attribute of a Net::ZooKeeper::Watch object after -an event occurs on a watched node. - -=item :log_levels - -The ZooKeeper log levels which may be passed to the -C function. The available -log levels are, from least to most verbose, C -(the default), C, C, -C, and C. - -=item :all - -Everything from all of the above tagsets. - -=back - -=head1 SEE ALSO - -The Apache ZooKeeper project's home page at -L provides a wealth of detail -on how to develop applications using ZooKeeper. - -=head1 AUTHOR - -Chris Darroch, Echrisd@apache.orgE - -=head1 COPYRIGHT AND LICENSE - -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -=cut - diff --git a/src/contrib/zkperl/ZooKeeper.xs b/src/contrib/zkperl/ZooKeeper.xs deleted file mode 100644 index f2244197e6c..00000000000 --- a/src/contrib/zkperl/ZooKeeper.xs +++ /dev/null @@ -1,2669 +0,0 @@ -/* Net::ZooKeeper - Perl extension for Apache ZooKeeper - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define PERL_NO_GET_CONTEXT - -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" - -#include /* pthread_mutex_lock(), etc. */ -#include /* memset(), etc. */ -#include /* CHAR_BIT */ -#include /* gettimeofday() */ - -#include "zookeeper.h" - -#include "build/check_zk_version.h" - - -#define PACKAGE_NAME "Net::ZooKeeper" -#define PACKAGE_SIGNATURE 19631123 - -#define STAT_PACKAGE_NAME "Net::ZooKeeper::Stat" -#define STAT_PACKAGE_SIGNATURE 19960512 - -#define WATCH_PACKAGE_NAME "Net::ZooKeeper::Watch" -#define WATCH_PACKAGE_SIGNATURE 20050326 - -#define MAX_KEY_NAME_LEN 16 /* "children_version" */ - -#define NUM_ACL_ENTRY_KEYS 3 -#define NUM_KEYS 7 -#define NUM_STAT_KEYS 11 -#define NUM_WATCH_KEYS 3 - -#define DEFAULT_RECV_TIMEOUT_MSEC 10000 - -#define DEFAULT_DATA_BUF_LEN 1023 -#define DEFAULT_PATH_BUF_LEN 1023 -#define DEFAULT_WATCH_TIMEOUT 60000 - -#define ZOO_LOG_LEVEL_OFF 0 - -#ifndef strcaseEQ -#define strcaseEQ(a,b) (!strcasecmp((a),(b))) -#endif - - -typedef struct Stat zk_stat_t; - -typedef HV* Net__ZooKeeper__Stat; - -typedef struct zk_watch_t zk_watch_t; - -struct zk_watch_t { - pthread_mutex_t mutex; - pthread_cond_t cond; - int done; - int ret; - int event_type; - int event_state; - unsigned int timeout; - zk_watch_t *prev; - zk_watch_t *next; - int ref_count; -}; - -typedef HV* Net__ZooKeeper__Watch; - -typedef struct { - zhandle_t *handle; - zk_watch_t *first_watch; - int data_buf_len; - int path_buf_len; - unsigned int watch_timeout; - const char *hosts; - int hosts_len; - int last_ret; - int last_errno; -} zk_t; - -typedef HV* Net__ZooKeeper; - -typedef struct { - I32 signature; - union { - zk_t *zk; - zk_stat_t *stat; - zk_watch_t *watch; - } handle; -} zk_handle_t; - -typedef struct { - const char name[MAX_KEY_NAME_LEN + 1]; - U32 name_len; - size_t offset; - size_t size; - U32 hash; -} zk_key_t; - - -static zk_key_t zk_acl_entry_keys[NUM_ACL_ENTRY_KEYS] = { - {"perms", 0, 0, 0, 0}, - {"scheme", 0, 0, 0, 0}, - {"id", 0, 0, 0, 0} -}; - -static zk_key_t zk_keys[NUM_KEYS] = { - {"data_read_len", 0, 0, 0, 0}, - {"path_read_len", 0, 0, 0, 0}, - {"watch_timeout", 0, 0, 0, 0}, - {"hosts", 0, 0, 0, 0}, - {"session_timeout", 0, 0, 0, 0}, - {"session_id", 0, 0, 0, 0}, - {"pending_watches", 0, 0, 0, 0} -}; - -static zk_key_t zk_stat_keys[NUM_STAT_KEYS] = { - {"czxid", 0, offsetof(struct Stat, czxid), - sizeof(((struct Stat*) 0)->czxid), 0}, - {"mzxid", 0, offsetof(struct Stat, mzxid), - sizeof(((struct Stat*) 0)->mzxid), 0}, - {"ctime", 0, offsetof(struct Stat, ctime), - sizeof(((struct Stat*) 0)->ctime), 0}, - {"mtime", 0, offsetof(struct Stat, mtime), - sizeof(((struct Stat*) 0)->mtime), 0}, - {"version", 0, offsetof(struct Stat, version), - sizeof(((struct Stat*) 0)->version), 0}, - {"children_version", 0, offsetof(struct Stat, cversion), - sizeof(((struct Stat*) 0)->cversion), 0}, - {"acl_version", 0, offsetof(struct Stat, aversion), - sizeof(((struct Stat*) 0)->aversion), 0}, - {"ephemeral_owner", 0, offsetof(struct Stat, ephemeralOwner), - sizeof(((struct Stat*) 0)->ephemeralOwner), 0}, - {"data_len", 0, offsetof(struct Stat, dataLength), - sizeof(((struct Stat*) 0)->dataLength), 0}, - {"num_children", 0, offsetof(struct Stat, numChildren), - sizeof(((struct Stat*) 0)->numChildren), 0}, - {"children_zxid", 0, offsetof(struct Stat, pzxid), - sizeof(((struct Stat*) 0)->pzxid), 0} -}; - -static zk_key_t zk_watch_keys[NUM_WATCH_KEYS] = { - {"timeout", 0, 0, 0, 0}, - {"event", 0, 0, 0, 0}, - {"state", 0, 0, 0, 0} -}; - - -static void _zk_watcher(zhandle_t *handle, int type, int state, - const char *path, void *context) -{ - zk_watch_t *watch_ctx = context; - - pthread_mutex_lock(&watch_ctx->mutex); - - watch_ctx->event_type = type; - watch_ctx->event_state = state; - - watch_ctx->done = 1; - - pthread_cond_signal(&watch_ctx->cond); - pthread_mutex_unlock(&watch_ctx->mutex); - - return; -} - -static void _zk_auth_completion(int ret, const void *data) -{ - zk_watch_t *watch_ctx = (zk_watch_t*) data; - - pthread_mutex_lock(&watch_ctx->mutex); - - watch_ctx->ret = ret; - - watch_ctx->done = 1; - - pthread_cond_signal(&watch_ctx->cond); - pthread_mutex_unlock(&watch_ctx->mutex); - - return; -} - -static zk_watch_t *_zk_create_watch(pTHX) -{ - zk_watch_t *watch; - - Newxz(watch, 1, zk_watch_t); - - if (pthread_mutex_init(&watch->mutex, NULL)) { - int save_errno = errno; - - Safefree(watch); - - errno = save_errno; - return NULL; - } - - if (pthread_cond_init(&watch->cond, NULL)) { - int save_errno = errno; - - pthread_mutex_destroy(&watch->mutex); - Safefree(watch); - - errno = save_errno; - return NULL; - } - - return watch; -} - -static void _zk_destroy_watch(pTHX_ zk_watch_t *watch) -{ - pthread_cond_destroy(&watch->cond); - pthread_mutex_destroy(&watch->mutex); - - Safefree(watch); - - return; -} - -static zk_watch_t *_zk_acquire_watch(pTHX) -{ - zk_watch_t *watch = _zk_create_watch(aTHX); - - if (watch) { - watch->ref_count = 1; - } - - return watch; -} - -static void _zk_release_watch(pTHX_ zk_watch_t *watch, int list) -{ - if (list) { - if (watch->prev) { - watch->prev->next = watch->next; - watch->prev = NULL; - } - if (watch->next) { - watch->next->prev = watch->prev; - watch->next = NULL; - } - } - - if (--watch->ref_count == 0) { - _zk_destroy_watch(aTHX_ watch); - } - - return; -} - -static unsigned int _zk_release_watches(pTHX_ zk_watch_t *first_watch, - int final) -{ - zk_watch_t *watch = first_watch->next; - unsigned int pending_watches = 0; - - while (watch) { - zk_watch_t *next_watch = watch->next; - int done = final; - - if (!final) { - pthread_mutex_lock(&watch->mutex); - done = watch->done; - pthread_mutex_unlock(&watch->mutex); - } - - if (done) { - _zk_release_watch(aTHX_ watch, 1); - } - else { - ++pending_watches; - } - - watch = next_watch; - } - - return pending_watches; -} - -static void _zk_replace_watch(pTHX_ zk_handle_t *handle, - zk_watch_t *first_watch, - zk_watch_t *old_watch, zk_watch_t *new_watch) -{ - zk_watch_t *next_watch; - - new_watch->timeout = old_watch->timeout; - - _zk_release_watch(aTHX_ old_watch, 0); - - /* cleanup any completed watches not tied to a handle */ - _zk_release_watches(aTHX_ first_watch, 0); - - next_watch = first_watch->next; - - new_watch->prev = first_watch; - new_watch->next = next_watch; - - if (next_watch) { - next_watch->prev = new_watch; - } - - first_watch->next = new_watch; - - ++new_watch->ref_count; - - handle->handle.watch = new_watch; - - return; -} - -static void _zk_free_acl(pTHX_ struct ACL_vector *acl) -{ - if (acl->data) { - Safefree(acl->data); - } - - return; -} - -static const char *_zk_fill_acl(pTHX_ AV *acl_arr, struct ACL_vector *acl) -{ - I32 num_acl_entries = av_len(acl_arr) + 1; - int i; - - Zero(acl, 1, struct ACL_vector); - - if (num_acl_entries <= 0) { - return NULL; - } - else if (num_acl_entries > PERL_INT_MAX) { - num_acl_entries = PERL_INT_MAX; - } - - Newx(acl->data, num_acl_entries, struct ACL); - - for (i = 0; i < num_acl_entries; ++i) { - SV **acl_entry_ptr; - HV *acl_entry_hash; - zk_key_t *key; - SV **val_ptr; - struct ACL acl_entry; - - acl_entry_ptr = av_fetch(acl_arr, i, 0); - - if (!acl_entry_ptr) { - continue; - } - - if (!SvROK(*acl_entry_ptr) || - SvTYPE(SvRV(*acl_entry_ptr)) != SVt_PVHV) { - _zk_free_acl(aTHX_ acl); - - return "invalid ACL entry hash reference"; - } - - acl_entry_hash = (HV*) SvRV(*acl_entry_ptr); - - key = &zk_acl_entry_keys[0]; - val_ptr = hv_fetch(acl_entry_hash, key->name, key->name_len, 0); - - if (!val_ptr) { - _zk_free_acl(aTHX_ acl); - - return "no ACL entry perms element"; - } - - acl_entry.perms = SvIV(*val_ptr); - - if (!acl_entry.perms || (acl_entry.perms & ~ZOO_PERM_ALL)) { - _zk_free_acl(aTHX_ acl); - - return "invalid ACL entry perms"; - } - - key = &zk_acl_entry_keys[1]; - val_ptr = hv_fetch(acl_entry_hash, key->name, key->name_len, 0); - - if (!val_ptr) { - _zk_free_acl(aTHX_ acl); - - return "no ACL entry scheme element"; - } - - acl_entry.id.scheme = SvPV_nolen(*val_ptr); - - key = &zk_acl_entry_keys[2]; - val_ptr = hv_fetch(acl_entry_hash, key->name, key->name_len, 0); - - if (!val_ptr) { - _zk_free_acl(aTHX_ acl); - - return "no ACL entry id element"; - } - - acl_entry.id.id = SvPV_nolen(*val_ptr); - - ++acl->count; - acl->data[i] = acl_entry; - } - - return NULL; -} - -static void _zk_fill_acl_entry_hash(pTHX_ struct ACL *acl_entry, - HV *acl_entry_hash) -{ - zk_key_t *key; - SV *val; - - key = &zk_acl_entry_keys[0]; - val = newSViv(acl_entry->perms); - - if (!hv_store(acl_entry_hash, key->name, key->name_len, val, key->hash)) { - SvREFCNT_dec(val); - } - - key = &zk_acl_entry_keys[1]; - val = newSVpv(acl_entry->id.scheme, 0); - - if (!hv_store(acl_entry_hash, key->name, key->name_len, val, key->hash)) { - SvREFCNT_dec(val); - } - - key = &zk_acl_entry_keys[2]; - val = newSVpv(acl_entry->id.id, 0); - - if (!hv_store(acl_entry_hash, key->name, key->name_len, val, key->hash)) { - SvREFCNT_dec(val); - } - - return; -} - -static zk_handle_t *_zk_check_handle_inner(pTHX_ HV *attr_hash, - I32 package_signature) -{ - zk_handle_t *handle = NULL; - - if (SvRMAGICAL(attr_hash)) { - MAGIC *magic = mg_find((SV*) attr_hash, PERL_MAGIC_ext); - - if (magic) { - handle = (zk_handle_t*) magic->mg_ptr; - - if (handle->signature != package_signature) { - handle = NULL; - } - } - } - - return handle; -} - -static zk_handle_t *_zk_check_handle_outer(pTHX_ HV *hash, HV **attr_hash_ptr, - const char *package_name, - I32 package_signature) -{ - zk_handle_t *handle = NULL; - - if (attr_hash_ptr) { - *attr_hash_ptr = NULL; - } - - if (SvRMAGICAL((SV*) hash)) { - MAGIC *magic = mg_find((SV*) hash, PERL_MAGIC_tied); - - if (magic) { - SV *attr = magic->mg_obj; - - if (SvROK(attr) && SvTYPE(SvRV(attr)) == SVt_PVHV && - sv_derived_from(attr, package_name)) { - HV *attr_hash = (HV*) SvRV(attr); - - handle = _zk_check_handle_inner(aTHX_ attr_hash, - package_signature); - - if (handle && attr_hash_ptr) { - *attr_hash_ptr = attr_hash; - } - } - } - } - - return handle; -} - -static zk_t *_zk_get_handle_inner(pTHX_ Net__ZooKeeper attr_hash) -{ - zk_handle_t *handle; - - handle = _zk_check_handle_inner(aTHX_ attr_hash, PACKAGE_SIGNATURE); - - return handle ? handle->handle.zk : NULL; -} - -static zk_t *_zk_get_handle_outer(pTHX_ Net__ZooKeeper zkh) -{ - zk_handle_t *handle; - - handle = _zk_check_handle_outer(aTHX_ zkh, NULL, PACKAGE_NAME, - PACKAGE_SIGNATURE); - - return handle ? handle->handle.zk : NULL; -} - -static zk_stat_t *_zks_get_handle_inner(pTHX_ Net__ZooKeeper__Stat attr_hash) -{ - zk_handle_t *handle; - - handle = _zk_check_handle_inner(aTHX_ attr_hash, STAT_PACKAGE_SIGNATURE); - - return handle ? handle->handle.stat : NULL; -} - -static zk_stat_t *_zks_get_handle_outer(pTHX_ Net__ZooKeeper__Stat zksh) -{ - zk_handle_t *handle; - - handle = _zk_check_handle_outer(aTHX_ zksh, NULL, STAT_PACKAGE_NAME, - STAT_PACKAGE_SIGNATURE); - - return handle ? handle->handle.stat : NULL; -} - -static zk_watch_t *_zkw_get_handle_inner(pTHX_ Net__ZooKeeper__Watch attr_hash) -{ - zk_handle_t *handle; - - handle = _zk_check_handle_inner(aTHX_ attr_hash, WATCH_PACKAGE_SIGNATURE); - - return handle ? handle->handle.watch : NULL; -} - -static zk_watch_t *_zkw_get_handle_outer(pTHX_ Net__ZooKeeper__Watch zkwh, - zk_handle_t **handle_ptr) -{ - zk_handle_t *handle; - - handle = _zk_check_handle_outer(aTHX_ zkwh, NULL, WATCH_PACKAGE_NAME, - WATCH_PACKAGE_SIGNATURE); - - if (handle_ptr) { - *handle_ptr = handle; - } - - return handle ? handle->handle.watch : NULL; -} - - -MODULE = Net::ZooKeeper PACKAGE = Net::ZooKeeper PREFIX = zk_ - -REQUIRE: 1.9508 - -PROTOTYPES: ENABLE - -BOOT: -{ - int i; - - for (i = 0; i < NUM_ACL_ENTRY_KEYS; ++i) { - zk_key_t *key = &zk_acl_entry_keys[i]; - - key->name_len = strlen(key->name); - PERL_HASH(key->hash, key->name, key->name_len); - } - - for (i = 0; i < NUM_KEYS; ++i) { - zk_keys[i].name_len = strlen(zk_keys[i].name); - } - - for (i = 0; i < NUM_STAT_KEYS; ++i) { - zk_stat_keys[i].name_len = strlen(zk_stat_keys[i].name); - } - - for (i = 0; i < NUM_WATCH_KEYS; ++i) { - zk_watch_keys[i].name_len = strlen(zk_watch_keys[i].name); - } - - zoo_set_log_stream(NULL); - zoo_set_debug_level(0); -} - - -I32 -zk_constant(alias=Nullch) - char *alias - ALIAS: - ZOK = ZOK - ZSYSTEMERROR = ZSYSTEMERROR - ZRUNTIMEINCONSISTENCY = ZRUNTIMEINCONSISTENCY - ZDATAINCONSISTENCY = ZDATAINCONSISTENCY - ZCONNECTIONLOSS = ZCONNECTIONLOSS - ZMARSHALLINGERROR = ZMARSHALLINGERROR - ZUNIMPLEMENTED = ZUNIMPLEMENTED - ZOPERATIONTIMEOUT = ZOPERATIONTIMEOUT - ZBADARGUMENTS = ZBADARGUMENTS - ZINVALIDSTATE = ZINVALIDSTATE - ZAPIERROR = ZAPIERROR - ZNONODE = ZNONODE - ZNOAUTH = ZNOAUTH - ZBADVERSION = ZBADVERSION - ZNOCHILDRENFOREPHEMERALS = ZNOCHILDRENFOREPHEMERALS - ZNODEEXISTS = ZNODEEXISTS - ZNOTEMPTY = ZNOTEMPTY - ZSESSIONEXPIRED = ZSESSIONEXPIRED - ZINVALIDCALLBACK = ZINVALIDCALLBACK - ZINVALIDACL = ZINVALIDACL - ZAUTHFAILED = ZAUTHFAILED - ZCLOSING = ZCLOSING - ZNOTHING = ZNOTHING - - ZOO_EPHEMERAL = ZOO_EPHEMERAL - ZOO_SEQUENCE = ZOO_SEQUENCE - - ZOO_PERM_READ = ZOO_PERM_READ - ZOO_PERM_WRITE = ZOO_PERM_WRITE - ZOO_PERM_CREATE = ZOO_PERM_CREATE - ZOO_PERM_DELETE = ZOO_PERM_DELETE - ZOO_PERM_ADMIN = ZOO_PERM_ADMIN - ZOO_PERM_ALL = ZOO_PERM_ALL - - ZOO_CREATED_EVENT = ZOO_CREATED_EVENT - ZOO_DELETED_EVENT = ZOO_DELETED_EVENT - ZOO_CHANGED_EVENT = ZOO_CHANGED_EVENT - ZOO_CHILD_EVENT = ZOO_CHILD_EVENT - ZOO_SESSION_EVENT = ZOO_SESSION_EVENT - ZOO_NOTWATCHING_EVENT = ZOO_NOTWATCHING_EVENT - - ZOO_EXPIRED_SESSION_STATE = ZOO_EXPIRED_SESSION_STATE - ZOO_AUTH_FAILED_STATE = ZOO_AUTH_FAILED_STATE - ZOO_CONNECTING_STATE = ZOO_CONNECTING_STATE - ZOO_ASSOCIATING_STATE = ZOO_ASSOCIATING_STATE - ZOO_CONNECTED_STATE = ZOO_CONNECTED_STATE - - ZOO_LOG_LEVEL_OFF = ZOO_LOG_LEVEL_OFF - ZOO_LOG_LEVEL_ERROR = ZOO_LOG_LEVEL_ERROR - ZOO_LOG_LEVEL_WARN = ZOO_LOG_LEVEL_WARN - ZOO_LOG_LEVEL_INFO = ZOO_LOG_LEVEL_INFO - ZOO_LOG_LEVEL_DEBUG = ZOO_LOG_LEVEL_DEBUG - CODE: - if (!ix) { - if (!alias) { - alias = GvNAME(CvGV(cv)); - } - - if (strEQ(alias, "ZOK")) { - RETVAL = ZOK; - } - else if (strEQ(alias, "ZOO_LOG_LEVEL_OFF")) { - RETVAL = ZOO_LOG_LEVEL_OFF; - } - else { - Perl_croak(aTHX_ "unknown " PACKAGE_NAME " constant: %s", - alias); - } - } - else { - RETVAL = ix; - } - OUTPUT: - RETVAL - - -AV * -zk_acl_constant(alias=Nullch) - char *alias - ALIAS: - ZOO_OPEN_ACL_UNSAFE = 1 - ZOO_READ_ACL_UNSAFE = 2 - ZOO_CREATOR_ALL_ACL = 3 - PREINIT: - struct ACL_vector acl; - AV *acl_arr; - int i; - PPCODE: - if (!ix && !alias) { - alias = GvNAME(CvGV(cv)); - } - - if (ix == 1 || strEQ(alias, "ZOO_OPEN_ACL_UNSAFE")) { - acl = ZOO_OPEN_ACL_UNSAFE; - } - else if (ix == 2 || strEQ(alias, "ZOO_READ_ACL_UNSAFE")) { - acl = ZOO_READ_ACL_UNSAFE; - } - else if (ix == 3 || strEQ(alias, "ZOO_CREATOR_ALL_ACL")) { - acl = ZOO_CREATOR_ALL_ACL; - } - else { - Perl_croak(aTHX_ "unknown " PACKAGE_NAME " constant: %s", alias); - } - - acl_arr = newAV(); - - av_extend(acl_arr, acl.count); - - for (i = 0; i < acl.count; ++i) { - HV *acl_entry_hash = newHV(); - SV *val; - - _zk_fill_acl_entry_hash(aTHX_ &acl.data[i], acl_entry_hash); - - val = newRV_noinc((SV*) acl_entry_hash); - - if (!av_store(acl_arr, i, val)) { - SvREFCNT_dec(val); - } - } - - ST(0) = sv_2mortal(newRV_noinc((SV*) acl_arr)); - - XSRETURN(1); - - -void -zk_set_log_level(level) - int level - PPCODE: - if (level < ZOO_LOG_LEVEL_OFF || level > ZOO_LOG_LEVEL_DEBUG) { - Perl_croak(aTHX_ "invalid log level: %d", level); - } - - zoo_set_debug_level(level); - - XSRETURN_EMPTY; - - -void -zk_set_deterministic_conn_order(flag) - bool flag - PPCODE: - zoo_deterministic_conn_order(!!flag); - - XSRETURN_EMPTY; - - -void -zk_new(package, hosts, ...) - char *package - char *hosts - PREINIT: - int recv_timeout = DEFAULT_RECV_TIMEOUT_MSEC; - const clientid_t *client_id = NULL; - zk_t *zk; - zk_handle_t *handle; - HV *stash, *zk_hash, *attr_hash; - SV *attr; - int i; - PPCODE: - if (items > 2 && items % 2) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - for (i = 2; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "session_timeout")) { - recv_timeout = SvIV(ST(i + 1)); - - /* NOTE: would be nice if requirement in zookeeper_interest() - * that recv_timeout*2 be non-negative was documented - */ - if (recv_timeout < 0 || recv_timeout > (PERL_INT_MAX >> 1)) { - Perl_croak(aTHX_ "invalid session timeout: %d", - recv_timeout); - } - } - else if (strcaseEQ(key, "session_id")) { - STRLEN client_id_len; - - client_id = (const clientid_t*) SvPV(ST(i + 1), client_id_len); - - if (client_id_len != sizeof(clientid_t)) { - Perl_croak(aTHX_ "invalid session ID"); - } - } - } - - Newxz(zk, 1, zk_t); - - zk->handle = zookeeper_init(hosts, NULL, recv_timeout, - client_id, NULL, 0); - - if (!zk->handle) { - Safefree(zk); - - XSRETURN_UNDEF; - } - - Newxz(zk->first_watch, 1, zk_watch_t); - - zk->data_buf_len = DEFAULT_DATA_BUF_LEN; - zk->path_buf_len = DEFAULT_PATH_BUF_LEN; - zk->watch_timeout = DEFAULT_WATCH_TIMEOUT; - - zk->hosts_len = strlen(hosts); - zk->hosts = savepvn(hosts, zk->hosts_len); - - Newx(handle, 1, zk_handle_t); - - handle->signature = PACKAGE_SIGNATURE; - handle->handle.zk = zk; - - /* We use several tricks from DBI here. The attr_hash is our - * empty inner hash; we attach extra magic to it in the form of - * our zk_handle_t structure. Then we tie attr_hash to zk_hash, - * our outer hash. This is what is passed around (by reference) by - * callers. - * - * Most methods use _zk_get_handle_outer() which finds our inner - * handle, then returns the zk_t structure from its extra magic - * pointer. - * - * However, the tied hash methods, FETCH(), STORE(), and so forth, - * receive an already-dereferenced inner handle hash. This is - * because we bless both inner and outer handles into this class, - * so when a caller's code references a hash element in our - * outer handle, Perl detects its tied magic, looks up the - * tied object (our inner handle) and invokes the tied hash methods - * in its class on it. Since we blessed it into the same class - * as the outer handle, these methods simply reside in our package. - */ - - stash = gv_stashpv(package, GV_ADDWARN); - - attr_hash = newHV(); - - sv_magic((SV*) attr_hash, Nullsv, PERL_MAGIC_ext, - (const char*) handle, 0); - - attr = sv_bless(newRV_noinc((SV*) attr_hash), stash); - - zk_hash = newHV(); - - sv_magic((SV*) zk_hash, attr, PERL_MAGIC_tied, Nullch, 0); - SvREFCNT_dec(attr); - - ST(0) = sv_bless(sv_2mortal(newRV_noinc((SV*) zk_hash)), stash); - - XSRETURN(1); - - -void -zk_DESTROY(zkh) - Net::ZooKeeper zkh - PREINIT: - zk_handle_t *handle; - HV *attr_hash; - int ret = ZBADARGUMENTS; - PPCODE: - handle = _zk_check_handle_outer(aTHX_ zkh, &attr_hash, - PACKAGE_NAME, PACKAGE_SIGNATURE); - - if (!handle) { - handle = _zk_check_handle_inner(aTHX_ zkh, PACKAGE_SIGNATURE); - - if (handle) { - attr_hash = zkh; - zkh = NULL; - } - } - - if (handle) { - zk_t *zk = handle->handle.zk; - - ret = zookeeper_close(zk->handle); - - /* detach all now-inactive watches still tied to handles */ - _zk_release_watches(aTHX_ zk->first_watch, 1); - - Safefree(zk->first_watch); - Safefree(zk->hosts); - Safefree(zk); - Safefree(handle); - - sv_unmagic((SV*) attr_hash, PERL_MAGIC_ext); - } - - if (zkh && attr_hash) { - sv_unmagic((SV*) zkh, PERL_MAGIC_tied); - } - - if (GIMME_V == G_VOID) { - XSRETURN_EMPTY; - } - else if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zk_CLONE(package) - char *package - PPCODE: - XSRETURN_EMPTY; - - -void -zk_CLONE_SKIP(package) - char *package - PPCODE: - XSRETURN_YES; - - -void -zk_TIEHASH(package, ...) - char *package - PPCODE: - Perl_croak(aTHX_ "tying hashes of class " - PACKAGE_NAME " not supported"); - - -void -zk_UNTIE(attr_hash, ref_count) - Net::ZooKeeper attr_hash - IV ref_count - PPCODE: - Perl_croak(aTHX_ "untying hashes of class " - PACKAGE_NAME " not supported"); - - -void -zk_FIRSTKEY(attr_hash) - Net::ZooKeeper attr_hash - PREINIT: - zk_t *zk; - PPCODE: - zk = _zk_get_handle_inner(aTHX_ attr_hash); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - ST(0) = sv_2mortal(newSVpvn(zk_keys[0].name, zk_keys[0].name_len)); - - XSRETURN(1); - - -void -zk_NEXTKEY(attr_hash, attr_key) - Net::ZooKeeper attr_hash - SV *attr_key - PREINIT: - zk_t *zk; - char *key; - int i; - PPCODE: - zk = _zk_get_handle_inner(aTHX_ attr_hash); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_KEYS; ++i) { - if (strcaseEQ(key, zk_keys[i].name)) { - ++i; - - break; - } - } - - if (i < NUM_KEYS) { - ST(0) = sv_2mortal(newSVpvn(zk_keys[i].name, zk_keys[i].name_len)); - - XSRETURN(1); - } - else { - XSRETURN_EMPTY; - } - - -void -zk_SCALAR(attr_hash) - Net::ZooKeeper attr_hash - PPCODE: - XSRETURN_YES; - - -void -zk_FETCH(attr_hash, attr_key) - Net::ZooKeeper attr_hash - SV *attr_key - PREINIT: - zk_t *zk; - char *key; - SV *val = NULL; - PPCODE: - zk = _zk_get_handle_inner(aTHX_ attr_hash); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - if (strcaseEQ(key, "data_read_len")) { - val = newSViv(zk->data_buf_len); - } - else if (strcaseEQ(key, "path_read_len")) { - val = newSViv(zk->path_buf_len); - } - else if (strcaseEQ(key, "watch_timeout")) { - val = newSVuv(zk->watch_timeout); - } - else if (strcaseEQ(key, "hosts")) { - val = newSVpvn(zk->hosts, zk->hosts_len); - } - else if (strcaseEQ(key, "session_timeout")) { - val = newSViv(zoo_recv_timeout(zk->handle)); - } - else if (strcaseEQ(key, "session_id")) { - const clientid_t *client_id; - clientid_t null_client_id; - - client_id = zoo_client_id(zk->handle); - - memset(&null_client_id, 0, sizeof(clientid_t)); - - if (!memcmp(client_id, &null_client_id, sizeof(clientid_t))) { - val = newSVpv("", 0); - } - else { - val = newSVpvn((const char*) client_id, sizeof(clientid_t)); - } - } - else if (strcaseEQ(key, "pending_watches")) { - /* cleanup any completed watches not tied to a handle */ - val = newSVuv(_zk_release_watches(aTHX_ zk->first_watch, 0)); - } - - if (val) { - ST(0) = sv_2mortal(val); - - XSRETURN(1); - } - - Perl_warn(aTHX_ "invalid element: %s", key); - - XSRETURN_UNDEF; - - -void -zk_STORE(attr_hash, attr_key, attr_val) - Net::ZooKeeper attr_hash - SV *attr_key - SV *attr_val - PREINIT: - zk_t *zk; - char *key; - PPCODE: - zk = _zk_get_handle_inner(aTHX_ attr_hash); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - if (strcaseEQ(key, "data_read_len")) { - int val = SvIV(attr_val); - - if (val < 0) { - Perl_croak(aTHX_ "invalid data read length: %d", val); - } - - zk->data_buf_len = val; - } - else if (strcaseEQ(key, "path_read_len")) { - int val = SvIV(attr_val); - - if (val < 0) { - Perl_croak(aTHX_ "invalid path read length: %d", val); - } - - zk->path_buf_len = val; - } - else if (strcaseEQ(key, "watch_timeout")) { - zk->watch_timeout = SvUV(attr_val); - } - else { - int i; - - for (i = 0; i < NUM_KEYS; ++i) { - if (strcaseEQ(key, zk_keys[i].name)) { - Perl_warn(aTHX_ "read-only element: %s", key); - - XSRETURN_EMPTY; - } - } - - Perl_warn(aTHX_ "invalid element: %s", key); - } - - XSRETURN_EMPTY; - - -void -zk_EXISTS(attr_hash, attr_key) - Net::ZooKeeper attr_hash - SV *attr_key - PREINIT: - zk_t *zk; - char *key; - int i; - PPCODE: - zk = _zk_get_handle_inner(aTHX_ attr_hash); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_KEYS; ++i) { - if (strcaseEQ(key, zk_keys[i].name)) { - XSRETURN_YES; - } - } - - XSRETURN_NO; - - -void -zk_DELETE(attr_hash, attr_key) - Net::ZooKeeper attr_hash - SV *attr_key - PPCODE: - Perl_warn(aTHX_ "deleting elements from hashes of class " - PACKAGE_NAME " not supported"); - - XSRETURN_EMPTY; - - -void -zk_CLEAR(attr_hash) - Net::ZooKeeper attr_hash - PPCODE: - Perl_warn(aTHX_ "clearing hashes of class " - PACKAGE_NAME " not supported"); - - XSRETURN_EMPTY; - - -SV * -zk_get_error(zkh) - Net::ZooKeeper zkh - PREINIT: - zk_t *zk; - CODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - RETVAL = newSViv(zk->last_ret); - errno = zk->last_errno; - OUTPUT: - RETVAL - - -void -zk_add_auth(zkh, scheme, cert) - Net::ZooKeeper zkh - char *scheme - char *cert; cert = (char *) SvPV($arg, cert_len); - PREINIT: - zk_t *zk; - STRLEN cert_len; - zk_watch_t *watch; - int ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (cert_len > PERL_INT_MAX) { - Perl_croak(aTHX_ "invalid certificate length: %u", cert_len); - } - - watch = _zk_create_watch(aTHX); - - if (!watch) { - /* errno will be set */ - zk->last_ret = ZSYSTEMERROR; - zk->last_errno = errno; - - XSRETURN_NO; - } - - errno = 0; - ret = zoo_add_auth(zk->handle, scheme, cert, cert_len, - _zk_auth_completion, watch); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (ret == ZOK) { - pthread_mutex_lock(&watch->mutex); - - while (!watch->done) { - pthread_cond_wait(&watch->cond, &watch->mutex); - } - - pthread_mutex_unlock(&watch->mutex); - - if (watch->done) { - ret = watch->ret; - } - else { - ret = ZINVALIDSTATE; - } - - /* errno may be set while we waited */ - zk->last_ret = ret; - zk->last_errno = errno; - } - - _zk_destroy_watch(aTHX_ watch); - - if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zk_create(zkh, path, buf, ...) - Net::ZooKeeper zkh - char *path - char *buf; buf = (char *) SvPV($arg, buf_len); - PREINIT: - zk_t *zk; - STRLEN buf_len; - int flags = 0; - char *path_buf; - int path_buf_len; - AV *acl_arr = NULL; - struct ACL_vector acl; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 3 && !(items % 2)) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - if (buf_len > PERL_INT_MAX) { - Perl_croak(aTHX_ "invalid data length: %u", buf_len); - } - - path_buf_len = zk->path_buf_len; - - for (i = 3; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "path_read_len")) { - path_buf_len = SvIV(ST(i + 1)); - - if (path_buf_len < 2) { - Perl_croak(aTHX_ "invalid path read length: %d", - path_buf_len); - } - } - else if (strcaseEQ(key, "flags")) { - flags = SvIV(ST(i + 1)); - - if (flags & ~(ZOO_SEQUENCE | ZOO_EPHEMERAL)) { - Perl_croak(aTHX_ "invalid create flags: %d", flags); - } - } - else if (strcaseEQ(key, "acl")) { - const char *err; - - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVAV) { - Perl_croak(aTHX_ "invalid ACL array reference"); - } - - acl_arr = (AV*) SvRV(ST(i + 1)); - - err = _zk_fill_acl(aTHX_ acl_arr, &acl); - - if (err) { - Perl_croak(aTHX_ err); - } - } - } - - /* NOTE: would be nice to be able to rely on null-terminated string */ - ++path_buf_len; - Newxz(path_buf, path_buf_len, char); - - errno = 0; - ret = zoo_create(zk->handle, path, buf, buf_len, - (acl_arr ? &acl : NULL), flags, - path_buf, path_buf_len); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (acl_arr) { - _zk_free_acl(aTHX_ &acl); - } - - if (ret == ZOK) { - ST(0) = sv_newmortal(); -#ifdef SV_HAS_TRAILING_NUL - sv_usepvn_flags(ST(0), path_buf, strlen(path_buf), - SV_HAS_TRAILING_NUL); -#else - sv_usepvn(ST(0), path_buf, strlen(path_buf)); -#endif - SvCUR_set(ST(0), strlen(path_buf)); - - XSRETURN(1); - } - - Safefree(path_buf); - - XSRETURN_UNDEF; - - -void -zk_delete(zkh, path, ...) - Net::ZooKeeper zkh - char *path - PREINIT: - zk_t *zk; - int version = -1; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 2 && items % 2) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - for (i = 2; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "version")) { - version = SvIV(ST(i + 1)); - - if (version < 0) { - Perl_croak(aTHX_ "invalid version requirement: %d", - version); - } - } - } - - errno = 0; - ret = zoo_delete(zk->handle, path, version); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zk_exists(zkh, path, ...) - Net::ZooKeeper zkh - char *path - PREINIT: - zk_t *zk; - zk_stat_t *stat = NULL; - zk_watch_t *old_watch = NULL; - zk_handle_t *watch_handle = NULL; - watcher_fn watcher = NULL; - zk_watch_t *new_watch = NULL; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 2 && items % 2) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - for (i = 2; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "stat")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), STAT_PACKAGE_NAME)) { - Perl_croak(aTHX_ "stat is not a hash reference of " - "type " STAT_PACKAGE_NAME); - } - - stat = _zks_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1))); - - if (!stat) { - Perl_croak(aTHX_ "invalid stat handle"); - } - } - else if (strcaseEQ(key, "watch")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), WATCH_PACKAGE_NAME)) { - Perl_croak(aTHX_ "watch is not a hash reference of " - "type " WATCH_PACKAGE_NAME); - } - - old_watch = _zkw_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1)), - &watch_handle); - - if (!old_watch) { - Perl_croak(aTHX_ "invalid watch handle"); - } - } - } - - if (watch_handle) { - new_watch = _zk_acquire_watch(aTHX); - - if (!new_watch) { - /* errno will be set */ - zk->last_ret = ZSYSTEMERROR; - zk->last_errno = errno; - - XSRETURN_NO; - } - - watcher = _zk_watcher; - } - - errno = 0; - ret = zoo_wexists(zk->handle, path, watcher, new_watch, stat); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (watch_handle) { - _zk_replace_watch(aTHX_ watch_handle, zk->first_watch, - old_watch, new_watch); - } - - if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zk_get_children(zkh, path, ...) - Net::ZooKeeper zkh - char *path - PREINIT: - zk_t *zk; - zk_watch_t *old_watch = NULL; - zk_handle_t *watch_handle = NULL; - watcher_fn watcher = NULL; - zk_watch_t *new_watch = NULL; - struct String_vector strings; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 2 && items % 2) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - for (i = 2; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "watch")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), WATCH_PACKAGE_NAME)) { - Perl_croak(aTHX_ "watch is not a hash reference of " - "type " WATCH_PACKAGE_NAME); - } - - old_watch = _zkw_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1)), - &watch_handle); - - if (!old_watch) { - Perl_croak(aTHX_ "invalid watch handle"); - } - } - } - - if (watch_handle) { - new_watch = _zk_acquire_watch(aTHX); - - if (!new_watch) { - /* errno will be set */ - zk->last_ret = ZSYSTEMERROR; - zk->last_errno = errno; - - if (GIMME_V == G_ARRAY) { - XSRETURN_EMPTY; - } - else { - XSRETURN_UNDEF; - } - } - - watcher = _zk_watcher; - } - - Zero(&strings, 1, struct String_vector); - - errno = 0; - ret = zoo_wget_children(zk->handle, path, watcher, new_watch, - &strings); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (watch_handle) { - _zk_replace_watch(aTHX_ watch_handle, zk->first_watch, - old_watch, new_watch); - } - - if (ret == ZOK) { - int num_children; - - num_children = - (strings.count > PERL_INT_MAX) ? PERL_INT_MAX : strings.count; - - if (GIMME_V == G_ARRAY && num_children > 0) { - EXTEND(SP, num_children); - - for (i = 0; i < num_children; ++i) { - ST(i) = sv_2mortal(newSVpv(strings.data[i], 0)); - } - } - - /* NOTE: would be nice if this were documented as required */ - deallocate_String_vector(&strings); - - if (GIMME_V == G_ARRAY) { - if (num_children == 0) { - XSRETURN_EMPTY; - } - - XSRETURN(num_children); - } - else { - ST(0) = sv_2mortal(newSViv(num_children)); - - XSRETURN(1); - } - } - else { - if (GIMME_V == G_ARRAY) { - XSRETURN_EMPTY; - } - else { - XSRETURN_UNDEF; - } - } - - -void -zk_get(zkh, path, ...) - Net::ZooKeeper zkh - char *path - PREINIT: - zk_t *zk; - int buf_len; - zk_stat_t *stat = NULL; - zk_watch_t *old_watch = NULL; - zk_handle_t *watch_handle = NULL; - char *buf; - watcher_fn watcher = NULL; - zk_watch_t *new_watch = NULL; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 2 && items % 2) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - buf_len = zk->data_buf_len; - - for (i = 2; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "data_read_len")) { - buf_len = SvIV(ST(i + 1)); - - if (buf_len < 0) { - Perl_croak(aTHX_ "invalid data read length: %d", - buf_len); - } - } - else if (strcaseEQ(key, "stat")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), STAT_PACKAGE_NAME)) { - Perl_croak(aTHX_ "stat is not a hash reference of " - "type " STAT_PACKAGE_NAME); - } - - stat = _zks_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1))); - - if (!stat) { - Perl_croak(aTHX_ "invalid stat handle"); - } - } - else if (strcaseEQ(key, "watch")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), WATCH_PACKAGE_NAME)) { - Perl_croak(aTHX_ "watch is not a hash reference of " - "type " WATCH_PACKAGE_NAME); - } - - old_watch = _zkw_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1)), - &watch_handle); - - if (!old_watch) { - Perl_croak(aTHX_ "invalid watch handle"); - } - } - } - - if (watch_handle) { - new_watch = _zk_acquire_watch(aTHX); - - if (!new_watch) { - /* errno will be set */ - zk->last_ret = ZSYSTEMERROR; - zk->last_errno = errno; - - XSRETURN_UNDEF; - } - - watcher = _zk_watcher; - } - - Newx(buf, buf_len + 1, char); - - errno = 0; - ret = zoo_wget(zk->handle, path, watcher, new_watch, - buf, &buf_len, stat); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (watch_handle) { - _zk_replace_watch(aTHX_ watch_handle, zk->first_watch, - old_watch, new_watch); - } - - if (ret == ZOK) { - ST(0) = sv_newmortal(); -#ifdef SV_HAS_TRAILING_NUL - buf[buf_len] = '\0'; - sv_usepvn_flags(ST(0), buf, buf_len, SV_HAS_TRAILING_NUL); -#else - sv_usepvn(ST(0), buf, buf_len); -#endif - - XSRETURN(1); - } - else { - Safefree(buf); - - XSRETURN_UNDEF; - } - - -void -zk_set(zkh, path, buf, ...) - Net::ZooKeeper zkh - char *path - char *buf; buf = (char *) SvPV($arg, buf_len); - PREINIT: - zk_t *zk; - int version = -1; - zk_stat_t *stat = NULL; - STRLEN buf_len; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 3 && !(items % 2)) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - if (buf_len > PERL_INT_MAX) { - Perl_croak(aTHX_ "invalid data length: %u", buf_len); - } - - for (i = 3; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "version")) { - version = SvIV(ST(i + 1)); - - if (version < 0) { - Perl_croak(aTHX_ "invalid version requirement: %d", - version); - } - } - else if (strcaseEQ(key, "stat")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), STAT_PACKAGE_NAME)) { - Perl_croak(aTHX_ "stat is not a hash reference of " - "type " STAT_PACKAGE_NAME); - } - - stat = _zks_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1))); - - if (!stat) { - Perl_croak(aTHX_ "invalid stat handle"); - } - } - } - - errno = 0; - ret = zoo_set2(zk->handle, path, buf, buf_len, version, stat); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zk_get_acl(zkh, path, ...) - Net::ZooKeeper zkh - char *path - PREINIT: - zk_t *zk; - zk_stat_t *stat = NULL; - struct ACL_vector acl; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 2 && items % 2) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - for (i = 2; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "stat")) { - if (!SvROK(ST(i + 1)) || SvTYPE(SvRV(ST(i + 1))) != SVt_PVHV || - !sv_derived_from(ST(i + 1), STAT_PACKAGE_NAME)) { - Perl_croak(aTHX_ "stat is not a hash reference of " - "type " STAT_PACKAGE_NAME); - } - - stat = _zks_get_handle_outer(aTHX_ (HV*) SvRV(ST(i + 1))); - - if (!stat) { - Perl_croak(aTHX_ "invalid stat handle"); - } - } - } - - errno = 0; - ret = zoo_get_acl(zk->handle, path, &acl, stat); - - zk->last_ret = ret; - zk->last_errno = errno; - - if (ret == ZOK) { - int num_acl_entries; - - num_acl_entries = - (acl.count > PERL_INT_MAX) ? PERL_INT_MAX : acl.count; - - if (GIMME_V == G_ARRAY && num_acl_entries > 0) { - EXTEND(SP, num_acl_entries); - - for (i = 0; i < num_acl_entries; ++i) { - HV *acl_entry_hash = newHV(); - - _zk_fill_acl_entry_hash(aTHX_ &acl.data[i], - acl_entry_hash); - - ST(i) = sv_2mortal(newRV_noinc((SV*) acl_entry_hash)); - } - } - - /* NOTE: would be nice if this were documented as required */ - deallocate_ACL_vector(&acl); - - if (GIMME_V == G_ARRAY) { - if (num_acl_entries == 0) { - XSRETURN_EMPTY; - } - - XSRETURN(num_acl_entries); - } - else { - ST(0) = sv_2mortal(newSViv(num_acl_entries)); - - XSRETURN(1); - } - } - else { - if (GIMME_V == G_ARRAY) { - XSRETURN_EMPTY; - } - else { - XSRETURN_UNDEF; - } - } - - -void -zk_set_acl(zkh, path, acl_arr, ...) - Net::ZooKeeper zkh - char *path - AV *acl_arr - PREINIT: - zk_t *zk; - const char *err; - int version = -1; - struct ACL_vector acl; - int i, ret; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 3 && !(items % 2)) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - err = _zk_fill_acl(aTHX_ acl_arr, &acl); - - if (err) { - Perl_croak(aTHX_ err); - } - - for (i = 3; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "version")) { - version = SvIV(ST(i + 1)); - - if (version < 0) { - Perl_croak(aTHX_ "invalid version requirement: %d", - version); - } - } - } - - errno = 0; - ret = zoo_set_acl(zk->handle, path, version, &acl); - - zk->last_ret = ret; - zk->last_errno = errno; - - _zk_free_acl(aTHX_ &acl); - - if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zk_stat(zkh) - Net::ZooKeeper zkh - PREINIT: - zk_t *zk; - zk_handle_t *handle; - HV *stash, *stat_hash, *attr_hash; - SV *attr; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - Newx(handle, 1, zk_handle_t); - - handle->signature = STAT_PACKAGE_SIGNATURE; - - Newxz(handle->handle.stat, 1, zk_stat_t); - - /* As in zk_new(), we use two levels of magic here. */ - - stash = gv_stashpv(STAT_PACKAGE_NAME, GV_ADDWARN); - - attr_hash = newHV(); - - sv_magic((SV*) attr_hash, Nullsv, PERL_MAGIC_ext, - (const char*) handle, 0); - - attr = sv_bless(newRV_noinc((SV*) attr_hash), stash); - - stat_hash = newHV(); - - sv_magic((SV*) stat_hash, attr, PERL_MAGIC_tied, Nullch, 0); - SvREFCNT_dec(attr); - - ST(0) = sv_bless(sv_2mortal(newRV_noinc((SV*) stat_hash)), stash); - - XSRETURN(1); - - -void -zk_watch(zkh, ...) - Net::ZooKeeper zkh - PREINIT: - zk_t *zk; - unsigned int timeout; - zk_watch_t *watch; - zk_handle_t *handle; - HV *stash, *watch_hash, *attr_hash; - SV *attr; - int i; - PPCODE: - zk = _zk_get_handle_outer(aTHX_ zkh); - - if (!zk) { - Perl_croak(aTHX_ "invalid handle"); - } - - zk->last_ret = ZOK; - zk->last_errno = 0; - - if (items > 1 && !(items % 2)) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - timeout = zk->watch_timeout; - - for (i = 1; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "timeout")) { - timeout = SvUV(ST(i + 1)); - } - } - - watch = _zk_acquire_watch(aTHX); - - if (!watch) { - /* errno will be set */ - zk->last_ret = ZSYSTEMERROR; - zk->last_errno = errno; - - XSRETURN_UNDEF; - } - - Newx(handle, 1, zk_handle_t); - - handle->signature = WATCH_PACKAGE_SIGNATURE; - handle->handle.watch = watch; - - /* As in zk_new(), we use two levels of magic here. */ - - stash = gv_stashpv(WATCH_PACKAGE_NAME, GV_ADDWARN); - - attr_hash = newHV(); - - watch->timeout = timeout; - - sv_magic((SV*) attr_hash, Nullsv, PERL_MAGIC_ext, - (const char*) handle, 0); - - attr = sv_bless(newRV_noinc((SV*) attr_hash), stash); - - watch_hash = newHV(); - - sv_magic((SV*) watch_hash, attr, PERL_MAGIC_tied, Nullch, 0); - SvREFCNT_dec(attr); - - ST(0) = sv_bless(sv_2mortal(newRV_noinc((SV*) watch_hash)), stash); - - XSRETURN(1); - - -MODULE = Net::ZooKeeper PACKAGE = Net::ZooKeeper::Stat PREFIX = zks_ - -void -zks_DESTROY(zksh) - Net::ZooKeeper::Stat zksh - PREINIT: - zk_handle_t *handle; - HV *attr_hash; - int ret = ZBADARGUMENTS; - PPCODE: - handle = _zk_check_handle_outer(aTHX_ zksh, &attr_hash, - STAT_PACKAGE_NAME, - STAT_PACKAGE_SIGNATURE); - - if (!handle) { - handle = _zk_check_handle_inner(aTHX_ zksh, - STAT_PACKAGE_SIGNATURE); - - if (handle) { - attr_hash = zksh; - zksh = NULL; - } - } - - if (handle) { - ret = ZOK; - - Safefree(handle->handle.stat); - Safefree(handle); - - sv_unmagic((SV*) attr_hash, PERL_MAGIC_ext); - } - - if (zksh && attr_hash) { - sv_unmagic((SV*) zksh, PERL_MAGIC_tied); - } - - if (GIMME_V == G_VOID) { - XSRETURN_EMPTY; - } - else if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zks_CLONE(package) - char *package - PPCODE: - XSRETURN_EMPTY; - - -void -zks_CLONE_SKIP(package) - char *package - PPCODE: - XSRETURN_YES; - - -void -zks_TIEHASH(package, ...) - char *package - PPCODE: - Perl_croak(aTHX_ "tying hashes of class " - STAT_PACKAGE_NAME " not supported"); - - -void -zks_UNTIE(attr_hash, ref_count) - Net::ZooKeeper::Stat attr_hash - IV ref_count - PPCODE: - Perl_croak(aTHX_ "untying hashes of class " - STAT_PACKAGE_NAME " not supported"); - - -void -zks_FIRSTKEY(attr_hash) - Net::ZooKeeper::Stat attr_hash - PREINIT: - zk_stat_t *stat; - PPCODE: - stat = _zks_get_handle_inner(aTHX_ attr_hash); - - if (!stat) { - Perl_croak(aTHX_ "invalid handle"); - } - - ST(0) = sv_2mortal(newSVpvn(zk_stat_keys[0].name, - zk_stat_keys[0].name_len)); - - XSRETURN(1); - - -void -zks_NEXTKEY(attr_hash, attr_key) - Net::ZooKeeper::Stat attr_hash - SV *attr_key - PREINIT: - zk_stat_t *stat; - char *key; - int i; - PPCODE: - stat = _zks_get_handle_inner(aTHX_ attr_hash); - - if (!stat) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_STAT_KEYS; ++i) { - if (strcaseEQ(key, zk_stat_keys[i].name)) { - ++i; - - break; - } - } - - if (i < NUM_STAT_KEYS) { - ST(0) = sv_2mortal(newSVpvn(zk_stat_keys[i].name, - zk_stat_keys[i].name_len)); - - XSRETURN(1); - } - else { - XSRETURN_EMPTY; - } - - -void -zks_SCALAR(attr_hash) - Net::ZooKeeper::Stat attr_hash - PPCODE: - XSRETURN_YES; - - -void -zks_FETCH(attr_hash, attr_key) - Net::ZooKeeper::Stat attr_hash - SV *attr_key - PREINIT: - zk_stat_t *stat; - char *key; - SV *val = NULL; - int i; - PPCODE: - stat = _zks_get_handle_inner(aTHX_ attr_hash); - - if (!stat) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_STAT_KEYS; ++i) { - if (strcaseEQ(key, zk_stat_keys[i].name)) { - if (zk_stat_keys[i].size * CHAR_BIT == 32) { - val = newSViv(*((int32_t*) (((char*) stat) + - zk_stat_keys[i].offset))); - } - else { - /* NOTE: %lld is inconsistent, so cast to a double */ - val = newSVpvf("%.0f", (double) - *((int64_t*) (((char*) stat) + - zk_stat_keys[i].offset))); - } - - break; - } - } - - if (val) { - ST(0) = sv_2mortal(val); - - XSRETURN(1); - } - - Perl_warn(aTHX_ "invalid element: %s", key); - - XSRETURN_UNDEF; - - -void -zks_STORE(attr_hash, attr_key, attr_val) - Net::ZooKeeper::Stat attr_hash - SV *attr_key - SV *attr_val - PREINIT: - zk_stat_t *stat; - char *key; - int i; - PPCODE: - stat = _zks_get_handle_inner(aTHX_ attr_hash); - - if (!stat) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_STAT_KEYS; ++i) { - if (strcaseEQ(key, zk_stat_keys[i].name)) { - Perl_warn(aTHX_ "read-only element: %s", key); - - XSRETURN_EMPTY; - } - } - - Perl_warn(aTHX_ "invalid element: %s", key); - - XSRETURN_EMPTY; - - -void -zks_EXISTS(attr_hash, attr_key) - Net::ZooKeeper::Stat attr_hash - SV *attr_key - PREINIT: - zk_stat_t *stat; - char *key; - int i; - PPCODE: - stat = _zks_get_handle_inner(aTHX_ attr_hash); - - if (!stat) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_STAT_KEYS; ++i) { - if (strcaseEQ(key, zk_stat_keys[i].name)) { - XSRETURN_YES; - } - } - - XSRETURN_NO; - - -void -zks_DELETE(attr_hash, attr_key) - Net::ZooKeeper::Stat attr_hash - SV *attr_key - PPCODE: - Perl_warn(aTHX_ "deleting elements from hashes of class " - STAT_PACKAGE_NAME " not supported"); - - XSRETURN_EMPTY; - - -void -zks_CLEAR(attr_hash) - Net::ZooKeeper::Stat attr_hash - PPCODE: - Perl_warn(aTHX_ "clearing hashes of class " - STAT_PACKAGE_NAME " not supported"); - - XSRETURN_EMPTY; - - -MODULE = Net::ZooKeeper PACKAGE = Net::ZooKeeper::Watch PREFIX = zkw_ - -void -zkw_DESTROY(zkwh) - Net::ZooKeeper::Watch zkwh - PREINIT: - zk_handle_t *handle; - HV *attr_hash; - int ret = ZBADARGUMENTS; - PPCODE: - handle = _zk_check_handle_outer(aTHX_ zkwh, &attr_hash, - WATCH_PACKAGE_NAME, - WATCH_PACKAGE_SIGNATURE); - - if (!handle) { - handle = _zk_check_handle_inner(aTHX_ zkwh, - WATCH_PACKAGE_SIGNATURE); - - if (handle) { - attr_hash = zkwh; - zkwh = NULL; - } - } - - if (handle) { - ret = ZOK; - - _zk_release_watch(aTHX_ handle->handle.watch, 0); - Safefree(handle); - - sv_unmagic((SV*) attr_hash, PERL_MAGIC_ext); - } - - if (zkwh && attr_hash) { - sv_unmagic((SV*) zkwh, PERL_MAGIC_tied); - } - - if (GIMME_V == G_VOID) { - XSRETURN_EMPTY; - } - else if (ret == ZOK) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - - -void -zkw_CLONE(package) - char *package - PPCODE: - XSRETURN_EMPTY; - - -void -zkw_CLONE_SKIP(package) - char *package - PPCODE: - XSRETURN_YES; - - -void -zkw_TIEHASH(package, ...) - char *package - PPCODE: - Perl_croak(aTHX_ "tying hashes of class " - WATCH_PACKAGE_NAME " not supported"); - - -void -zkw_UNTIE(attr_hash, ref_count) - Net::ZooKeeper::Watch attr_hash - IV ref_count - PPCODE: - Perl_croak(aTHX_ "untying hashes of class " - WATCH_PACKAGE_NAME " not supported"); - - -void -zkw_FIRSTKEY(attr_hash) - Net::ZooKeeper::Watch attr_hash - PREINIT: - zk_watch_t *watch; - PPCODE: - watch = _zkw_get_handle_inner(aTHX_ attr_hash); - - if (!watch) { - Perl_croak(aTHX_ "invalid handle"); - } - - ST(0) = sv_2mortal(newSVpvn(zk_watch_keys[0].name, - zk_watch_keys[0].name_len)); - - XSRETURN(1); - - -void -zkw_NEXTKEY(attr_hash, attr_key) - Net::ZooKeeper::Watch attr_hash - SV *attr_key - PREINIT: - zk_watch_t *watch; - char *key; - int i; - PPCODE: - watch = _zkw_get_handle_inner(aTHX_ attr_hash); - - if (!watch) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_WATCH_KEYS; ++i) { - if (strcaseEQ(key, zk_watch_keys[i].name)) { - ++i; - - break; - } - } - - if (i < NUM_WATCH_KEYS) { - ST(0) = sv_2mortal(newSVpvn(zk_watch_keys[i].name, - zk_watch_keys[i].name_len)); - - XSRETURN(1); - } - else { - XSRETURN_EMPTY; - } - - -void -zkw_SCALAR(attr_hash) - Net::ZooKeeper::Watch attr_hash - PPCODE: - XSRETURN_YES; - - -void -zkw_FETCH(attr_hash, attr_key) - Net::ZooKeeper::Watch attr_hash - SV *attr_key - PREINIT: - zk_watch_t *watch; - char *key; - SV *val = NULL; - PPCODE: - watch = _zkw_get_handle_inner(aTHX_ attr_hash); - - if (!watch) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - if (strcaseEQ(key, "timeout")) { - val = newSVuv(watch->timeout); - } - else if (strcaseEQ(key, "event")) { - val = newSViv(watch->event_type); - } - else if (strcaseEQ(key, "state")) { - val = newSViv(watch->event_state); - } - - if (val) { - ST(0) = sv_2mortal(val); - - XSRETURN(1); - } - - Perl_warn(aTHX_ "invalid element: %s", key); - - XSRETURN_UNDEF; - - -void -zkw_STORE(attr_hash, attr_key, attr_val) - Net::ZooKeeper::Watch attr_hash - SV *attr_key - SV *attr_val - PREINIT: - zk_watch_t *watch; - char *key; - PPCODE: - watch = _zkw_get_handle_inner(aTHX_ attr_hash); - - if (!watch) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - if (strcaseEQ(key, "timeout")) { - watch->timeout = SvUV(attr_val); - } - else { - int i; - - for (i = 0; i < NUM_WATCH_KEYS; ++i) { - if (strcaseEQ(key, zk_watch_keys[i].name)) { - Perl_warn(aTHX_ "read-only element: %s", key); - - XSRETURN_EMPTY; - } - } - - Perl_warn(aTHX_ "invalid element: %s", key); - } - - XSRETURN_EMPTY; - - -void -zkw_EXISTS(attr_hash, attr_key) - Net::ZooKeeper::Watch attr_hash - SV *attr_key - PREINIT: - zk_watch_t *watch; - char *key; - int i; - PPCODE: - watch = _zkw_get_handle_inner(aTHX_ attr_hash); - - if (!watch) { - Perl_croak(aTHX_ "invalid handle"); - } - - key = SvPV_nolen(attr_key); - - for (i = 0; i < NUM_WATCH_KEYS; ++i) { - if (strcaseEQ(key, zk_watch_keys[i].name)) { - XSRETURN_YES; - } - } - - XSRETURN_NO; - - -void -zkw_DELETE(attr_hash, attr_key) - Net::ZooKeeper::Watch attr_hash - SV *attr_key - PPCODE: - Perl_warn(aTHX_ "deleting elements from hashes of class " - WATCH_PACKAGE_NAME " not supported"); - - XSRETURN_EMPTY; - - -void -zkw_CLEAR(attr_hash) - Net::ZooKeeper::Watch attr_hash - PPCODE: - Perl_warn(aTHX_ "clearing hashes of class " - WATCH_PACKAGE_NAME " not supported"); - - XSRETURN_EMPTY; - - -void -zkw_wait(zkwh, ...) - Net::ZooKeeper::Watch zkwh - PREINIT: - zk_watch_t *watch; - unsigned int timeout; - struct timeval end_timeval; - int i, done; - PPCODE: - watch = _zkw_get_handle_outer(aTHX_ zkwh, NULL); - - if (!watch) { - Perl_croak(aTHX_ "invalid handle"); - } - - if (items > 1 && !(items % 2)) { - Perl_croak(aTHX_ "invalid number of arguments"); - } - - timeout = watch->timeout; - - for (i = 1; i < items; i += 2) { - char *key = SvPV_nolen(ST(i)); - - if (strcaseEQ(key, "timeout")) { - timeout = SvUV(ST(i + 1)); - } - } - - gettimeofday(&end_timeval, NULL); - - end_timeval.tv_sec += timeout / 1000; - end_timeval.tv_usec += (timeout % 1000) * 1000; - - pthread_mutex_lock(&watch->mutex); - - while (!watch->done) { - struct timeval curr_timeval; - struct timespec wait_timespec; - - gettimeofday(&curr_timeval, NULL); - - wait_timespec.tv_sec = end_timeval.tv_sec - curr_timeval.tv_sec; - wait_timespec.tv_nsec = - (end_timeval.tv_usec - curr_timeval.tv_usec) * 1000; - - if (wait_timespec.tv_nsec < 0) { - --wait_timespec.tv_sec; - wait_timespec.tv_nsec += 1000000000; - } - - if (wait_timespec.tv_sec < 0 || - (wait_timespec.tv_sec == 0 && wait_timespec.tv_nsec <= 0)) { - break; - } - - pthread_cond_timedwait(&watch->cond, &watch->mutex, - &wait_timespec); - } - - done = watch->done; - - pthread_mutex_unlock(&watch->mutex); - - if (done) { - XSRETURN_YES; - } - else { - XSRETURN_NO; - } - diff --git a/src/contrib/zkperl/build.xml b/src/contrib/zkperl/build.xml deleted file mode 100644 index 0c7207dc020..00000000000 --- a/src/contrib/zkperl/build.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/zkperl/build/check_zk_version.c b/src/contrib/zkperl/build/check_zk_version.c deleted file mode 100644 index 591280c0829..00000000000 --- a/src/contrib/zkperl/build/check_zk_version.c +++ /dev/null @@ -1,25 +0,0 @@ -/* Net::ZooKeeper - Perl extension for Apache ZooKeeper - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "zookeeper_version.h" - -#include "check_zk_version.h" - -int main() {} - diff --git a/src/contrib/zkperl/build/check_zk_version.h b/src/contrib/zkperl/build/check_zk_version.h deleted file mode 100644 index 67a36420f78..00000000000 --- a/src/contrib/zkperl/build/check_zk_version.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Net::ZooKeeper - Perl extension for Apache ZooKeeper - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* keep in sync with Makefile.PL */ -#if !defined(ZOO_MAJOR_VERSION) || ZOO_MAJOR_VERSION != 3 || \ - !defined(ZOO_MINOR_VERSION) || ZOO_MINOR_VERSION < 1 || \ - !defined(ZOO_PATCH_VERSION) || \ - (ZOO_MINOR_VERSION == 1 && ZOO_PATCH_VERSION < 1) -#error "Net::ZooKeeper requires at least ZooKeeper version 3.1.1" -#endif - diff --git a/src/contrib/zkperl/t/10_invalid.t b/src/contrib/zkperl/t/10_invalid.t deleted file mode 100644 index 5e080b64c7d..00000000000 --- a/src/contrib/zkperl/t/10_invalid.t +++ /dev/null @@ -1,773 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 107; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(1); - - -## new() - -eval { - Net::ZooKeeper->new(); -}; -like($@, qr/Usage: Net::ZooKeeper::new\(package, hosts, \.\.\.\)/, - 'new(): no hostname specified'); - -eval { - Net::ZooKeeper->new($hosts, 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'new(): invalid number of arguments'); - -eval { - Net::ZooKeeper->new($hosts, 'session_timeout' => -3); -}; -like($@, qr/invalid session timeout/, - 'new(): invalid session timeout'); - -eval { - Net::ZooKeeper->new($hosts, 'session_timeout' => 0x4000_0000); -}; -like($@, qr/invalid session timeout/, - 'new(): invalid session timeout'); - -eval { - Net::ZooKeeper->new($hosts, 'session_id' => 'abcdef'); -}; -like($@, qr/invalid session ID/, - 'new(): invalid session ID'); - -my $zkh = Net::ZooKeeper->new($hosts); -isa_ok($zkh, 'Net::ZooKeeper', - 'new(): created handle'); - - -## DESTROY() - -eval { - $zkh->DESTROY('foo'); -}; -like($@, qr/Usage: Net::ZooKeeper::DESTROY\(zkh\)/, - 'DESTROY(): too many arguments'); - -my $bad_zkh = {}; -$bad_zkh = bless($bad_zkh, 'Net::ZooKeeper'); - -my $ret = $bad_zkh->DESTROY(); -ok(!$ret, - 'DESTROY(): no action on invalid handle'); - - -## add_auth() - -eval { - $zkh->add_auth(); -}; -like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/, - 'add_auth(): no scheme specified'); - -eval { - $zkh->add_auth('foo'); -}; -like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/, - 'add_auth(): no certificate specified'); - -eval { - $zkh->add_auth('foo', 'foo', 'bar'); -}; -like($@, qr/Usage: Net::ZooKeeper::add_auth\(zkh, scheme, cert\)/, - 'add_auth(): too many arguments'); - -eval { - $bad_zkh->add_auth('foo', 'foo'); -}; -like($@, qr/invalid handle/, - 'add_auth(): invalid handle'); - -eval { - Net::ZooKeeper::add_auth(1, 'foo', 'foo'); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'add_auth(): invalid hash reference'); - - -## create() - -eval { - $zkh->create(); -}; -like($@, qr/Usage: Net::ZooKeeper::create\(zkh, path, buf, \.\.\.\)/, - 'create(): no path specified'); - -eval { - $zkh->create($node_path); -}; -like($@, qr/Usage: Net::ZooKeeper::create\(zkh, path, buf, \.\.\.\)/, - 'create(): no data buffer specified'); - -eval { - $zkh->create($node_path, 'foo', 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'create(): invalid number of arguments'); - -eval { - $zkh->create($node_path, 'foo', 'path_read_len' => -3); -}; -like($@, qr/invalid path read length/, - 'create(): invalid path read length'); - -eval { - $zkh->create($node_path, 'foo', 'path_read_len' => 1); -}; -like($@, qr/invalid path read length/, - 'create(): invalid path read length'); - -eval { - $zkh->create($node_path, 'foo', 'flags' => 15); -}; -like($@, qr/invalid create flags/, - 'create(): invalid create flags'); - -eval { - $zkh->create($node_path, 'foo', 'flags' => ZOO_EPHEMERAL, 'acl', 'foo'); -}; -like($@, qr/invalid ACL array reference/, - 'create(): invalid ACL array reference'); - -eval { - $zkh->create($node_path, 'foo', 'acl', {}); -}; -like($@, qr/invalid ACL array reference/, - 'create(): invalid ACL array reference to hash'); - -eval { - my @acl = ('foo', 'bar'); - $zkh->create($node_path, 'foo', 'acl', \@acl); -}; -like($@, qr/invalid ACL entry hash reference/, - 'create(): invalid ACL entry hash reference'); - -eval { - my @acl = ({ 'foo' => 'bar' }); - $zkh->create($node_path, 'foo', 'acl', \@acl); -}; -like($@, qr/no ACL entry perms element/, - 'create(): no ACL entry perms element'); - -eval { - my @acl = ( - { - 'perms' => -1 - } - ); - $zkh->create($node_path, 'foo', 'acl', \@acl); -}; -like($@, qr/invalid ACL entry perms/, - 'create(): invalid ACL entry perms'); - -eval { - my @acl = ( - { - 'perms' => ZOO_PERM_ALL - } - ); - $zkh->create($node_path, 'foo', 'acl', \@acl); -}; -like($@, qr/no ACL entry scheme element/, - 'create(): no ACL entry scheme element'); - -eval { - my @acl = ( - { - 'perms' => ZOO_PERM_ALL, - 'scheme' => 'foo' - } - ); - $zkh->create($node_path, 'foo', 'acl', \@acl); -}; -like($@, qr/no ACL entry id element/, - 'create(): no ACL entry id element'); - -eval { - my @acl = ( - { - 'perms' => ZOO_PERM_ALL, - 'scheme' => 'foo', - 'id' => 'bar' - }, - 'bar' - ); - $zkh->create($node_path, 'foo', 'acl', \@acl); -}; -like($@, qr/invalid ACL entry hash reference/, - 'create(): invalid second ACL entry hash reference'); - -eval { - $bad_zkh->create($node_path, 'foo'); -}; -like($@, qr/invalid handle/, - 'create(): invalid handle'); - -eval { - Net::ZooKeeper::create(1, $node_path, 'foo'); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'create(): invalid hash reference'); - - -## delete() - -eval { - $zkh->delete(); -}; -like($@, qr/Usage: Net::ZooKeeper::delete\(zkh, path, \.\.\.\)/, - 'delete(): no path specified'); - -eval { - $zkh->delete($node_path, 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'delete(): invalid number of arguments'); - -eval { - $zkh->delete($node_path, 'version' => -3); -}; -like($@, qr/invalid version requirement/, - 'delete(): invalid version requirement'); - -eval { - $bad_zkh->delete($node_path); -}; -like($@, qr/invalid handle/, - 'delete(): invalid handle'); - -eval { - Net::ZooKeeper::delete(1, $node_path); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'delete(): invalid hash reference'); - - -## exists() - -eval { - $zkh->exists(); -}; -like($@, qr/Usage: Net::ZooKeeper::exists\(zkh, path, \.\.\.\)/, - 'exists(): no path specified'); - -eval { - $zkh->exists($node_path, 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'exists(): invalid number of arguments'); - -eval { - $zkh->exists($node_path, 'watch', 'bar'); -}; -like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/, - 'exists(): invalid watch hash reference'); - -eval { - $zkh->exists($node_path, 'watch', []); -}; -like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/, - 'exists(): invalid watch hash reference to array'); - -eval { - $zkh->exists($node_path, 'stat', 'bar'); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'exists(): invalid stat hash reference'); - -eval { - $zkh->exists($node_path, 'stat', []); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'exists(): invalid stat hash reference'); - -eval { - $bad_zkh->exists($node_path); -}; -like($@, qr/invalid handle/, - 'exists(): invalid handle'); - -eval { - Net::ZooKeeper::exists(1, $node_path); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'exists(): invalid hash reference'); - - -## get_children() - -eval { - $zkh->get_children(); -}; -like($@, qr/Usage: Net::ZooKeeper::get_children\(zkh, path, \.\.\.\)/, - 'get_children(): no path specified'); - -eval { - $zkh->get_children($node_path, 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'get_children(): invalid number of arguments'); - -eval { - $zkh->get_children($node_path, 'watch', 'bar'); -}; -like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/, - 'get_children(): invalid watch hash reference'); - -eval { - $zkh->get_children($node_path, 'watch', []); -}; -like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/, - 'get_children(): invalid watch ash reference to array'); - -eval { - $bad_zkh->get_children($node_path); -}; -like($@, qr/invalid handle/, - 'get_children(): invalid handle'); - -eval { - Net::ZooKeeper::get_children(1, $node_path); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'get_children(): invalid hash reference'); - - -## get() - -eval { - $zkh->get(); -}; -like($@, qr/Usage: Net::ZooKeeper::get\(zkh, path, \.\.\.\)/, - 'get(): no path specified'); - -eval { - $zkh->get($node_path, 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'get(): invalid number of arguments'); - -eval { - $zkh->get($node_path, 'data_read_len' => -3); -}; -like($@, qr/invalid data read length/, - 'get(): invalid data read length'); - -eval { - $zkh->get($node_path, 'data_read_len' => 10, 'watch', 'bar'); -}; -like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/, - 'get(): invalid watch hash reference'); - -eval { - $zkh->get($node_path, 'watch', []); -}; -like($@, qr/watch is not a hash reference of type Net::ZooKeeper::Watch/, - 'get(): invalid watch hash reference to array'); - -eval { - $zkh->get($node_path, 'stat', 'bar'); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'get(): invalid stat hash reference'); - -eval { - $zkh->get($node_path, 'stat', []); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'get(): invalid stat hash reference'); - -eval { - $bad_zkh->get($node_path); -}; -like($@, qr/invalid handle/, - 'get(): invalid handle'); - -eval { - Net::ZooKeeper::get(1, $node_path); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'get(): invalid hash reference'); - - -## set() - -eval { - $zkh->set(); -}; -like($@, qr/Usage: Net::ZooKeeper::set\(zkh, path, buf, \.\.\.\)/, - 'set(): no path specified'); - -eval { - $zkh->set($node_path); -}; -like($@, qr/Usage: Net::ZooKeeper::set\(zkh, path, buf, \.\.\.\)/, - 'set(): no data buffer specified'); - -eval { - $zkh->set($node_path, 'foo', 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'set(): invalid number of arguments'); - -eval { - $zkh->set($node_path, 'foo', 'version' => -3); -}; -like($@, qr/invalid version requirement/, - 'set(): invalid version requirement'); - -eval { - $zkh->set($node_path, 'foo', 'version', 0, 'stat', 'bar'); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'set(): invalid stat hash reference'); - -eval { - $zkh->set($node_path, 'foo', 'stat', []); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'set(): invalid stat hash reference'); - -eval { - $bad_zkh->set($node_path, 'foo'); -}; -like($@, qr/invalid handle/, - 'set(): invalid handle'); - -eval { - Net::ZooKeeper::set(1, $node_path, 'foo'); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'set(): invalid hash reference'); - - -## get_acl() - -eval { - $zkh->get_acl(); -}; -like($@, qr/Usage: Net::ZooKeeper::get_acl\(zkh, path, \.\.\.\)/, - 'get_acl(): no path specified'); - -eval { - $zkh->get_acl($node_path, 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'get_acl(): invalid number of arguments'); - -eval { - $zkh->get_acl($node_path, 'stat', 'bar'); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'get_acl(): invalid stat hash reference'); - -eval { - $zkh->get_acl($node_path, 'stat', []); -}; -like($@, qr/stat is not a hash reference of type Net::ZooKeeper::Stat/, - 'get_acl(): invalid stat hash reference'); - -eval { - $bad_zkh->get_acl($node_path); -}; -like($@, qr/invalid handle/, - 'get_acl(): invalid handle'); - -eval { - Net::ZooKeeper::get_acl(1, $node_path); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'get_acl(): invalid hash reference'); - - -## set_acl() - -eval { - $zkh->set_acl(); -}; -like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/, - 'set_acl(): no path specified'); - -eval { - $zkh->set_acl($node_path); -}; -like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/, - 'set_acl(): no data buffer specified'); - -eval { - $zkh->set_acl($node_path, 'foo'); -}; -like($@, qr/acl_arr is not an array reference/, - 'set_acl(): invalid ACL array reference'); - -eval { - $zkh->set_acl($node_path, {}); -}; -like($@, qr/acl_arr is not an array reference/, - 'set_acl(): invalid ACL array reference to hash'); - -eval { - my @acl = ('foo', 'bar'); - $zkh->set_acl($node_path, \@acl); -}; -like($@, qr/invalid ACL entry hash reference/, - 'set_acl(): invalid ACL entry hash reference'); - -eval { - my @acl = ({ 'foo' => 'bar' }); - $zkh->set_acl($node_path, \@acl); -}; -like($@, qr/no ACL entry perms element/, - 'set_acl(): no ACL entry perms element'); - -eval { - my @acl = ( - { - 'perms' => -1 - } - ); - $zkh->set_acl($node_path, \@acl); -}; -like($@, qr/invalid ACL entry perms/, - 'set_acl(): invalid ACL entry perms'); - -eval { - my @acl = ( - { - 'perms' => ZOO_PERM_ALL - } - ); - $zkh->set_acl($node_path, \@acl); -}; -like($@, qr/no ACL entry scheme element/, - 'set_acl(): no ACL entry scheme element'); - -eval { - my @acl = ( - { - 'perms' => ZOO_PERM_ALL, - 'scheme' => 'foo' - } - ); - $zkh->set_acl($node_path, \@acl); -}; -like($@, qr/no ACL entry id element/, - 'set_acl(): no ACL entry id element'); - -eval { - my @acl = ( - { - 'perms' => ZOO_PERM_ALL, - 'scheme' => 'foo', - 'id' => 'bar' - }, - 'bar' - ); - $zkh->set_acl($node_path, \@acl); -}; -like($@, qr/invalid ACL entry hash reference/, - 'set_acl(): invalid second ACL entry hash reference'); - -eval { - $zkh->set_acl($node_path, [], 'bar'); -}; -like($@, qr/invalid number of arguments/, - 'set_acl(): invalid number of arguments'); - -eval { - $zkh->set_acl($node_path, [], 'version' => -3); -}; -like($@, qr/invalid version requirement/, - 'set_acl(): invalid version requirement'); - -eval { - $bad_zkh->set_acl($node_path, []); -}; -like($@, qr/invalid handle/, - 'set_acl(): invalid handle'); - -eval { - Net::ZooKeeper::set_acl(1, $node_path, []); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'set_acl(): invalid hash reference'); - - -## stat() - -eval { - $zkh->stat('bar'); -}; -like($@, qr/Usage: Net::ZooKeeper::stat\(zkh\)/, - 'stat(): too many arguments'); - -eval { - $bad_zkh->stat(); -}; -like($@, qr/invalid handle/, - 'stat(): invalid handle'); - -eval { - Net::ZooKeeper::stat(1); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'stat(): invalid hash reference'); - -my $stat = $zkh->stat(); -isa_ok($stat, 'Net::ZooKeeper::Stat', - 'stat(): created stat handle'); - - -## stat DESTROY() - -eval { - $stat->DESTROY('foo'); -}; -like($@, qr/Usage: Net::ZooKeeper::Stat::DESTROY\(zksh\)/, - 'stat DESTROY(): too many arguments'); - -my $bad_stat = {}; -$bad_stat = bless($bad_stat, 'Net::ZooKeeper::Stat'); - -$ret = $bad_stat->DESTROY(); -ok(!$ret, - 'stat DESTROY(): no action on invalid handle'); - - -## watch() - -eval { - $zkh->watch('bar'); -}; -like($@, qr/invalid number of arguments/, - 'watch(): invalid number of arguments'); - -eval { - $bad_zkh->watch(); -}; -like($@, qr/invalid handle/, - 'watch(): invalid handle'); - -eval { - Net::ZooKeeper::watch(1); -}; -like($@, qr/zkh is not a hash reference of type Net::ZooKeeper/, - 'watch(): invalid hash reference'); - -my $watch = $zkh->watch(); -isa_ok($watch, 'Net::ZooKeeper::Watch', - 'watch(): created watch handle'); - - -## watch DESTROY() - -eval { - $watch->DESTROY('foo'); -}; -like($@, qr/Usage: Net::ZooKeeper::Watch::DESTROY\(zkwh\)/, - 'watch DESTROY(): too many arguments'); - -my $bad_watch = {}; -$bad_watch = bless($bad_watch, 'Net::ZooKeeper::Watch'); - -$ret = $bad_watch->DESTROY(); -ok(!$ret, - 'watch DESTROY(): no action on invalid handle'); - - -## wait() - -eval { - $watch->wait('bar'); -}; -like($@, qr/invalid number of arguments/, - 'wait(): invalid number of arguments'); - -eval { - $bad_watch->wait(); -}; -like($@, qr/invalid handle/, - 'wait(): invalid watch handle'); - -eval { - Net::ZooKeeper::Watch::wait(1); -}; -like($@, qr/zkwh is not a hash reference of type Net::ZooKeeper::Watch/, - 'wait(): invalid watch hash reference'); - - -## set_log_level() - -eval { - my $f = \&Net::ZooKeeper::set_log_level; - &$f(); -}; -like($@, qr/Usage: Net::ZooKeeper::set_log_level\(level\)/, - 'set_log_level(): no level specified'); - -eval { - my $f = \&Net::ZooKeeper::set_log_level; - &$f(ZOO_LOG_LEVEL_OFF, 'foo'); -}; -like($@, qr/Usage: Net::ZooKeeper::set_log_level\(level\)/, - 'set_log_level(): too many arguments'); - -eval { - Net::ZooKeeper::set_log_level((ZOO_LOG_LEVEL_OFF) - 1); -}; -like($@, qr/invalid log level/, - 'set_log_level(): invalid low log level'); - -eval { - Net::ZooKeeper::set_log_level((ZOO_LOG_LEVEL_DEBUG) + 1); -}; -like($@, qr/invalid log level/, - 'set_log_level(): invalid high log level'); - - -## set_deterministic_conn_order() - -eval { - my $f = \&Net::ZooKeeper::set_deterministic_conn_order; - &$f(); -}; -like($@, qr/Usage: Net::ZooKeeper::set_deterministic_conn_order\(flag\)/, - 'set_deterministic_conn_order(): no flag specified'); - -eval { - my $f = \&Net::ZooKeeper::set_deterministic_conn_order; - &$f(1, 'foo'); -}; -like($@, qr/Usage: Net::ZooKeeper::set_deterministic_conn_order\(flag\)/, - 'set_deterministic_conn_order(): too many arguments'); - diff --git a/src/contrib/zkperl/t/15_thread.t b/src/contrib/zkperl/t/15_thread.t deleted file mode 100644 index 1ef56d0c7c8..00000000000 --- a/src/contrib/zkperl/t/15_thread.t +++ /dev/null @@ -1,121 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use Config; -use File::Spec; -use Test::More; - -BEGIN { - if ($Config{'useithreads'}) { - plan tests => 10; - } - else { - plan skip_all => 'no thread support'; - } -} - -use threads; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -my $zkh = Net::ZooKeeper->new($hosts); - -SKIP: { - skip 'no valid handle', 9 unless (defined($zkh)); - - my($thread) = threads->new(\&thread_test, $zkh); - - SKIP: { - skip 'no valid thread', 3 unless (defined($thread)); - - my(@ret) = $thread->join; - - ok((@ret == 3 and $ret[0]), - 'CLONE_SKIP(): handle reference after spawning thread'); - - ok((@ret == 3 and $ret[1]), - 'CLONE_SKIP(): scalar handle reference after spawning thread'); - - ok((@ret == 3 and $ret[2]), - 'CLONE_SKIP(): undef handle reference after spawning thread'); - } - - my $stat = $zkh->stat(); - - ($thread) = threads->new(\&thread_test, $stat); - - SKIP: { - skip 'no valid thread', 3 unless (defined($thread)); - - my(@ret) = $thread->join; - - ok((@ret == 3 and $ret[0]), - 'stat CLONE_SKIP(): stat handle reference after spawning thread'); - - ok((@ret == 3 and $ret[1]), - 'stat CLONE_SKIP(): scalar stat handle reference after ' . - 'spawning thread'); - - ok((@ret == 3 and $ret[2]), - 'stat CLONE_SKIP(): undef stat handle reference after ' . - 'spawning thread'); - } - - my $watch = $zkh->watch(); - - ($thread) = threads->new(\&thread_test, $watch); - - SKIP: { - skip 'no valid thread', 3 unless (defined($thread)); - - my(@ret) = $thread->join; - - ok((@ret == 3 and $ret[0]), - 'watch CLONE_SKIP(): watch handle reference after spawning thread'); - - ok((@ret == 3 and $ret[1]), - 'watch CLONE_SKIP(): scalar watch handle reference after ' . - 'spawning thread'); - - ok((@ret == 3 and $ret[2]), - 'watch CLONE_SKIP(): undef watch handle reference after ' . - 'spawning thread'); - } -} - -sub thread_test -{ - my $zkh = shift; - - my @ret; - - $ret[0] = ref($zkh) ? 1 : 0; - $ret[1] = ($ret[0] and ref($zkh) eq 'SCALAR') ? 1 : 0; - $ret[2] = ($ret[1] and !defined(${$zkh})) ? 1 : 0; - - return @ret; -} - diff --git a/src/contrib/zkperl/t/20_tie.t b/src/contrib/zkperl/t/20_tie.t deleted file mode 100644 index 37e9a4f4e51..00000000000 --- a/src/contrib/zkperl/t/20_tie.t +++ /dev/null @@ -1,353 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 54; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - skip 'no valid handle', 4 unless (defined($zkh)); - - - ## DESTROY() - - my $attr = tied(%{$zkh}); - - my $ret = $attr->DESTROY(); - ok($ret, - 'DESTROY(): destroyed inner hash'); - - $ret = $attr->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on destroyed inner hash'); - - $ret = $zkh->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on handle with destroyed inner hash'); - - undef $zkh; - ok(!defined($zkh), - 'undef: released handle with destroyed inner hash'); -} - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - skip 'no valid handle', 49 unless (defined($zkh)); - - - ## TIEHASH(), UNTIE() - - eval { - tie(%{$zkh}, 'Net::ZooKeeper'); - }; - like($@, qr/tying hashes of class Net::ZooKeeper not supported/, - 'tie(): tying hashes not supported'); - - eval { - Net::ZooKeeper::TIEHASH('Net::ZooKeeper'); - }; - like($@, qr/tying hashes of class Net::ZooKeeper not supported/, - 'TIEHASH(): tying hashes not supported'); - - eval { - untie(%{$zkh}); - }; - like($@, qr/untying hashes of class Net::ZooKeeper not supported/, - 'untie(): untying hashes not supported'); - - my $attr = tied(%{$zkh}); - - eval { - $attr->UNTIE(0); - }; - like($@, qr/untying hashes of class Net::ZooKeeper not supported/, - 'UNTIE(): untying hashes not supported'); - - - ## FIRSTKEY(), NEXTKEY(), SCALAR() - - my $copy_zkh; - { - my %copy_zkh = %{$zkh}; - $copy_zkh = \%copy_zkh; - } - bless($copy_zkh, 'Net::ZooKeeper'); - is(ref($copy_zkh), 'Net::ZooKeeper', - 'FIRSTKEY(), NEXTKEY(): copied dereferenced handle'); - - eval { - my $val = $copy_zkh->FIRSTKEY(); - }; - like($@, qr/invalid handle/, - 'FETCHKEY(): invalid handle'); - - eval { - my $val = $copy_zkh->NEXTKEY('data_read_len'); - }; - like($@, qr/invalid handle/, - 'NEXTKEY(): invalid handle'); - - my @keys = keys(%{$zkh}); - is(scalar(@keys), 7, - 'keys(): count of keys from handle'); - - @keys = keys(%{$copy_zkh}); - is(scalar(@keys), 7, - 'keys(): count of keys from copied dereferenced handle'); - - is($attr->FIRSTKEY(), 'data_read_len', - 'FIRSTKEY(): retrieved first key using inner hash'); - - is($attr->NEXTKEY('session_id'), 'pending_watches', - 'NEXTKEY(): retrieved last key using inner hash'); - - is($attr->NEXTKEY('pending_watches'), undef, - 'NEXTKEY(): undef returned after last key using inner hash'); - - ok(scalar(%{$zkh}), - 'scalar(): true value returned for dereferenced handle'); - - ok($zkh->SCALAR(), - 'SCALAR(): true value returned'); - - - ## FETCH() - - eval { - my $val = $copy_zkh->FETCH('data_read_len'); - }; - like($@, qr/invalid handle/, - 'FETCH(): invalid handle'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - my $val = $zkh->{'foo'}; - ok(!defined($val), - 'FETCH(): undef returned for invalid element'); - - like($msg, qr/invalid element/, - 'FETCH(): invalid element'); - } - - is($zkh->{'data_read_len'}, 1023, - 'FETCH(): default data read length'); - - is($zkh->{'path_read_len'}, 1023, - 'FETCH(): default path read length'); - - is($zkh->{'hosts'}, $hosts, - 'FETCH(): server hosts'); - - is($zkh->{'session_timeout'}, 10000, - 'FETCH(): default session timeout'); - - ok(defined($zkh->{'session_id'}), - 'FETCH(): session ID'); - - SKIP: { - my $zkh = Net::ZooKeeper->new('0.0.0.0:0'); - - skip 'no valid handle with invalid host', 1 unless (defined($zkh)); - - is($zkh->{'session_id'}, '', - 'FETCH(): empty session ID with invalid host'); - } - - is($zkh->{'pending_watches'}, 0, - 'FETCH(): default pending watch list length'); - - is($attr->FETCH('data_read_len'), 1023, - 'FETCH(): default data read length using inner hash'); - - - ## STORE() - - eval { - my $val = $copy_zkh->STORE('data_read_len', 'foo'); - }; - like($@, qr/invalid handle/, - 'STORE(): invalid handle'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->{'foo'} = 'foo'; - like($msg, qr/invalid element/, - 'STORE(): invalid element'); - } - - eval { - $zkh->{'data_read_len'} = -3; - }; - like($@, qr/invalid data read length/, - 'STORE(): invalid data read length'); - - eval { - $zkh->{'path_read_len'} = -3; - }; - like($@, qr/invalid path read length/, - 'STORE(): invalid path read length'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->{'hosts'} = 'foo'; - like($msg, qr/read-only element: hosts/, - 'STORE(): read-only server hosts element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->{'session_timeout'} = 0; - like($msg, qr/read-only element: session_timeout/, - 'STORE(): read-only session timeout element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->{'session_id'} = 'foo'; - like($msg, qr/read-only element: session_id/, - 'STORE(): read-only session ID element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->{'pending_watches'} = 0; - like($msg, qr/read-only element: pending_watches/, - 'STORE(): read-only pending watch list length element'); - } - - $zkh->{'data_read_len'} = 200; - is($zkh->{'data_read_len'}, 200, - 'STORE(): updated data read length'); - - $zkh->{'path_read_len'} = 100; - is($zkh->{'path_read_len'}, 100, - 'STORE(): updated path read length'); - - $attr->STORE('data_read_len', 100); - is($zkh->{'data_read_len'}, 100, - 'STORE(): updated data read length using inner hash'); - - - ## EXISTS() - - eval { - my $val = $copy_zkh->EXISTS('data_read_len'); - }; - like($@, qr/invalid handle/, - 'EXISTS(): invalid handle'); - - ok(!exists($zkh->{'foo'}), - 'exists(): invalid element of handle'); - - ok(exists($zkh->{'data_read_len'}), - 'exists(): data read length'); - - ok(exists($zkh->{'path_read_len'}), - 'exists(): path read length'); - - ok(exists($zkh->{'hosts'}), - 'exists(): server hosts'); - - ok(exists($zkh->{'session_timeout'}), - 'exists(): session timeout'); - - ok(exists($zkh->{'session_id'}), - 'exists(): session ID'); - - ok(exists($zkh->{'pending_watches'}), - 'exists(): pending watch list length'); - - ok($attr->EXISTS('data_read_len'), - 'EXISTS(): data read length using inner hash'); - - - ## DELETE(), CLEAR() - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - delete($zkh->{'data_read_len'}); - like($msg, - qr/deleting elements from hashes of class Net::ZooKeeper not supported/, - 'delete(): deleting hash elements not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->DELETE({'data_read_len'}); - like($msg, - qr/deleting elements from hashes of class Net::ZooKeeper not supported/, - 'DELETE(): deleting hash elements not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - %{$zkh} = (); - like($msg, qr/clearing hashes of class Net::ZooKeeper not supported/, - 'assign: clearing hashes not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $zkh->CLEAR(); - like($msg, qr/clearing hashes of class Net::ZooKeeper not supported/, - 'CLEAR(): clearing hashes not supported'); - } -} - diff --git a/src/contrib/zkperl/t/22_stat_tie.t b/src/contrib/zkperl/t/22_stat_tie.t deleted file mode 100644 index 02e79131de2..00000000000 --- a/src/contrib/zkperl/t/22_stat_tie.t +++ /dev/null @@ -1,438 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 66; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - my $stat = $zkh->stat() if (defined($zkh)); - - skip 'no valid stat handle', 4 unless (defined($stat)); - - - ## DESTROY() - - my $attr = tied(%{$stat}); - - my $ret = $attr->DESTROY(); - ok($ret, - 'stat DESTROY(): destroyed inner stat hash'); - - $ret = $attr->DESTROY(); - ok(!$ret, - 'stat DESTROY(): no action on destroyed inner stat hash'); - - $ret = $stat->DESTROY(); - ok(!$ret, - 'stat DESTROY(): no action on stat handle with destroyed inner hash'); - - undef $stat; - ok(!defined($stat), - 'undef: released stat handle with destroyed inner hash'); -} - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - my $stat = $zkh->stat() if (defined($zkh)); - - skip 'no valid stat handle', 61 unless (defined($stat)); - - - ## TIEHASH(), UNTIE() - - eval { - tie(%{$stat}, 'Net::ZooKeeper::Stat'); - }; - like($@, qr/tying hashes of class Net::ZooKeeper::Stat not supported/, - 'tie(): tying stat hashes not supported'); - - eval { - Net::ZooKeeper::Stat::TIEHASH('Net::ZooKeeper::Stat'); - }; - like($@, qr/tying hashes of class Net::ZooKeeper::Stat not supported/, - 'stat TIEHASH(): tying stat hashes not supported'); - - eval { - untie(%{$stat}); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/, - 'untie(): untying stat hashes not supported'); - - my $attr = tied(%{$stat}); - - eval { - $attr->UNTIE(0); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/, - 'stat UNTIE(): untying stat hashes not supported'); - - - ## FIRSTKEY(), NEXTKEY(), SCALAR() - - my $copy_stat; - { - my %copy_stat = %{$stat}; - $copy_stat = \%copy_stat; - } - bless($copy_stat, 'Net::ZooKeeper::Stat'); - is(ref($copy_stat), 'Net::ZooKeeper::Stat', - 'stat FIRSTKEY(), NEXTKEY(): copied dereferenced stat handle'); - - eval { - my $val = $copy_stat->FIRSTKEY(); - }; - like($@, qr/invalid handle/, - 'stat FETCHKEY(): invalid stat handle'); - - eval { - my $val = $copy_stat->NEXTKEY('czxid'); - }; - like($@, qr/invalid handle/, - 'stat NEXTKEY(): invalid stat handle'); - - my @keys = keys(%{$stat}); - is(scalar(@keys), 11, - 'keys(): count of keys from stat handle'); - - @keys = keys(%{$copy_stat}); - is(scalar(@keys), 11, - 'keys(): count of keys from copied dereferenced stat handle'); - - is($attr->FIRSTKEY(), 'czxid', - 'stat FIRSTKEY(): retrieved first key using inner stat hash'); - - is($attr->NEXTKEY('num_children'), 'children_zxid', - 'stat NEXTKEY(): retrieved last key using inner stat hash'); - - is($attr->NEXTKEY('children_zxid'), undef, - 'NEXTKEY(): undef returned after last key using inner stat hash'); - - ok(scalar(%{$stat}), - 'scalar(): true value returned for dereferenced stat handle'); - - ok($stat->SCALAR(), - 'stat SCALAR(): true value returned'); - - - ## FETCH() - - eval { - my $val = $copy_stat->FETCH('version'); - }; - like($@, qr/invalid handle/, - 'stat FETCH(): invalid stat handle'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - my $val = $stat->{'foo'}; - ok(!defined($val), - 'stat FETCH(): undef returned for invalid element'); - - like($msg, qr/invalid element/, - 'stat FETCH(): invalid element'); - } - - is($stat->{'czxid'}, 0, - 'stat FETCH(): default node creation ZooKeeper transaction ID'); - - is($stat->{'mzxid'}, 0, - 'stat FETCH(): default data last-modified ZooKeeper transaction ID'); - - is($stat->{'ctime'}, 0, - 'stat FETCH(): default node creation time'); - - is($stat->{'mtime'}, 0, - 'stat FETCH(): default data last-modified time'); - - is($stat->{'version'}, 0, - 'stat FETCH(): default data version'); - - is($stat->{'children_version'}, 0, - 'stat FETCH(): default child node list version'); - - is($stat->{'acl_version'}, 0, - 'stat FETCH(): default ACL version'); - - is($stat->{'ephemeral_owner'}, 0, - 'stat FETCH(): ephemeral node owner session ID'); - - is($stat->{'data_len'}, 0, - 'stat FETCH(): default data length'); - - is($stat->{'num_children'}, 0, - 'stat FETCH(): default child node list length'); - - is($stat->{'children_zxid'}, 0, - 'stat FETCH(): default child node list last-modified ' . - 'ZooKeeper transaction ID'); - - is($attr->FETCH('version'), 0, - 'stat FETCH(): default data version using inner stat hash'); - - - ## STORE() - - eval { - my $val = $copy_stat->STORE('version', 'foo'); - }; - like($@, qr/invalid handle/, - 'stat STORE(): invalid stat handle'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'foo'} = 'foo'; - like($msg, qr/invalid element/, - 'stat STORE(): invalid element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'czxid'} = 'foo'; - like($msg, qr/read-only element: czxid/, - 'stat STORE(): read-only node creation ' . - 'ZooKeeper transaction ID element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'mzxid'} = 'foo'; - like($msg, qr/read-only element: mzxid/, - 'stat STORE(): read-only data last-modified ' . - 'ZooKeeper transaction ID element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'ctime'} = 'foo'; - like($msg, qr/read-only element: ctime/, - 'stat STORE(): read-only node creation time element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'mtime'} = 'foo'; - like($msg, qr/read-only element: mtime/, - 'stat STORE(): read-only data last-modified time element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'version'} = 'foo'; - like($msg, qr/read-only element: version/, - 'stat STORE(): read-only data version element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'children_version'} = 'foo'; - like($msg, qr/read-only element: children_version/, - 'stat STORE(): read-only child node list version element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'acl_version'} = 'foo'; - like($msg, qr/read-only element: acl_version/, - 'stat STORE(): read-only ACL version element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'ephemeral_owner'} = 'foo'; - like($msg, qr/read-only element: ephemeral_owner/, - 'stat STORE(): read-only ephemeral node owner ' . - 'session ID element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'data_len'} = 'foo'; - like($msg, qr/read-only element: data_len/, - 'stat STORE(): read-only data length element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'num_children'} = 'foo'; - like($msg, qr/read-only element: num_children/, - 'stat STORE(): read-only child node list length element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->{'children_zxid'} = 'foo'; - like($msg, qr/read-only element: children_zxid/, - 'stat STORE(): read-only child node list last-modified ' . - 'ZooKeeper transaction ID element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $attr->STORE('version', 'foo'); - like($msg, qr/read-only element: version/, - 'stat STORE(): read-only data version element using ' . - 'inner stat hash'); - } - - - ## EXISTS() - - eval { - my $val = $copy_stat->EXISTS('version'); - }; - like($@, qr/invalid handle/, - 'stat EXISTS(): invalid stat handle'); - - ok(!exists($stat->{'foo'}), - 'exists(): invalid element of stat handle'); - - ok(exists($stat->{'czxid'}), - 'exists(): node creation ZooKeeper transaction ID'); - - ok(exists($stat->{'mzxid'}), - 'exists(): data last-modified ZooKeeper transaction ID'); - - ok(exists($stat->{'ctime'}), - 'exists(): node creation time'); - - ok(exists($stat->{'mtime'}), - 'exists(): data last-modified time'); - - ok(exists($stat->{'version'}), - 'exists(): data version'); - - ok(exists($stat->{'children_version'}), - 'exists(): child node list version'); - - ok(exists($stat->{'acl_version'}), - 'exists(): ACL version'); - - ok(exists($stat->{'ephemeral_owner'}), - 'exists(): ephemeral node owner session ID'); - - ok(exists($stat->{'data_len'}), - 'exists(): data length'); - - ok(exists($stat->{'num_children'}), - 'exists(): child node list length'); - - ok(exists($stat->{'children_zxid'}), - 'exists(): child node list last-modified ZooKeeper transaction ID'); - - ok($attr->EXISTS('version'), - 'stat EXISTS(): data version using inner stat hash'); - - - ## DELETE(), CLEAR() - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - delete($stat->{'version'}); - like($msg, - qr/deleting elements from hashes of class Net::ZooKeeper::Stat not supported/, - 'delete(): deleting stat hash elements not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->DELETE({'version'}); - like($msg, - qr/deleting elements from hashes of class Net::ZooKeeper::Stat not supported/, - 'stat DELETE(): deleting stat hash elements not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - %{$stat} = (); - like($msg, qr/clearing hashes of class Net::ZooKeeper::Stat not supported/, - 'assign: clearing stat hashes not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $stat->CLEAR(); - like($msg, qr/clearing hashes of class Net::ZooKeeper::Stat not supported/, - 'stat CLEAR(): clearing stat hashes not supported'); - } -} - diff --git a/src/contrib/zkperl/t/24_watch_tie.t b/src/contrib/zkperl/t/24_watch_tie.t deleted file mode 100644 index e77879e6229..00000000000 --- a/src/contrib/zkperl/t/24_watch_tie.t +++ /dev/null @@ -1,292 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 42; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - my $watch = $zkh->watch() if (defined($zkh)); - - skip 'no valid watch handle', 4 unless (defined($watch)); - - - ## DESTROY() - - my $attr = tied(%{$watch}); - - my $ret = $attr->DESTROY(); - ok($ret, - 'watch DESTROY(): destroyed inner watch hash'); - - $ret = $attr->DESTROY(); - ok(!$ret, - 'watch DESTROY(): no action on destroyed inner watch hash'); - - $ret = $watch->DESTROY(); - ok(!$ret, - 'watch DESTROY(): no action on watch handle with destroyed inner hash'); - - undef $watch; - ok(!defined($watch), - 'undef: released watch handle with destroyed inner hash'); -} - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - my $watch = $zkh->watch() if (defined($zkh)); - - skip 'no valid watch handle', 37 unless (defined($watch)); - - - ## TIEHASH(), UNTIE() - - eval { - tie(%{$watch}, 'Net::ZooKeeper::Watch'); - }; - like($@, qr/tying hashes of class Net::ZooKeeper::Watch not supported/, - 'tie(): tying watch hashes not supported'); - - eval { - Net::ZooKeeper::Watch::TIEHASH('Net::ZooKeeper::Watch'); - }; - like($@, qr/tying hashes of class Net::ZooKeeper::Watch not supported/, - 'watch TIEHASH(): tying watch hashes not supported'); - - eval { - untie(%{$watch}); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/, - 'untie(): untying watch hashes not supported'); - - my $attr = tied(%{$watch}); - - eval { - $attr->UNTIE(0); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/, - 'watch UNTIE(): untying watch hashes not supported'); - - - ## FIRSTKEY(), NEXTKEY(), SCALAR() - - my $copy_watch; - { - my %copy_watch = %{$watch}; - $copy_watch = \%copy_watch; - } - bless($copy_watch, 'Net::ZooKeeper::Watch'); - is(ref($copy_watch), 'Net::ZooKeeper::Watch', - 'watch FIRSTKEY(), NEXTKEY(): copied dereferenced watch handle'); - - eval { - my $val = $copy_watch->FIRSTKEY(); - }; - like($@, qr/invalid handle/, - 'watch FETCHKEY(): invalid watch handle'); - - eval { - my $val = $copy_watch->NEXTKEY('czxid'); - }; - like($@, qr/invalid handle/, - 'watch NEXTKEY(): invalid watch handle'); - - my @keys = keys(%{$watch}); - is(scalar(@keys), 3, - 'keys(): count of keys from watch handle'); - - @keys = keys(%{$copy_watch}); - is(scalar(@keys), 3, - 'keys(): count of keys from copied dereferenced watch handle'); - - is($attr->FIRSTKEY(), 'timeout', - 'watch FIRSTKEY(): retrieved first key using inner watch hash'); - - is($attr->NEXTKEY('event'), 'state', - 'watch NEXTKEY(): retrieved last key using inner watch hash'); - - is($attr->NEXTKEY('state'), undef, - 'NEXTKEY(): undef returned after last key using inner watch hash'); - - ok(scalar(%{$watch}), - 'scalar(): true value returned for dereferenced watch handle'); - - ok($watch->SCALAR(), - 'watch SCALAR(): true value returned'); - - - ## FETCH() - - eval { - my $val = $copy_watch->FETCH('version'); - }; - like($@, qr/invalid handle/, - 'watch FETCH(): invalid watch handle'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - my $val = $watch->{'foo'}; - ok(!defined($val), - 'watch FETCH(): undef returned for invalid element'); - - like($msg, qr/invalid element/, - 'watch FETCH(): invalid element'); - } - - is($watch->{'timeout'}, 60000, - 'watch FETCH(): default timeout'); - - is($watch->{'event'}, 0, - 'watch FETCH(): default event'); - - is($watch->{'state'}, 0, - 'watch FETCH(): default state'); - - is($attr->FETCH('timeout'), 60000, - 'watch FETCH(): default timeout using inner watch hash'); - - - ## STORE() - - eval { - my $val = $copy_watch->STORE('version', 'foo'); - }; - like($@, qr/invalid handle/, - 'watch STORE(): invalid watch handle'); - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $watch->{'foo'} = 'foo'; - like($msg, qr/invalid element/, - 'watch STORE(): invalid element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $watch->{'event'} = 'foo'; - like($msg, qr/read-only element: event/, - 'watch STORE(): read-only event element'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $watch->{'state'} = 'foo'; - like($msg, qr/read-only element: state/, - 'watch STORE(): read-only state element'); - } - - $watch->{'timeout'} = 100; - is($watch->{'timeout'}, 100, - 'watch STORE(): updated timeout'); - - $attr->STORE('timeout', 200); - is($watch->{'timeout'}, 200, - 'watch STORE(): updated timeout using inner hash'); - - - ## EXISTS() - - eval { - my $val = $copy_watch->EXISTS('version'); - }; - like($@, qr/invalid handle/, - 'watch EXISTS(): invalid watch handle'); - - ok(!exists($watch->{'foo'}), - 'exists(): invalid element of watch handle'); - - ok(exists($watch->{'timeout'}), - 'exists(): timeout'); - - ok(exists($watch->{'event'}), - 'exists(): event'); - - ok(exists($watch->{'state'}), - 'exists(): state'); - - ok($attr->EXISTS('timeout'), - 'watch EXISTS(): timeout using inner watch hash'); - - - ## DELETE(), CLEAR() - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - delete($watch->{'version'}); - like($msg, - qr/deleting elements from hashes of class Net::ZooKeeper::Watch not supported/, - 'delete(): deleting watch hash elements not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $watch->DELETE({'version'}); - like($msg, - qr/deleting elements from hashes of class Net::ZooKeeper::Watch not supported/, - 'watch DELETE(): deleting watch hash elements not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - %{$watch} = (); - like($msg, qr/clearing hashes of class Net::ZooKeeper::Watch not supported/, - 'assign: clearing watch hashes not supported'); - } - - { - my $msg; - - $SIG{'__WARN__'} = sub { $msg = $_[0]; }; - - $watch->CLEAR(); - like($msg, qr/clearing hashes of class Net::ZooKeeper::Watch not supported/, - 'watch CLEAR(): clearing watch hashes not supported'); - } -} - diff --git a/src/contrib/zkperl/t/30_connect.t b/src/contrib/zkperl/t/30_connect.t deleted file mode 100644 index c2b68bb4e5f..00000000000 --- a/src/contrib/zkperl/t/30_connect.t +++ /dev/null @@ -1,202 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 29; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -## new(), DESTROY() - -Net::ZooKeeper::set_deterministic_conn_order(1); - -my $zkh = Net::ZooKeeper->new($hosts); -isa_ok($zkh, 'Net::ZooKeeper', - 'new(): created handle'); - -SKIP: { - skip 'no valid handle', 3 unless (defined($zkh)); - - my $ret = $zkh->DESTROY(); - ok($ret, - 'DESTROY(): destroyed handle'); - - $ret = $zkh->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on destroyed handle'); - - undef $zkh; - ok(!defined($zkh), - 'undef: released handle'); -} - -Net::ZooKeeper::set_deterministic_conn_order(0); - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - skip 'no valid handle', 10 unless (defined($zkh)); - - my $copy_zkh = $zkh; - isa_ok($copy_zkh, 'Net::ZooKeeper', - 'assign: copied handle'); - - my $ret = $zkh->exists($root_path); - ok(defined($ret), - 'exists(): no error from original handle'); - - undef $zkh; - ok(!defined($zkh), - 'undef: released original handle'); - - $ret = $copy_zkh->exists($root_path); - ok(defined($ret), - 'exists(): no error from first copy of handle'); - - $zkh = $copy_zkh; - isa_ok($zkh, 'Net::ZooKeeper', - 'assign: re-copied handle'); - - $ret = $copy_zkh->DESTROY(); - ok($ret, - 'DESTROY(): destroyed first copy of handle'); - - eval { - $zkh->exists($root_path); - }; - like($@, qr/invalid handle/, - 'exists(): invalid second copy of handle'); - - undef $copy_zkh; - ok(!defined($copy_zkh), - 'undef: released first copy of handle'); - - $ret = $zkh->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on second copy of destroyed handle'); - - undef $zkh; - ok(!defined($zkh), - 'undef: released second copy of handle'); -} - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - skip 'no valid handle', 6 unless (defined($zkh)); - - my $copy_zkh; - { - my %copy_zkh = %{$zkh}; - $copy_zkh = \%copy_zkh; - } - bless($copy_zkh, 'Net::ZooKeeper'); - isa_ok($copy_zkh, 'Net::ZooKeeper', - 'FIRSTKEY(), NEXTKEY(): copied dereferenced handle'); - - eval { - $copy_zkh->exists($root_path); - }; - like($@, qr/invalid handle/, - 'exists(): invalid copy of dereferenced handle'); - - $ret = $copy_zkh->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on copy of dereferenced handle'); - - undef $copy_zkh; - ok(!defined($copy_zkh), - 'undef: released copy of dereferenced handle'); - - my $ret = $zkh->exists($root_path); - ok(defined($ret), - 'exists(): no error from original handle'); - - undef $zkh; - ok(!defined($zkh), - 'undef: released original handle'); -} - -Net::ZooKeeper::set_deterministic_conn_order(1); - -my $zkh1 = Net::ZooKeeper->new($hosts, 'session_timeout' => 0x3FFF_FFFF); -isa_ok($zkh1, 'Net::ZooKeeper', - 'new(): created handle with maximum session timeout'); - -SKIP: { - my $ret = $zkh1->exists($root_path) if (defined($zkh1)); - - skip 'no connection to ZooKeeper', 7 unless - (defined($ret) and $ret); - - - ## FETCH() of read-only attributes - - ok(($zkh1->{'session_timeout'} > 0 and - $zkh1->{'session_timeout'} <= 0x3FFF_FFFF), - 'FETCH(): session timeout reset after connection'); - - my $session_id1 = $zkh1->{'session_id'}; - ok((length($session_id1) > 0), - 'FETCH(): non-empty session ID after connection'); - - SKIP: { - skip 'no session ID after connection', 1 unless - (length($session_id1) > 0); - - my @nonzero_bytes = grep($_ != 0, unpack('c' x length($session_id1), - $session_id1)); - ok((@nonzero_bytes > 0), - 'FETCH(): non-zero session ID after connection'); - } - - ## NOTE: to test re-connections with saved session IDs we create a second - ## connection with the same ID while the first is still active; - ## this is bad practice in normal usage - - my $zkh2 = Net::ZooKeeper->new($hosts, - 'session_id' => $session_id1, - 'session_timeout' => 20000); - isa_ok($zkh2, 'Net::ZooKeeper', - 'new(): created handle with session ID and valid session timeout'); - - $ret = $zkh2->exists($root_path); - ok($ret, - 'new(): reconnection with session ID'); - - SKIP: { - skip 'no connection to ZooKeeper', 2 unless ($ret); - - is($zkh2->{'session_timeout'}, 20000, - 'FETCH(): session timeout unchanged after connection'); - - my $session_id2 = $zkh2->{'session_id'}; - ok((length($session_id2) == length($session_id1) - and $session_id2 eq $session_id1), - 'FETCH(): reconnect with session ID'); - } -} - diff --git a/src/contrib/zkperl/t/35_log.t b/src/contrib/zkperl/t/35_log.t deleted file mode 100644 index 92821afc1f3..00000000000 --- a/src/contrib/zkperl/t/35_log.t +++ /dev/null @@ -1,88 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 3; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -my $zkh = Net::ZooKeeper->new($hosts); - -Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_INFO); - -SKIP: { - skip 'no valid handle', 2 unless (defined($zkh)); - - SKIP: { - my $dup = 0; - - if (open(OLDERR, '>&', fileno(STDERR))) { - if (close(STDERR) and open(STDERR, '+>', undef)) { - $dup = 1; - - my $old_select = select(STDERR); - $| = 1; - select($old_select); - } - else { - open(STDERR, '>&', fileno(OLDERR)); - close(OLDERR); - } - } - - skip 'no duplicated stderr', 2 unless ($dup); - - SKIP: { - $zkh->exists($root_path); - - sleep(1); - - skip 'no seek on stderr', 1 unless (seek(STDERR, 0, 0)); - - my $log = ; - like($log, qr/ZOO_/, - 'exists(): generated log message'); - } - - SKIP: { - $zkh->DESTROY(); - - sleep(1); - - skip 'no seek on stderr', 1 unless (seek(STDERR, 0, 0)); - - my $log = ; - like($log, qr/ZOO_/, - 'DESTROY(): generated log message'); - } - - open(STDERR, '>&', fileno(OLDERR)); - close(OLDERR); - } -} - -Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_OFF); - diff --git a/src/contrib/zkperl/t/40_basic.t b/src/contrib/zkperl/t/40_basic.t deleted file mode 100644 index 38a8a2138e4..00000000000 --- a/src/contrib/zkperl/t/40_basic.t +++ /dev/null @@ -1,277 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 35; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -my $zkh = Net::ZooKeeper->new($hosts); -my $path; - -SKIP: { - my $ret = $zkh->exists($root_path) if (defined($zkh)); - - skip 'no connection to ZooKeeper', 1 unless - (defined($ret) and $ret); - - $path = $zkh->create($node_path, 'foo', 'acl' => ZOO_OPEN_ACL_UNSAFE); - is($path, $node_path, - 'create(): created node'); -} - -SKIP: { - skip 'no connection to ZooKeeper', 21 unless - (defined($path) and $path eq $node_path); - - - ## exists() - - my $ret = $zkh->exists($node_path); - ok($ret, - 'exists(): checked node existence'); - - $ret = $zkh->exists($node_path . '/NONE'); - ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''), - 'exists(): checked node non-existence'); - - my $stat = $zkh->stat(); - - $ret = $zkh->exists($node_path, 'stat' => $stat); - ok(($ret and $stat->{'data_len'} == 3), - 'exists(): checked node existence with stat handle'); - - - ## get() - - my $node = $zkh->get($node_path); - is($node, 'foo', - 'get(): retrieved node value'); - - $node = $zkh->get($node_path . '/NONE'); - ok((!defined($node) and $zkh->get_error() == ZNONODE and $! eq ''), - 'get(): undef returned for non-extant node'); - - $node = $zkh->get($node_path, 'data_read_len', 2); - is($node, 'fo', - 'get(): retrieved truncated node value'); - - $node = $zkh->get($node_path, 'data_read_len' => 0); - is($node, '', - 'get(): retrieved zero-length node value'); - - $node = $zkh->get($node_path, 'stat' => $stat); - ok(($node eq 'foo' and $stat->{'data_len'} == 3), - 'get(): retrieved node value with stat handle'); - - - ## set() - - $ret = $zkh->set($node_path, 'foo'); - ok($ret, - 'set(): set node value'); - - SKIP: { - my $ret = $zkh->exists($node_path, 'stat' => $stat); - - skip 'invalid node data', 2 unless - ($ret and $stat->{'version'} == 1); - - $ret = $zkh->set($node_path, 'foo', 'version' => $stat->{'version'}); - ok($ret, - 'set(): set node value with matching version'); - - $ret = $zkh->set($node_path, 'foo', 'version' => $stat->{'version'}); - ok((!$ret and $zkh->get_error() == ZBADVERSION and $! eq ''), - 'set(): node value unchanged if non-matching version'); - } - - $ret = $zkh->set($node_path, 'foobaz', 'stat' => $stat); - ok(($ret and $stat->{'data_len'} == 6), - 'set(): retrieved node value with stat handle'); - - - ## create(), delete() - - $path = $zkh->create($node_path, 'foo', 'acl' => ZOO_OPEN_ACL_UNSAFE); - ok((!defined($path) and $zkh->get_error() == ZNODEEXISTS and $! eq ''), - 'create(): undef when attempting to create extant node'); - - $ret = $zkh->delete($node_path . '/NONE'); - ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''), - 'delete(): no deletion of non-extant node'); - - $ret = $zkh->delete($node_path); - ok($ret, - 'delete(): deleted node'); - - my $path_read_len = length($node_path) - 2; - - $path = $zkh->create($node_path, 'foo', - 'path_read_len' => $path_read_len, - 'acl' => ZOO_OPEN_ACL_UNSAFE); - is($path, substr($node_path, 0, -2), - 'create(): created node with small return path buffer'); - - $path = $zkh->create("$node_path/s", 'foo', - 'flags' => ZOO_SEQUENCE, - 'acl' => ZOO_OPEN_ACL_UNSAFE); - like($path, qr/^$node_path\/s[0-9]+$/, - 'create(): created sequential node'); - - SKIP: { - my $ret = $zkh->exists($path, 'stat' => $stat); - - unless ($ret and $stat->{'version'} == 0) { - my $ret = $zkh->delete($path); - diag(sprintf('unable to delete node %s: %d, %s', - $path, $zkh->get_error(), $!)) unless ($ret); - - skip 'invalid node data', 2; - } - - $ret = $zkh->delete($path, 'version' => ($stat->{'version'} + 1)); - ok((!$ret and $zkh->get_error() == ZBADVERSION and $! eq ''), - 'delete(): node not deleted if non-matching version'); - - $ret = $zkh->delete($path, 'version' => $stat->{'version'}); - ok($ret, - 'delete(): deleted sequential node with matching version'); - } - - $path = $zkh->create("$node_path/e", 'foo', - 'flags' => ZOO_EPHEMERAL, - 'acl' => ZOO_OPEN_ACL_UNSAFE); - is($path, "$node_path/e", - 'create(): created ephemeral node'); - - $path = $zkh->create("$node_path/es", 'foo', - 'flags' => (ZOO_SEQUENCE | ZOO_EPHEMERAL), - 'acl' => ZOO_OPEN_ACL_UNSAFE); - like($path, qr/^$node_path\/es[0-9]+$/, - 'create(): created ephemeral sequential node'); - - undef $zkh; -} - -$zkh = Net::ZooKeeper->new($hosts); - -SKIP: { - my $ret = $zkh->exists($node_path) if (defined($zkh)); - - skip 'no connection to ZooKeeper', 12 unless - (defined($ret) and $ret); - - $ret = $zkh->exists("$node_path/e"); - ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''), - 'exists(): checked ephemeral node non-extant after reconnection'); - - $ret = $zkh->exists($path); - ok((!$ret and $zkh->get_error() == ZNONODE and $! eq ''), - 'exists(): checked ephemeral sequential node non-extant ' . - 'after reconnection'); - - - ## get_children() - - my @child_paths = ('abc'); - @child_paths = $zkh->get_children($node_path); - ok((@child_paths == 0 and $zkh->get_error() == ZOK), - 'get_children(): retrieved empty list of child nodes'); - - my $num_children = $zkh->get_children($node_path); - ok((defined($num_children) and $num_children == 0), - 'get_children(): retrieved zero count of child nodes'); - - @child_paths = $zkh->get_children($node_path . '/NONE'); - ok((@child_paths == 0 and $zkh->get_error() == ZNONODE and $! eq ''), - 'get_children(): empty list returned for non-extant node'); - - $num_children = $zkh->get_children($node_path . '/NONE'); - ok((!defined($num_children) and $zkh->get_error() == ZNONODE and $! eq ''), - 'get_children(): undef returned for non-extant node'); - - SKIP: { - my $path = $zkh->create("$node_path/c1", 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE); - - skip 'no connection to ZooKeeper', 6 unless - (defined($path) and $path eq "$node_path/c1"); - - my @child_paths = ('abc'); - @child_paths = $zkh->get_children($node_path); - ok((@child_paths == 1 and $child_paths[0] eq 'c1'), - 'get_children(): retrieved list of single child node'); - - my $num_children = $zkh->get_children($node_path); - ok((defined($num_children) and $num_children == 1), - 'get_children(): retrieved count of single child node'); - - SKIP: { - my $path = $zkh->create("$node_path/c2", 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE); - - skip 'no connection to ZooKeeper', 2 unless - (defined($path) and $path eq "$node_path/c2"); - - my @child_paths = ('abc'); - @child_paths = $zkh->get_children($node_path); - ok((@child_paths == 2 and $child_paths[0] eq 'c1' and - $child_paths[1] eq 'c2'), - 'get_children(): retrieved list of two child nodes'); - - my $num_children = $zkh->get_children($node_path); - ok((defined($num_children) and $num_children == 2), - 'get_children(): retrieved count of two child nodes'); - - my $ret = $zkh->delete("$node_path/c2"); - diag(sprintf('unable to delete node %s: %d, %s', - "$node_path/c2", $zkh->get_error(), $!)) unless - ($ret); - } - - @child_paths = ('abc'); - @child_paths = $zkh->get_children($node_path); - ok((@child_paths == 1 and $child_paths[0] eq 'c1'), - 'get_children(): retrieved list of single child node'); - - $num_children = $zkh->get_children($node_path); - ok((defined($num_children) and $num_children == 1), - 'get_children(): retrieved count of single child node'); - - my $ret = $zkh->delete("$node_path/c1"); - diag(sprintf('unable to delete node %s: %d, %s', - "$node_path/c1", $zkh->get_error(), $!)) unless ($ret); - } - - - ## cleanup - - $ret = $zkh->delete($node_path); - diag(sprintf('unable to delete node %s: %d, %s', - $node_path, $zkh->get_error(), $!)) unless ($ret); -} - diff --git a/src/contrib/zkperl/t/45_class.t b/src/contrib/zkperl/t/45_class.t deleted file mode 100644 index 4aa1a5733bc..00000000000 --- a/src/contrib/zkperl/t/45_class.t +++ /dev/null @@ -1,408 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 47; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - skip 'no valid handle', 15 unless (defined($zkh)); - - my $stat = $zkh->stat(); - my $watch = $zkh->watch(); - - - ## DESTROY() on reblessed handle - - bless($zkh, 'My::ZooKeeper'); - is(ref($zkh), 'My::ZooKeeper', - 'bless(): reblessed handle'); - - eval { - $zkh->EXISTS(); - }; - like($@, qr/Can't locate object method "EXISTS" via package "My::ZooKeeper"/, - 'EXISTS(): not defined on reblessed handle'); - - my $attr = tied(%{$zkh}); - - my $ret = $attr->DESTROY(); - ok($ret, - 'DESTROY(): destroyed inner hash of reblessed handle'); - - $ret = $attr->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on destroyed inner hash of reblessed handle'); - - undef $zkh; - ok(!defined($zkh), - 'undef: released reblessed handle'); - - - ## DESTROY() on reblessed stat handle - - bless($stat, 'My::ZooKeeper::Stat'); - is(ref($stat), 'My::ZooKeeper::Stat', - 'bless(): reblessed stat handle'); - - eval { - $stat->EXISTS(1); - }; - like($@, qr/Can't locate object method "EXISTS" via package "My::ZooKeeper::Stat"/, - 'stat EXISTS(): not defined on reblessed stat handle'); - - $attr = tied(%{$stat}); - - $ret = $attr->DESTROY(); - ok($ret, - 'stat DESTROY(): destroyed inner hash of reblessed stat handle'); - - $ret = $attr->DESTROY(); - ok(!$ret, - 'stat DESTROY(): no action on destroyed inner hash of ' . - 'reblessed stat handle'); - - undef $stat; - ok(!defined($stat), - 'undef: released reblessed stat handle'); - - - ## DESTROY() on reblessed watch handle - - bless($watch, 'My::ZooKeeper::Watch'); - is(ref($watch), 'My::ZooKeeper::Watch', - 'bless(): reblessed watch handle'); - - eval { - $watch->EXISTS(1); - }; - like($@, qr/Can't locate object method "EXISTS" via package "My::ZooKeeper::Watch"/, - 'watch EXISTS(): not defined on reblessed watch handle'); - - $attr = tied(%{$watch}); - - $ret = $attr->DESTROY(); - ok($ret, - 'watch DESTROY(): destroyed inner hash of reblessed watch handle'); - - $ret = $attr->DESTROY(); - ok(!$ret, - 'watch DESTROY(): no action on destroyed inner hash of ' . - 'reblessed watch handle'); - - undef $watch; - ok(!defined($watch), - 'undef: released reblessed watch handle'); -} - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - skip 'no valid handle', 9 unless (defined($zkh)); - - my $stat = $zkh->stat(); - my $watch = $zkh->watch(); - - - ## UNTIE() on reblessed handle - - bless($zkh, 'My::ZooKeeper'); - is(ref($zkh), 'My::ZooKeeper', - 'bless(): reblessed handle'); - - eval { - untie(%{$zkh}); - }; - like($@, qr/untying hashes of class Net::ZooKeeper not supported/, - 'untie(): untying hashes from reblessed handle not supported'); - - my $attr = tied(%{$zkh}); - - eval { - $attr->UNTIE(0); - }; - like($@, qr/untying hashes of class Net::ZooKeeper not supported/, - 'UNTIE(): untying hashes from reblessed handle not supported'); - - - ## UNTIE() on reblessed stat handle - - bless($stat, 'My::ZooKeeper::Stat'); - is(ref($stat), 'My::ZooKeeper::Stat', - 'bless(): reblessed stat handle'); - - eval { - untie(%{$stat}); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/, - 'untie(): untying hashes from reblessed stat handle not supported'); - - $attr = tied(%{$stat}); - - eval { - $attr->UNTIE(0); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Stat not supported/, - 'stat UNTIE(): untying hashes from reblessed stat handle ' . - 'not supported'); - - - ## UNTIE() on reblessed watch handle - - bless($watch, 'My::ZooKeeper::Watch'); - is(ref($watch), 'My::ZooKeeper::Watch', - 'bless(): reblessed watch handle'); - - eval { - untie(%{$watch}); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/, - 'untie(): untying hashes from reblessed watch handle not supported'); - - $attr = tied(%{$watch}); - - eval { - $attr->UNTIE(0); - }; - like($@, qr/untying hashes of class Net::ZooKeeper::Watch not supported/, - 'watch UNTIE(): untying hashes from reblessed watch handle ' . - 'not supported'); -} - - -package Net::ZooKeeper::Test; - -use Net::ZooKeeper qw(:acls); - -our @ISA = qw(Net::ZooKeeper); - -sub create -{ - my($self, $path, $buf) = @_; - - return $self->SUPER::create($path, $buf, - 'path_read_len' => length($path), - 'acl' => ZOO_OPEN_ACL_UNSAFE); -} - -sub get_first_child -{ - my($self, $path) = @_; - - my @child_paths = $self->get_children($path); - - if (@child_paths > 0) { - return $path . (($path =~ /\/$/) ? '' : '/') . $child_paths[0]; - } - - return undef; -} - -sub stat -{ - my $self = shift; - - my $stat = $self->SUPER::stat(); - - return bless($stat, 'Net::ZooKeeper::Test::Stat'); -} - - -sub watch -{ - my $self = shift; - - my $watch = $self->SUPER::watch(); - - return bless($watch, 'Net::ZooKeeper::Test::Watch'); -} - - -package Net::ZooKeeper::Test::Stat; - -our @ISA = qw(Net::ZooKeeper::Stat); - -sub get_ctime -{ - my $self = shift; - - return $self->{'ctime'}; -} - - -package Net::ZooKeeper::Test::Watch; - -our @ISA = qw(Net::ZooKeeper::Watch); - -sub get_timeout -{ - my $self = shift; - - return $self->{'timeout'}; -} - - -package main; - -my $sub_zkh = Net::ZooKeeper::Test->new($hosts); -isa_ok($sub_zkh, 'Net::ZooKeeper::Test', - 'new(): created subclassed handle'); - -SKIP: { - skip 'no valid subclassed handle', 21 unless (defined($sub_zkh)); - - is($sub_zkh->{'data_read_len'}, 1023, - 'FETCH(): default data read length using subclassed handle'); - - my $path; - - SKIP: { - my $ret = $sub_zkh->exists($root_path); - - skip 'no connection to ZooKeeper', 1 unless - (defined($ret) and $ret); - - $path = $sub_zkh->create($node_path, 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE); - is($path, $node_path, - 'create(): created node with subclassed handle'); - } - - SKIP: { - skip 'no connection to ZooKeeper', 1 unless - (defined($path) and $path eq $node_path); - - my $child_path = $sub_zkh->get_first_child($root_path); - is($child_path, $node_path, - 'get_first_child(): retrieved first child with subclassed handle'); - } - - my $sub_stat = $sub_zkh->stat(); - isa_ok($sub_stat, 'Net::ZooKeeper::Test::Stat', - 'stat(): created subclassed stat handle'); - - SKIP: { - skip 'no valid subclassed stat handle', 6 unless - (defined($sub_stat)); - - is($sub_stat->{'ctime'}, 0, - 'stat FETCH(): default ctime using subclassed stat handle'); - - SKIP: { - my $ret = $sub_zkh->exists($node_path, 'stat' => $sub_stat) if - (defined($path) and $path eq $node_path); - - skip 'no connection to ZooKeeper', 2 unless - (defined($ret) and $ret); - - my $ctime = $sub_stat->get_ctime(); - ok($ctime > 0, - 'get_ctime(): retrieved ctime with subclassed stat handle'); - - is($sub_stat->{'ctime'}, $ctime, - 'stat FETCH(): ctime using subclassed stat handle'); - } - - my $ret = $sub_stat->DESTROY(); - ok($ret, - 'stat DESTROY(): destroyed subclassed stat handle'); - - $ret = $sub_stat->DESTROY(); - ok(!$ret, - 'stat DESTROY(): no action on destroyed subclassed stat handle'); - - undef $sub_stat; - ok(!defined($sub_stat), - 'undef: released subclassed stat handle'); - } - - my $sub_watch = $sub_zkh->watch(); - isa_ok($sub_watch, 'Net::ZooKeeper::Test::Watch', - 'watch(): created subclassed watch handle'); - - SKIP: { - skip 'no valid subclassed watch handle', 6 unless - (defined($sub_watch)); - - SKIP: { - my $ret = $sub_zkh->exists($root_path, 'watch' => $sub_watch); - - skip 'no connection to ZooKeeper', 3 unless - (defined($ret) and $ret); - - $sub_watch->{'timeout'} = 50; - - is($sub_watch->get_timeout(), 50, - 'get_timeout(): retrieved timeout with subclassed ' . - 'watch handle'); - - is($sub_watch->{'timeout'}, 50, - 'watch FETCH(): timeout using subclassed stat handle'); - - $ret = $sub_watch->wait(); - ok(!$ret, - 'wait(): watch after checking node existence timed out with ' . - 'subclassed watch handle'); - } - - my $ret = $sub_watch->DESTROY(); - ok($ret, - 'watch DESTROY(): destroyed subclassed watch handle'); - - $ret = $sub_watch->DESTROY(); - ok(!$ret, - 'watch DESTROY(): no action on destroyed subclassed watch handle'); - - undef $sub_watch; - ok(!defined($sub_watch), - 'undef: released subclassed watch handle'); - } - - SKIP: { - skip 'no connection to ZooKeeper', 1 unless - (defined($path) and $path eq $node_path); - - my $ret = $sub_zkh->delete($node_path); - ok($ret, - 'delete(): deleted node with subclassed handle'); - } - - my $ret = $sub_zkh->DESTROY(); - ok($ret, - 'DESTROY(): destroyed subclassed handle'); - - $ret = $sub_zkh->DESTROY(); - ok(!$ret, - 'DESTROY(): no action on destroyed subclassed handle'); - - undef $sub_zkh; - ok(!defined($sub_zkh), - 'undef: released subclassed handle'); -} - diff --git a/src/contrib/zkperl/t/50_access.t b/src/contrib/zkperl/t/50_access.t deleted file mode 100644 index 1610319f898..00000000000 --- a/src/contrib/zkperl/t/50_access.t +++ /dev/null @@ -1,340 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 38; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - -my($username, $password, $digest) = zk_acl_test_setup(); - - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - my $path = $zkh->create($node_path, 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE) if (defined($zkh)); - - skip 'no connection to ZooKeeper', 36 unless - (defined($path) and $path eq $node_path); - - - ## _zk_acl_constant() - - my $no_read_acl = ZOO_OPEN_ACL_UNSAFE; - ok((ref($no_read_acl) eq 'ARRAY' and - @{$no_read_acl} == 1 and - ref($no_read_acl->[0]) eq 'HASH' and - keys(%{$no_read_acl->[0]}) == 3 and - $no_read_acl->[0]->{'perms'} == ZOO_PERM_ALL), - '_zk_acl_constant(): returned default ACL'); - - $no_read_acl->[0]->{'perms'} &= ~ZOO_PERM_READ; - is($no_read_acl->[0]->{'perms'}, ((ZOO_PERM_ALL) & ~ZOO_PERM_READ), - 'assign: altered default ACL'); - - is(ZOO_OPEN_ACL_UNSAFE->[0]->{'perms'}, ZOO_PERM_ALL, - '_zk_acl_constant(): returned unaltered default ACL'); - - my $copy_no_read_acl = $no_read_acl; - is_deeply($copy_no_read_acl, $no_read_acl, - 'assign: copied default ACL'); - - undef $no_read_acl; - ok(!defined($no_read_acl), - 'undef: released original default ACL'); - - is($copy_no_read_acl->[0]->{'perms'}, ((ZOO_PERM_ALL) & ~ZOO_PERM_READ), - 'undef: no change to copied default ACL'); - - $no_read_acl = $copy_no_read_acl; - is_deeply($no_read_acl, $copy_no_read_acl, - 'assign: re-copied default ACL'); - - - ## create() - - my $acl_node_path = "$node_path/a1"; - - $path = $zkh->create($acl_node_path, 'foo', 'acl' => $no_read_acl); - is($path, $acl_node_path, - 'create(): created node with no-read ACL'); - - my $node = $zkh->get($acl_node_path); - - my $skip_acl; - if (defined($node) and $node eq 'foo') { - $skip_acl = 1; - } - elsif(!defined($node) and $zkh->get_error() == ZNOAUTH) { - $skip_acl = 0; - } - else { - $skip_acl = -1; - diag(sprintf('unable to get node with no-read ACL %s: %d, %s', - $acl_node_path, $zkh->get_error(), $!)); - } - - my $ret = $zkh->delete($acl_node_path); - diag(sprintf('unable to delete node with no-read ACL %s: %d, %s', - $acl_node_path, $zkh->get_error(), $!)) unless ($ret); - - my $digest_acl = [ - { - 'perms' => ZOO_PERM_READ, - 'scheme' => 'world', - 'id' => 'anyone' - }, - { - 'perms' => (ZOO_PERM_WRITE | ZOO_PERM_ADMIN), - 'scheme' => 'digest', - 'id' => "$username:$digest" - } - ]; - - $path = $zkh->create($acl_node_path, 'foo', 'acl' => $digest_acl); - is($path, $acl_node_path, - 'create(): created node with digest auth ACL'); - - SKIP: { - skip 'ZooKeeper skipping ACLs', 1 unless (!$skip_acl); - - my $acl_node_path = "$node_path/a2"; - - my $path = $zkh->create($acl_node_path, 'foo', 'acl' => [ - { - 'perms' => ZOO_PERM_WRITE, - 'scheme' => 'foo', - 'id' => 'bar' - } - ]); - ok((!defined($path) and $zkh->get_error() == ZINVALIDACL and $! eq ''), - 'create(): undef when attempting to create node with invalid ACL'); - } - - - ## get_acl() - - my @acl = ('abc'); - @acl = $zkh->get_acl($node_path . '/NONE'); - ok((@acl == 0 and $zkh->get_error() == ZNONODE and $! eq ''), - 'get_acl(): empty list returned for non-extant node'); - - $num_acl_entries = $zkh->get_acl($node_path . '/NONE'); - ok((!defined($num_acl_entries) and $zkh->get_error() == ZNONODE and - $! eq ''), - 'get_acl(): undef returned for non-extant node'); - - @acl = ('abc'); - @acl = $zkh->get_acl($acl_node_path); - is_deeply(\@acl, $digest_acl, - 'get_acl(): retrieved digest ACL'); - - my $stat = $zkh->stat(); - - @acl = ('abc'); - @acl = $zkh->get_acl($node_path, 'stat' => $stat); - is_deeply(\@acl, ZOO_OPEN_ACL_UNSAFE, - 'get_acl(): retrieved ACL'); - - is($stat->{'data_len'}, 3, - 'get_acl(): retrieved ACL with stat handle'); - - SKIP: { - skip 'ZooKeeper not skipping ACLs', 3 unless ($skip_acl > 0); - - my $acl_node_path = "$node_path/a2"; - - my $path = $zkh->create($acl_node_path, 'foo', 'acl' => []); - is($path, $acl_node_path, - 'create(): created node with empty ACL'); - - my @acl = ('abc'); - @acl = $zkh->get_acl($acl_node_path); - ok((@acl == 0 and $zkh->get_error() == ZOK), - 'get_acl(): retrieved empty ACL'); - - my $num_acl_entries = $zkh->get_acl($acl_node_path); - ok((defined($num_acl_entries) and $num_acl_entries == 0), - 'get_acl(): retrieved zero count of ACL entries'); - - my $ret = $zkh->delete($acl_node_path); - diag(sprintf('unable to delete node with empty ACL %s: %d, %s', - $acl_node_path, $zkh->get_error(), $!)) unless ($ret); - } - - - ## set_acl() - - SKIP: { - skip 'ZooKeeper skipping ACLs', 2 unless (!$skip_acl); - - my $ret = $zkh->set_acl($acl_node_path, [ - { - 'perms' => ZOO_PERM_CREATE, - 'scheme' => 'foo', - 'id' => 'bar' - } - ]); - ok((!$ret and $zkh->get_error() == ZINVALIDACL and $! eq ''), - 'set_acl(): invalid ACL'); - - push @{$digest_acl}, { - 'perms' => (ZOO_PERM_CREATE | ZOO_PERM_DELETE), - 'scheme' => 'ip', - 'id' => '0.0.0.0' - }; - - $ret = $zkh->set_acl($acl_node_path, $digest_acl); - ok((!$ret and $zkh->get_error() == ZNOAUTH and $! eq ''), - 'set_acl(): ACL unchanged if no auth'); - } - - - ## add_auth(), set_acl() - - $ret = $zkh->add_auth('digest', ''); - ok($ret, - 'add_auth(): empty digest cert'); - - SKIP: { - skip 'ZooKeeper skipping ACLs', 1 unless (!$skip_acl); - - my $ret = $zkh->set($acl_node_path, 'foo'); - ok((!$ret and $zkh->get_error() == ZNOAUTH and $! eq ''), - 'set(): node value unchanged if no auth'); - } - - $ret = $zkh->add_auth('digest', "$username:$password"); - ok($ret, - 'add_auth(): valid digest cert'); - - SKIP: { - skip 'ZooKeeper skipping ACLs', 13 unless (!$skip_acl); - - my $ret = $zkh->set($acl_node_path, 'baz'); - ok($ret, - 'set(): set node value with auth'); - - my $node = $zkh->get($acl_node_path); - is($node, 'baz', - 'get(): retrieved node value with auth'); - - $ret = $zkh->set_acl($acl_node_path, $digest_acl); - ok($ret, - 'set_acl(): set digest ACL with auth'); - - my $stat = $zkh->stat(); - - my @acl = ('abc'); - @acl = $zkh->get_acl($acl_node_path, 'stat' => $stat); - is_deeply(\@acl, $digest_acl, - 'get_acl(): retrieved digest ACL with auth'); - - is($stat->{'data_len'}, 3, - 'get_acl(): retrieved digest ACL with stat handle and auth'); - - SKIP: { - skip 'invalid node data', 2 unless ($stat->{'version'} == 1); - - my $ret = $zkh->set_acl($acl_node_path, $digest_acl, - 'version' => $stat->{'version'}); - ok($ret, - 'set_acl(): set digest ACL with matching version with auth'); - - $ret = $zkh->set_acl($acl_node_path, $digest_acl, - 'version' => $stat->{'version'}); - ok((!$ret and $zkh->get_error() == ZBADVERSION and $! eq ''), - 'set_acl(): ACL unchanged if non-matching version'); - } - - my $child_node_path = "$acl_node_path/c1"; - - my $path = $zkh->create($child_node_path, 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE); - ok((!defined($path) and $zkh->get_error() == ZNOAUTH and $! eq ''), - 'create(): undef when attempting to create node if no auth'); - - $digest_acl->[1]->{'perms'} |= ZOO_PERM_CREATE; - $digest_acl->[2]->{'perms'} &= ~ZOO_PERM_CREATE; - - $ret = $zkh->set_acl($acl_node_path, $digest_acl); - ok($ret, - 'set_acl(): set changed digest ACL with auth'); - - $path = $zkh->create($child_node_path, 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE); - is($path, $child_node_path, - 'create(): created node with auth'); - - $ret = $zkh->delete($child_node_path); - ok((!$ret and $zkh->get_error() == ZNOAUTH and $! eq ''), - 'delete(): no deletion of node if no auth'); - - $digest_acl->[1]->{'perms'} |= ZOO_PERM_DELETE; - pop @{$digest_acl}; - - $ret = $zkh->set_acl($acl_node_path, $digest_acl); - ok($ret, - 'set_acl(): set reduced digest ACL with auth'); - - $ret = $zkh->delete($child_node_path); - ok($ret, - 'delete(): deleted node with auth'); - } - - - ## cleanup - - $ret = $zkh->delete($acl_node_path); - diag(sprintf('unable to delete node with digest auth ACL %s: %d, %s', - $acl_node_path, $zkh->get_error(), $!)) unless ($ret); - - $ret = $zkh->delete($node_path); - diag(sprintf('unable to delete node %s: %d, %s', - $node_path, $zkh->get_error(), $!)) unless ($ret); -} - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - my $ret = $zkh->exists($root_path) if (defined($zkh)); - - skip 'no connection to ZooKeeper', 1 unless - (defined($ret) and $ret); - - - ## add_auth() - - $ret = $zkh->add_auth('foo', 'bar'); - my $err = $zkh->get_error(); - ok((!$ret and - ($err == ZAUTHFAILED or - $err == ZCONNECTIONLOSS or - $err == ZSESSIONEXPIRED) - and $! eq ''), - 'set_acl(): invalid scheme'); -} - diff --git a/src/contrib/zkperl/t/60_watch.t b/src/contrib/zkperl/t/60_watch.t deleted file mode 100644 index 7d30602d553..00000000000 --- a/src/contrib/zkperl/t/60_watch.t +++ /dev/null @@ -1,304 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use File::Spec; -use Test::More tests => 30; - -BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; - - -my $test_dir; -(undef, $test_dir, undef) = File::Spec->splitpath($0); -require File::Spec->catfile($test_dir, 'util.pl'); - -my($hosts, $root_path, $node_path) = zk_test_setup(0); - - -SKIP: { - my $zkh = Net::ZooKeeper->new($hosts); - - my $path = $zkh->create($node_path, 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE) if (defined($zkh)); - - skip 'no connection to ZooKeeper', 20 unless - (defined($path) and $path eq $node_path); - - - ## exists() - - $zkh->{'watch_timeout'} = 100; - - my $watch = $zkh->watch(); - - my $ret = $zkh->exists($node_path, 'watch' => $watch); - ok($ret, - 'exists(): checked node existence with watch handle'); - - $ret = $watch->wait(); - ok(!$ret, - 'wait(): watch after checking node existence timed out'); - - $ret = $zkh->exists($node_path, 'watch' => $watch); - ok($ret, - 'exists(): checked node existence with renewed watch handle'); - - $ret = $watch->wait(); - ok(!$ret, - 'wait(): watch after checking node existence timed out with ' . - 'renewed watch handle'); - - undef $watch; - ok(!defined($watch), - 'undef: released watch handle'); - - my $pending_watches = $zkh->{'pending_watches'}; - is($pending_watches, 2, - '_zk_release_watches(): report pending watches'); - - - ## get_children() - - $watch = $zkh->watch('timeout' => 50); - - my $num_children = $zkh->get_children($node_path, 'watch' => $watch); - ok((defined($num_children) and $num_children == 0), - 'get_children(): retrieved zero count of child nodes with ' . - 'watch handle'); - - $ret = $watch->wait(); - ok(!$ret, - 'wait(): watch after retrieving child nodes timed out with ' . - 'watch handle'); - - $watch->{'timeout'} = 100; - - my @child_paths = $zkh->get_children($node_path, 'watch' => $watch); - ok((@child_paths == 0), - 'get_children(): retrieved empty list of child nodes with ' . - 'renewed watch handle'); - - $ret = $watch->wait(); - ok(!$ret, - 'wait(): watch after retrieving child nodes timed out with ' . - 'renewed watch handle'); - - $pending_watches = $zkh->{'pending_watches'}; - is($pending_watches, 4, - '_zk_release_watches(): report pending watches'); - - - ## get() - - $watch = $zkh->watch(); - - my $node = $zkh->get($node_path, 'watch' => $watch); - is($node, 'foo', - 'get(): retrieved node value with watch handle'); - - $ret = $watch->wait('timeout' => 0); - ok(!$ret, - 'wait(): watch after retrieving node value timed out with ' . - 'watch handle'); - - $node = $zkh->get($node_path, 'watch' => $watch); - is($node, 'foo', - 'get(): retrieved node value with renewed watch handle'); - - $ret = $watch->wait(); - ok(!$ret, - 'wait(): watch after retrieving node value timed out with ' . - 'renewed watch handle'); - - $pending_watches = $zkh->{'pending_watches'}; - is($pending_watches, 6, - '_zk_release_watches(): all watches pending'); - - - ## _zk_release_watches() - - $ret = $zkh->DESTROY(); - ok($ret, - 'DESTROY(): destroyed handle with pending watches'); - - my $event = $watch->{'event'}; - is($event, 0, - '_zk_release_watches(): watch not destroyed when tied to watch handle'); - - $zkh = Net::ZooKeeper->new($hosts); - - SKIP: { - my $ret = $zkh->exists($node_path, 'watch' => $watch); - - skip 'no connection to ZooKeeper', 2 unless - (defined($ret) and $ret); - - ok($ret, - 'exists(): checked node existence with renewed watch handle ' . - 'from prior connection'); - - $ret = $watch->wait(); - ok(!$ret, - 'wait(): watch after checking node existence timed out with ' . - 'renewed watch handle from prior connection'); - - - } -} - -my $pid = fork(); - -SKIP: { - skip 'unable to fork', 4 unless (defined($pid)); - - my $zkh = Net::ZooKeeper->new($hosts); - - my $ret = $zkh->exists($node_path) if (defined($zkh)); - - if ($pid == 0) { - ## child process - - my $code = 0; - - if (defined($ret) and $ret) { - sleep(1); - - my $ret = $zkh->set($node_path, 'foo'); - - diag(sprintf('set(): failed in child process: %d, %s', - $zkh->get_error(), $!)) unless ($ret); - - $code = !$ret; - - sleep(1); - - my $path = $zkh->create("$node_path/c", 'foo', - 'acl' => ZOO_OPEN_ACL_UNSAFE); - - diag(sprintf('create(): failed in child process: %d, %s', - $zkh->get_error(), $!)) unless - (defined($path) and $path eq "$node_path/c"); - - $code &= !$ret; - - sleep(1); - - $ret = $zkh->delete("$node_path/c"); - - diag(sprintf('delete(): failed in child process: %d, %s', - $zkh->get_error(), $!)) unless ($ret); - - $code &= !$ret; - - sleep(1); - - $ret = $zkh->set($node_path, 'foo'); - - diag(sprintf('set(): failed in child process: %d, %s', - $zkh->get_error(), $!)) unless ($ret); - - $code &= !$ret; - } - - exit($code); - } - else { - ## parent process - - SKIP: { - skip 'no connection to ZooKeeper', 9 unless - (defined($ret) and $ret); - - my $watch = $zkh->watch('timeout' => 5000); - - - ## wait() - - my $ret = $zkh->exists($node_path, 'watch' => $watch); - ok($ret, - 'exists(): checked node existence with watch handle ' . - 'in parent'); - - $ret = $watch->wait(); - ok(($ret and $watch->{'event'} == ZOO_CHANGED_EVENT and - $watch->{'state'} == ZOO_CONNECTED_STATE), - 'wait(): waited for event after checking node existence'); - - my $num_children = $zkh->get_children($node_path, - 'watch' => $watch); - ok((defined($num_children) and $num_children == 0), - 'get_children(): retrieved zero count of child nodes with ' . - 'watch handle in parent'); - - $ret = $watch->wait(); - ok(($ret and $watch->{'event'} == ZOO_CHILD_EVENT and - $watch->{'state'} == ZOO_CONNECTED_STATE), - 'wait(): waited for create child event after ' . - 'retrieving child nodes'); - - my @child_paths = $zkh->get_children($node_path, - 'watch' => $watch); - ok((@child_paths == 1 and $child_paths[0] eq 'c'), - 'get_children(): retrieved list of child nodes with ' . - 'watch handle in parent'); - - $ret = $watch->wait(); - ok(($ret and $watch->{'event'} == ZOO_CHILD_EVENT and - $watch->{'state'} == ZOO_CONNECTED_STATE), - 'wait(): waited for delete child event after ' . - 'retrieving child nodes'); - - my $node = $zkh->get($node_path, 'watch' => $watch); - is($node, 'foo', - 'get(): retrieved node value with watch handle in parent'); - - $ret = $watch->wait(); - ok(($ret and $watch->{'event'} == ZOO_CHANGED_EVENT and - $watch->{'state'} == ZOO_CONNECTED_STATE), - 'wait(): waited for event after retrieving node value'); - - undef $watch; - - my $pending_watches = $zkh->{'pending_watches'}; - is($pending_watches, 0, - '_zk_release_watches(): no watches pending'); - } - - my $reap = waitpid($pid, 0); - - diag(sprintf('child process failed: exit %d, signal %d%s', - ($? >> 8), ($? & 127), - (($? & 128) ? ', core dump' : ''))) if - ($reap == $pid and $? != 0); - } -} - - -## cleanup - -{ - my $zkh = Net::ZooKeeper->new($hosts); - - my $ret = $zkh->exists($node_path) if (defined($zkh)); - - if (defined($ret) and $ret) { - $ret = $zkh->delete($node_path); - diag(sprintf('unable to delete node %s: %d, %s', - $node_path, $zkh->get_error(), $!)) unless ($ret); - } -} - diff --git a/src/contrib/zkperl/t/util.pl b/src/contrib/zkperl/t/util.pl deleted file mode 100644 index 1ca738d9999..00000000000 --- a/src/contrib/zkperl/t/util.pl +++ /dev/null @@ -1,62 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -sub zk_test_setup -{ - my $verbose = shift; - - $SIG{'PIPE'} = 'IGNORE'; - - my $hosts = $ENV{'ZK_TEST_HOSTS'}; - unless (defined($hosts) and $hosts =~ /\S/) { - $hosts = 'localhost:0'; - diag('no ZooKeeper hostnames specified in ZK_TEST_HOSTS env var, ' . - "using $hosts") if ($verbose); - } - - my $root_path = $ENV{'ZK_TEST_PATH'}; - if (defined($root_path) and $root_path =~ /^\//) { - $root_path =~ s/\/+/\//g; - $root_path =~ s/\/$//; - } - else { - $root_path = '/'; - diag('no ZooKeeper path specified in ZK_TEST_PATH env var, ' . - 'using root path') if ($verbose); - } - - my $node_path = $root_path . (($root_path =~ /\/$/) ? '' : '/') . - '_net_zookeeper_test'; - - return ($hosts, $root_path, $node_path); -} - -sub zk_acl_test_setup -{ - my $username = '_net_zookeeper_test'; - - my $password = 'test'; - - ## digest is Base64-encoded SHA1 digest of username:password - my $digest = '2qi7Erp2cXYLGcQbXADiwUFaOGo='; - - return ($username, $password, $digest); -} - -1; - diff --git a/src/contrib/zkperl/typemap b/src/contrib/zkperl/typemap deleted file mode 100644 index 84636fdfe3a..00000000000 --- a/src/contrib/zkperl/typemap +++ /dev/null @@ -1,38 +0,0 @@ -# Net::ZooKeeper - Perl extension for Apache ZooKeeper -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -TYPEMAP -Net::ZooKeeper T_ZK_HANDLE -Net::ZooKeeper::Stat T_ZK_HANDLE -Net::ZooKeeper::Watch T_ZK_HANDLE - -INPUT -T_ZK_HANDLE - if (SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVHV && - sv_derived_from($arg, \"${ntype}\")) { - $var = (HV*) SvRV($arg); - } - else { - Perl_croak(aTHX_ - \"$var is not a hash reference of type ${ntype}\"); - } - -OUTPUT -T_ZK_HANDLE - NOT_IMPLEMENTED - diff --git a/src/contrib/zkpython/README b/src/contrib/zkpython/README deleted file mode 100644 index 89d99989756..00000000000 --- a/src/contrib/zkpython/README +++ /dev/null @@ -1,109 +0,0 @@ -Early version of ZooKeeper bindings for Python. All functions are imported as methods into the zookeeper module. - -Please do not rely on APIs staying constant in the short term. The handling of exceptions and failure modes is one area that is subject to change. - -DEPENDENCIES: -------------- - -This has only been tested against SVN (i.e. 3.2.0 in development) but should work against 3.1.1. - -You will need the Python development headers installed to build the module - on many package-management systems, these can be found in python-devel. - -Python >= 2.6 is required. We have tested against 2.6. We have not tested against 3.x. - -BUILD AND INSTALL: -------------------- - -To install, make sure that the C client has been built and that the libraries are installed in /usr/local/lib (or change this directory in setup.py). Then run: - -ant install - -from zookeeper/src/contrib/zkpython/. - -To test, run ant test from the same directory. - -You can compile the module without installing by running - -ant compile - -In order to use the module, zookeeper.so must be in your PYTHONPATH or in one of the directories referenced by sys.path. Running ant install should make sure that this is the case, but if you only run ant compile you probably need to add build/contrib/zkpython/* to PYTHONPATH to find the module. The C client libraries must be in a system library path, or LD_LIBRARY_PATH or DYLD_LIBRARY_PATH (Mac OS) for the module to work correctly, otherwise you will see a library not found error when trying to import the module. - -NAMING CONVENTIONS: --------------------- - -All methods that in the C library are zoo_fn_name have been implemented as zookeeper.fn_name. The exception is any function that has a watch function argument is named without the 'w' prefix (for example, zoo_wexists becomes zookeeper.exists). The variants of these functions without the watch argument (i.e. zoo_exists) have not been implemented on the understanding that they are superseded by the zoo_w* API. - -Enums and integer constants that begin ZOO_int_name are named as zookeeper.int_name. - -PARAMETER CHANGES: ------------------- - -Zookeeper handles are represented as integers to avoid marshalling the entire structure for every call. Therefore they are opaque from Python. - -Any parameter that is used to provide arguments to callback methods is not exposed in the API. Python provides better mechanisms for providing a closure to be called in the future. - -Every callback gets passed the handle of the ZooKeeper instance used to register the callback. - -DATA TYPES: ------------ - -ACL_vectors are lists of dictionaries. Stat structures are dictionaries. String_vectors are lists of strings. - -EXCEPTIONS AND ERROR HANDLING: ------------------------------- - -Currently synchronous calls indicate failure by throwing an exception (note that this includes the synchronous calls to set up asynchronous completion callbacks!). Success is returned as an integer. - -Callbacks signify failure by having the integer response code passed in. - -WHAT'S NEW IN 0.4: ------------------- - -More test coverage. - -Better reference counting, fixing at least two serious bugs. - -Out-of-range zhandles are now checked, fixing a potential security hole. - -Docstrings! Editing and cleanup required, but most of the text is there. - -zookeeper.set_watcher is now implemented correctly. - -zookeeper.client_id is now implemented correctly. zookeeper.init now respects the client_id parameter. - -get_context and set_context have been removed from the API. The context mechanism is used by PyZK to store the callables that are dispatched by C-side watchers. Messing with this from Python-side causes bugs very quickly. You should wrap all desired context up in a callable and then use zookeeper.set_watcher to attach it to the global watcher. - -Many methods now have optional parameters (usually if you can specify a watch, it's optional). The only time where genuinely optional parameters are still mandatory is when a required parameters comes after it. Currently we still respect the ZK C client parameter ordering. For example, you can simply connect with zookeeper.init("host:port") and ignore the other three parameters. - - -WHAT'S NEW IN 0.3: ------------------- - -Some tests in zkpython/test. More to follow! - -A variety of bugfixes. - -Changed the way methods return results - all responses are integers now, for the client to convert to a string if it needs. - -WHAT'S NEW IN 0.2: ------------------- - -The asynchronous API is now implemented (see zookeeper.a*). - -Most enums defined in zookeeper.h are now added as constants to the module. - -_set2 and a few other edge API calls have been implemented. The module is now nearly 100% feature complete! - -A reference count error was tracked down and killed. More probably lurk in there! - -WHAT'S NOT DONE / KNOWN ISSUES / FUTURE WORK: ---------------------------------------------- - -1. There may well be more memory leaks / reference count issues; however I am more confident that common paths are relatively safe. -2. There probably needs to be a more Pythonic Python-side wrapper for these functions (e.g. a zookeeper object, the ability to iterate through a tree of zk nodes) -3. Docstrings need a cleanup. -4. The way exceptions and error codes are returned needs looking at. Currently synchronous calls throw exceptions on everything but ZOK return, but asynchronous completions are simply passed the error code. Async. functions should never throw an exception on the C-side as they are practically impossible to catch. For the sync. functions, exceptions seem more reasonable, but some cases are certainly not exceptional. - -Bug reports / comments very welcome! - -Henry Robinson henry@cloudera.com diff --git a/src/contrib/zkpython/build.xml b/src/contrib/zkpython/build.xml deleted file mode 100644 index 9f5edf3537e..00000000000 --- a/src/contrib/zkpython/build.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/zkpython/src/c/pyzk_docstrings.h b/src/contrib/zkpython/src/c/pyzk_docstrings.h deleted file mode 100644 index d2c4d60f6a4..00000000000 --- a/src/contrib/zkpython/src/c/pyzk_docstrings.h +++ /dev/null @@ -1,594 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PYZK_DOCSTRINGS_H -#define PYZK_DOCSTRINGS_H - -const char pyzk_acreate_doc[] = -"Create a node asynchronously.\n" -"\n" -"This method will create a node in ZooKeeper. A node can only be created if\n" -"it does not already exists. The Create Flags affect the creation of nodes.\n" -"If EPHEMERAL flag is set, the node will automatically get removed if the\n" -"client session goes away. If the SEQUENCE flag is set, a unique\n" -"monotonically increasing sequence number is appended to the path name.\n" -"\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: The name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" value: The data to be stored in the node.\n" -" acl: The initial ACL of the node. If None, the ACL of the parent will be\n" -" used.\n" -"\n" -" (Subsequent parameters are optional)\n" -" flags: this parameter can be set to 0 for normal create or an OR\n" -" of the Create Flags\n" -" completion: the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the parent node does not exist.\n" -"NODEEXISTS the node already exists\n" -"NOAUTH the client does not have permission.\n" -"NOCHILDRENFOREPHEMERALS cannot create children of ephemeral nodes.\n" -"\n" -"RETURNS:\n" -"Returns OK on success or throws of the following errcodes on failure:\n" -"EXCEPTIONS:\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -"MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -static const char pyzk_client_id_doc[] = -"Return the client session id, only valid if the connections\n" -" is currently connected (ie. last watcher state is CONNECTED_STATE)"; - -static const char pyzk_state_doc[] = -"Get the state of the zookeeper connection.\n" - "The return value will be one of the State Consts."; - -static const char pyzk_adelete_doc[] = -" Delete a node in zookeeper.\n" -"\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" version: the expected version of the node. The function will fail if the\n" -" actual version of the node does not match the expected version.\n" -" If -1 is used the version check will not take place. \n" -" completion: the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADVERSION expected version does not match actual version.\n" -"NOTEMPTY children are present; node cannot be deleted.\n" -"Returns OK on success or one of the following errcodes on failure:\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -static const char pyzk_aexists_doc[] = -" checks the existence of a node in zookeeper.\n" -"\n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" watch: if not None, a watch will be set at the server to notify the \n" -"client if the node changes. The watch will be set even if the node does not \n" -"exist. This allows clients to watch for nodes to appear.\n" -"\n" -" completion: the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -" OK operation completed successfully\n" -" NONODE the node does not exist.\n" -" NOAUTH the client does not have permission.\n" -" data the data that will be passed to the completion routine when the \n" -"function completes.\n" -" OK on success or one of the following errcodes on failure:\n" -" BADARGUMENTS - invalid input parameters\n" -" INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -" MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -static const char pyzk_aget_doc[] = -"Gets the data associated with a node.\n" -"\n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" watcher if not None, a watch will be set at the server to notify \n" -"the client if the node changes.\n" -" completion: the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -" OK operation completed successfully\n" -" NONODE the node does not exist.\n" -" NOAUTH the client does not have permission.\n" -" data the data that will be passed to the completion routine when \n" -"the function completes.\n" -"Returns OK on success or one of the following errcodes on failure:\n" -" BADARGUMENTS - invalid input parameters\n" -" INVALIDSTATE - zhandle state is either in SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - " MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -static const char pyzk_aset_doc[] = -" Sets the data associated with a node.\n" -"\n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" buffer the buffer holding data to be written to the node.\n" -" buflen the number of bytes from buffer to write.\n" -"\n" -"(Subsequent parameters are optional)\n" -" version the expected version of the node. The function will fail if \n" -"the actual version of the node does not match the expected version. If -1 is \n" -"used the version check will not take place.\n" -"completion: If None, \n" -"the function will execute synchronously. Otherwise, the function will return \n" -"immediately and invoke the completion routine when the request completes.\n" -" completion the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADVERSION expected version does not match actual version.\n" -" data the data that will be passed to the completion routine when \n" -"the function completes.\n" -"Returns OK on success or one of the following errcodes on failure:\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -"MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -static const char pyzk_aget_children_doc[] = -" Lists the children of a node.\n" -"\n" -"This function is similar to zoo_aget_children except it allows one specify \n" -"a watcher object rather than a boolean watch flag.\n" -" \n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" watcher if non-null, a watch will be set at the server to notify \n" -"the client if the node changes.\n" -"\n" -" completion the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"\n" -"Returns OK on success or one of the following errcodes on failure:\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -"MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -static const char pyzk_async_doc[] = -" Flush leader channel.\n" -"\n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes\n" -"separating ancestors of the node.\n" -" completion the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"\n" -"Returns OK on success or one of the following errcodes on failure:\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -const static char pyzk_aget_acl_doc[] = -" Gets the acl associated with a node.\n" -"\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" completion: the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"\n" -"Returns:\n" -" OK on success or one of the following errcodes on failure:\n" -" BADARGUMENTS - invalid input parameters\n" -" INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -" MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -const char pyzk_aset_acl_doc[] = -" Sets the acl associated with a node.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" buffer: the buffer holding the acls to be written to the node.\n" -" completion: the routine to invoke when the request completes. The completion\n" -"will be triggered with one of the following codes passed in as the rc argument:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"INVALIDACL invalid ACL specified\n" -"BADVERSION expected version does not match actual version.\n" -"" -" Returns OK on success or one of the following errcodes on failure:\n" -" BADARGUMENTS - invalid input parameters\n" -" INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -" MARSHALLINGERROR - failed to marshall a request; possibly, out of memory"; - -const char pyzk_zerror_doc[] = -"Returns an error string corresponding to an integer error code.\n" -"\n" -"PARAMETERS:\n" -" err: Error code\n" -"RETURNS:\n" - " string corresponding to the return code\n"; - -const char pyzk_add_auth_doc[] = -" specify application credentials.\n" -"\n" -"The application calls this function to specify its credentials for purposes\n" -"of authentication. The server will use the security provider specified by \n" -"the scheme parameter to authenticate the client connection. If the \n" -"authentication request has failed:\n" -"- the server connection is dropped\n" -"- the watcher is called with the AUTH_FAILED_STATE value as the state \n" -"parameter.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" scheme the id of authentication scheme. Natively supported:\n" -"'digest' password-based authentication\n" -" cert: application credentials. The actual value depends on the scheme.\n" -" completion: the routine to invoke when the request completes. One of \n" -"the following result codes may be passed into the completion callback:\n" -"OK operation completed successfully\n" -"AUTHFAILED authentication failed \n" -"\n" -"RETURNS:\n" -"OK on success or one of the following errcodes on failure:\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" -"MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n" - "SYSTEMERROR - a system error occured\n"; - -const char pyzk_is_unrecoverable_doc[] = -" checks if the current zookeeper connection state can't be recovered.\n" -"\n" -" The application must close the zhandle and try to reconnect.\n" -"\n" -"PARAMETERS:\n" -" zh the zookeeper handle (see zookeeper.init)\n" -"\n" -"RETURNS:\n" - "True if connection is unrecoverable, otherwise False\n"; - -const char pyzk_set_debug_level_doc[] = -"\brief sets the debugging level for the library \n" -"\n" -"PARAMETERS:\n" -" logLevel: One of LOG_LEVEL_ERROR, LOG_LEVEL_WARN, LOG_LEVEL_INFO or LOG_LEVEL_DEBUG\n" -"\n" -"RETURNS:\n" - " None\n"; - -static const char pyzk_set_log_stream_doc[] = -" sets the stream to be used by the library for logging \n" -"\n" -"The zookeeper library uses stderr as its default log stream. Applications\n" -"must make sure the stream is writable. Passing in NULL resets the stream \n" - "to its default value (stderr).\n" -"\n" -"PARAMETERS:\n" -" logStream: a writable file object\n" -"RETURNS:\n" -" None\n"; - -static const char pyzk_deterministic_conn_order_doc[] = -" enable/disable quorum endpoint order randomization\n" -"\n" -"If passed a non-zero value, will make the client connect to quorum peers\n" -"in the order as specified in the zookeeper.init() call.\n" -"A zero value causes zookeeper.init() to permute the peer endpoints\n" -"which is good for more even client connection distribution among the \n" -"quorum peers.\n" -"PARAMETERS:\n" -" yesOrNo\n" -"\n" -"RETURNS:\n" - " None\n"; - -static const char pyzk_create_doc[] = -" create a node synchronously.\n" -"\n" -"This method will create a node in ZooKeeper. A node can only be created if\n" -"it does not already exists. The Create Flags affect the creation of nodes.\n" -"If the EPHEMERAL flag is set, the node will automatically get removed if the\n" -"client session goes away. If the SEQUENCE flag is set, a unique\n" -"monotonically increasing sequence number is appended to the path name.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: The name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" value: The data to be stored in the node.\n" -" acl: The initial ACL of the node. If null, the ACL of the parent will be\n" -" used.\n" -" flags: this parameter can be set to 0 for normal create or an OR\n" -" of the Create Flags\n" -" realpath: the real path that is created (this might be different than the\n" -" path to create because of the SEQUENCE flag.\n" -" the maximum length of real path you would want.\n" -"\n" -"RETURNS:\n" -" The actual znode path that was created (may be different from path due to use of SEQUENTIAL\n" -" flag).\n" -"EXCEPTIONS:\n" -" NONODE the parent node does not exist.\n" -" NODEEXISTS the node already exists\n" -" NOAUTH the client does not have permission.\n" -" NOCHILDRENFOREPHEMERALS cannot create children of ephemeral nodes.\n" -" BADARGUMENTS - invalid input parameters\n" -" INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - " MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -static const char pyzk_delete_doc[] = -" delete a node in zookeeper synchronously.\n" -"\n" -"PARAMETERS:\n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" version: the expected version of the node. The function will fail if the\n" -" actual version of the node does not match the expected version.\n" -" If -1 (the default) is used the version check will not take place. \n" -"\n" -"RETURNS:\n" -"One of the following values is returned.\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADVERSION expected version does not match actual version.\n" -"NOTEMPTY children are present; node cannot be deleted.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -static const char pyzk_exists_doc[] = -" checks the existence of a node in zookeeper synchronously.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(Subsequent parameters are optional)\n" -" watch: if nonzero, a watch will be set at the server to notify the \n" -"client if the node changes. The watch will be set even if the node does not \n" -"exist. This allows clients to watch for nodes to appear.\n" -"\n" -"RETURNS:\n" -" the return stat value of the node.\n" -"EXCEPTIONS:\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - - -static const char pyzk_get_children_doc[] = -" lists the children of a node synchronously.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(subsequent parameters are optional)\n" -" watcher: if non-null, a watch will be set at the server to notify \n" -"the client if the node changes.\n" -"\n" -"RETURNS:\n" -" A list of znode names\n" -"EXCEPTIONS:\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -static const char pyzk_set_doc[] = -"\n" -" sets the data associated with a node. See set2 function if\n" -"you require access to the stat information associated with the znode.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" buffer: the buffer holding data to be written to the node.\n" -"\n" -"(subsequent parameters are optional)\n" -" version: the expected version of the node. The function will fail if \n" -"the actual version of the node does not match the expected version. If -1 is \n" -"used the version check will not take place. \n" -"\n" -"RETURNS:\n" -" the return code for the function call.\n" -"OK operation completed successfully\n" -"EXCEPTIONS:\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADVERSION expected version does not match actual version.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -static const char pyzk_get_acl_doc[] = -" gets the acl associated with a node synchronously.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" acl: the return value of acls on the path.\n" -"RETURNS:" -" returns the stat of the path specified.\n" -"EXCEPTIONS:" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - - -static const char pyzk_set_acl_doc[] = -" sets the acl associated with a node synchronously.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" version: the expected version of the path.\n" -" acl: the acl to be set on the path. \n" -"\n" -"RETURNS:\n" -"OK operation completed successfully\n" -"EXCEPTIONS:\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"INVALIDACL invalid ACL specified\n" -"BADVERSION expected version does not match actual version.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -static const char pyzk_close_doc[] = -" close the zookeeper handle and free up any resources.\n" -"\n" -"After this call, the client session will no longer be valid. The function\n" -"will flush any outstanding send requests before return. As a result it may \n" -"block.\n" -"\n" -"This method should only be called only once on a zookeeper handle. Calling\n" -"twice will cause undefined (and probably undesirable behavior).\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -"RETURNS:\n" -"Regardless of the error code returned, the zhandle \n" -"will be destroyed and all resources freed. \n" -"OK - success\n" -"EXCEPTIONS:\n" -"BADARGUMENTS - invalid input parameters\n" -"MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n" -"OPERATIONTIMEOUT - failed to flush the buffers within the specified timeout.\n" -"CONNECTIONLOSS - a network error occured while attempting to send request to server\n" - "SYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details\n"; - -static const char pyzk_set2_doc[] = -"\n" -" sets the data associated with a node, and returns the associated stat structure.\n" -"\n" -"PARAMETERS:\n" -" zh: the zookeeper handle obtained by a call to zookeeper.init\n" -" path: the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -" buffer: the buffer holding data to be written to the node.\n" -"\n" -"(subsequent parameters are optional)\n" -" version: the expected version of the node. The function will fail if \n" -"the actual version of the node does not match the expected version. If -1 is \n" -"used the version check will not take place. \n" -"\n" -"RETURNS:\n" -" the stat structure for the target znode\n" -"OK operation completed successfully\n" -"EXCEPTIONS:\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADVERSION expected version does not match actual version.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -static const char pyzk_init_doc[] = -"This method creates a new handle and a zookeeper session that corresponds\n" -"to that handle. Session establishment is asynchronous, meaning that the\n" -"session should not be considered established until (and unless) an\n" -"event of state CONNECTED_STATE is received.\n" -"PARAMETERS:\n" -" host: comma separated host:port pairs, each corresponding to a zk\n" -" server. e.g. '127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002'\n" -"\n" -"(subsequent parameters are optional)\n" -" fn: the global watcher callback function. When notifications are\n" -" triggered this function will be invoked.\n" -" recv_timeout: \n" -" (clientid, passwd)\n" -" clientid the id of a previously established session that this\n" -" client will be reconnecting to. Clients can access the session id of an established, valid,\n" -" connection by calling zoo_client_id. If\n" -" the specified clientid has expired, or if the clientid is invalid for \n" -" any reason, the returned zhandle_t will be invalid -- the zhandle_t \n" -" state will indicate the reason for failure (typically\n" -" EXPIRED_SESSION_STATE).\n" -"\n" -"RETURNS:\n" -" an integer handle. If it fails to create \n" -" a new zhandle the function throws an exception.\n"; - -static const char pyzk_get_doc[] = -" gets the data associated with a node synchronously.\n" -"\n" -"\n" -"PARAMETERS:\n" -" zh the zookeeper handle obtained by a call to zookeeper.init\n" -" path the name of the node. Expressed as a file name with slashes \n" -"separating ancestors of the node.\n" -"\n" -"(subsequent parameters are optional)\n" -" watcher if not None, a watch will be set at the server to notify \n" -" the client if the node changes.\n" -" bufferlen: This value defaults to 1024*1024 - 1Mb. This method returns \n" -" the minimum of bufferlen and the true length of the znode's data. \n" -"RETURNS:\n" -" the data associated with the node\n" -"OK operation completed successfully\n" -"NONODE the node does not exist.\n" -"NOAUTH the client does not have permission.\n" -"BADARGUMENTS - invalid input parameters\n" -"INVALIDSTATE - zhandle state is either in SESSION_EXPIRED_STATE or AUTH_FAILED_STATE\n" - "MARSHALLINGERROR - failed to marshall a request; possibly, out of memory\n"; - -#endif diff --git a/src/contrib/zkpython/src/c/zookeeper.c b/src/contrib/zkpython/src/c/zookeeper.c deleted file mode 100644 index dfc3af58762..00000000000 --- a/src/contrib/zkpython/src/c/zookeeper.c +++ /dev/null @@ -1,1603 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -////////////////////////////////////////////// -// EXCEPTIONS -PyObject *ZooKeeperException = NULL; -PyObject *SystemErrorException; -PyObject *RuntimeInconsistencyException; -PyObject *DataInconsistencyException; -PyObject *ConnectionLossException; -PyObject *MarshallingErrorException; -PyObject *UnimplementedException; -PyObject *OperationTimeoutException; -PyObject *BadArgumentsException; -PyObject *InvalidStateException; - -PyObject *ApiErrorException; -PyObject *NoNodeException; -PyObject *NoAuthException; -PyObject *NodeExistsException; -PyObject *BadVersionException; -PyObject *NoChildrenForEphemeralsException; -PyObject *NotEmptyException; -PyObject *SessionExpiredException; -PyObject *SessionMovedException; -PyObject *InvalidCallbackException; -PyObject *InvalidACLException; -PyObject *AuthFailedException; -PyObject *ClosingException; -PyObject *NothingException; - -PyObject *err_to_exception(int errcode) { - switch (errcode) { - case ZSYSTEMERROR: - return SystemErrorException; - case ZRUNTIMEINCONSISTENCY: - return RuntimeInconsistencyException; - case ZDATAINCONSISTENCY: - return DataInconsistencyException; - case ZCONNECTIONLOSS: - return ConnectionLossException; - case ZMARSHALLINGERROR: - return MarshallingErrorException; - case ZUNIMPLEMENTED: - return UnimplementedException; - case ZOPERATIONTIMEOUT: - return OperationTimeoutException; - case ZBADARGUMENTS: - return BadArgumentsException; - case ZAPIERROR: - return ApiErrorException; - case ZNONODE: - return NoNodeException; - case ZNOAUTH: - return NoAuthException; - case ZBADVERSION: - return BadVersionException; - case ZNOCHILDRENFOREPHEMERALS: - return NoChildrenForEphemeralsException; - case ZNODEEXISTS: - return NodeExistsException; - case ZINVALIDACL: - return InvalidACLException; - case ZAUTHFAILED: - return AuthFailedException; - case ZNOTEMPTY: - return NotEmptyException; - case ZSESSIONEXPIRED: - return SessionExpiredException; - case ZINVALIDCALLBACK: - return InvalidCallbackException; - case ZSESSIONMOVED: - return SessionMovedException; - - case ZOK: - default: - return NULL; - } -} - - -#define CHECK_ZHANDLE(z) if ( (z) < 0 || (z) >= num_zhandles) { \ - PyErr_SetString( ZooKeeperException, "zhandle out of range" ); \ -return NULL; \ -} else if ( zhandles[(z)] == NULL ) { \ - PyErr_SetString(ZooKeeperException, "zhandle already freed"); \ - return NULL; \ - } - -/* Contains all the state required for a watcher callback - these are - passed to the *dispatch functions as void*, cast to pywatcher_t and - then their callback member is invoked if not NULL */ -typedef struct { - int zhandle; - PyObject *callback; - int permanent; -}pywatcher_t; - -/* This array exists because we need to ref. count the global watchers - for each connection - but they're inaccessible without pulling in - zk_adaptor.h, which I'm trying to avoid. */ -static pywatcher_t **watchers; - -/* We keep an array of zhandles available for use. When a zhandle is - correctly closed, the C client frees the memory so we set the - zhandles[i] entry to NULL. This entry can then be re-used. */ -static zhandle_t** zhandles = NULL; -static int num_zhandles = 0; -static int max_zhandles = 0; -#define REAL_MAX_ZHANDLES 32768 - -/* -------------------------------------------------------------------------- */ -/* zhandles - unique connection ids - tracking */ -/* -------------------------------------------------------------------------- */ - - -/* Allocates an initial zhandle and watcher array */ -int init_zhandles(int num) { - zhandles = malloc(sizeof(zhandle_t*)*num); - watchers = malloc(sizeof(pywatcher_t*)*num); - if (zhandles == NULL || watchers == NULL) { - return 0; - } - max_zhandles = num; - num_zhandles = 0; - memset(zhandles, 0, sizeof(zhandle_t*)*max_zhandles); - return 1; -} - -/* Note that the following zhandle functions are not thread-safe. The - C-Python runtime does not seem to pre-empt a thread that is in a C - module, so there's no need for synchronisation. */ - -/* Doubles the size of the zhandle / watcher array Returns 0 if the - new array would be >= REAL_MAX_ZHANDLES in size. Called when zhandles - is full. Returns 0 if allocation failed or if max num zhandles - exceeded. */ -int resize_zhandles(void) { - zhandle_t **tmp = zhandles; - pywatcher_t ** wtmp = watchers; - if (max_zhandles >= REAL_MAX_ZHANDLES >> 1) { - return 0; - } - max_zhandles *= 2; - zhandles = malloc(sizeof(zhandle_t*)*max_zhandles); - if (zhandles == NULL) { - PyErr_SetString(PyExc_MemoryError, "malloc for new zhandles failed"); - return 0; - } - memset(zhandles, 0, sizeof(zhandle_t*)*max_zhandles); - memcpy(zhandles, tmp, sizeof(zhandle_t*)*max_zhandles/2); - - watchers = malloc(sizeof(pywatcher_t*)*max_zhandles); - if (watchers == NULL) { - PyErr_SetString(PyExc_MemoryError, "malloc for new watchers failed"); - return 0; - } - memset(watchers, 0, sizeof(pywatcher_t*)*max_zhandles); - memcpy(watchers, wtmp, sizeof(pywatcher_t*)*max_zhandles/2); - - free(wtmp); - free(tmp); - return 1; -} - -/* Find a free zhandle - this iterates through the list of open - zhandles, but we expect it to be infrequently called. There are - optimisations that can be made if this turns out to be problematic. - Returns -1 if no free handle is found - resize_handles() can be - called in that case. */ -unsigned int next_zhandle(void) { - int i = 0; - for (i=0;izhandle = zh; ret->callback = cb; ret->permanent = permanent; - return ret; -} - -/* Releases the reference taken in create_pywatcher to the callback, - then frees the allocated pywatcher_t* */ -void free_pywatcher(pywatcher_t *pw) -{ - if (pw == NULL) { - return; - } - Py_DECREF(pw->callback); - - free(pw); -} - -/* Constructs a new stat object. Returns Py_None if stat == NULL or a - dictionary containing all the stat information otherwise. In either - case, takes a reference to the returned object. */ -PyObject *build_stat( const struct Stat *stat ) -{ - if (stat == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return Py_BuildValue( "{s:K, s:K, s:K, s:K," - "s:i, s:i, s:i, s:K," - "s:i, s:i, s:K}", - "czxid", stat->czxid, - "mzxid", stat->mzxid, - "ctime", stat->ctime, - "mtime", stat->mtime, - "version", stat->version, - "cversion", stat->cversion, - "aversion", stat->aversion, - "ephemeralOwner", stat->ephemeralOwner, - "dataLength", stat->dataLength, - "numChildren", stat->numChildren, - "pzxid", stat->pzxid ); -} - -/* Creates a new list of strings from a String_vector. Returns the - empty list if the String_vector is NULL. Takes a reference to the - returned PyObject and gives that reference to the caller. */ -PyObject *build_string_vector(const struct String_vector *sv) -{ - PyObject *ret; - if (!sv) { - return PyList_New(0); - } - - ret = PyList_New(sv->count); - if (ret) { - int i; - for (i=0;icount;++i) { - PyObject *s = PyString_FromString(sv->data[i]); - if (!s) { - if (ret != Py_None) { - Py_DECREF(ret); - } - ret = NULL; - break; - } - PyList_SetItem(ret, i, s); - } - } - return ret; -} - -/* Returns 1 if the PyObject is a valid representation of an ACL, and - 0 otherwise. */ -int check_is_acl(PyObject *o) { - int i; - PyObject *entry; - if (o == NULL) { - return 0; - } - if (!PyList_Check(o)) { - return 0; - } - for (i=0;icount); - int i; - for (i=0;icount;++i) { - PyObject *acl = Py_BuildValue( "{s:i, s:s, s:s}", - "perms", acls->data[i].perms, - "scheme", acls->data[i].id.scheme, - "id", acls->data[i].id.id ); - PyList_SetItem(ret, i, acl); - } - return ret; -} - -/* Parse the Python representation of an ACL list into an ACL_vector - (which needs subsequent freeing) */ -int parse_acls(struct ACL_vector *acls, PyObject *pyacls) -{ - PyObject *a; - int i; - if (acls == NULL || pyacls == NULL) { - PyErr_SetString(PyExc_ValueError, "acls or pyacls NULL in parse_acls"); - return 0; - } - - acls->count = PyList_Size( pyacls ); - - // Is this a list? If not, we can't do anything - if (PyList_Check(pyacls) == 0) { - PyErr_SetString(InvalidACLException, "List of ACLs required in parse_acls"); - return 0; - } - - acls->data = (struct ACL *)calloc(acls->count, sizeof(struct ACL)); - if (acls->data == NULL) { - PyErr_SetString(PyExc_MemoryError, "calloc failed in parse_acls"); - return 0; - } - - for (i=0;icount;++i) { - a = PyList_GetItem(pyacls, i); - // a is now a dictionary - PyObject *perms = PyDict_GetItemString( a, "perms" ); - acls->data[i].perms = (int32_t)(PyInt_AsLong(perms)); - acls->data[i].id.id = strdup( PyString_AsString( PyDict_GetItemString( a, "id" ) ) ); - acls->data[i].id.scheme = strdup( PyString_AsString( PyDict_GetItemString( a, "scheme" ) ) ); - } - return 1; -} - -/* Deallocates the memory allocated inside an ACL_vector, but not the - ACL_vector itself */ -void free_acls( struct ACL_vector *acls ) -{ - if (acls == NULL) { - return; - } - int i; - for (i=0;icount;++i) { - free(acls->data[i].id.id); - free(acls->data[i].id.scheme); - } - free(acls->data); -} - -/* -------------------------------------------------------------------------- */ -/* Watcher and callback implementation */ -/* -------------------------------------------------------------------------- */ - -/* Every watcher invocation goes through this dispatch point, which - a) acquires the global interpreter lock - - b) unpacks the PyObject to call from the passed context pointer, - which handily includes the index of the relevant zookeeper handle - to pass back to Python. - - c) Makes the call into Python, checking for error conditions which - we are responsible for detecting and doing something about (we just - print the error and plough right on) - - d) releases the lock after freeing up the context object, which is - only used for one watch invocation (watches are one-shot, unless - 'permanent' != 0) -*/ -void watcher_dispatch(zhandle_t *zzh, int type, int state, - const char *path, void *context) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)context; - PyObject *callback = pyw->callback; - if (callback == NULL) { - // This is unexpected - char msg[256]; - sprintf(msg, "pywatcher: %d %p %d", pyw->zhandle, pyw->callback, pyw->permanent); - PyErr_SetString(PyExc_ValueError, msg); - return; - } - - gstate = PyGILState_Ensure(); - PyObject *arglist = Py_BuildValue("(i,i,i,s)", pyw->zhandle,type, state, path); - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) { - PyErr_Print(); - } - if (pyw->permanent == 0) { - free_pywatcher(pyw); - } - PyGILState_Release(gstate); -} - -/* The completion callbacks (from asynchronous calls) are implemented similarly */ - -/* Called when an asynchronous call that returns void completes and - dispatches user provided callback */ -void void_completion_dispatch(int rc, const void *data) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) - return; - PyObject *callback = pyw->callback; - gstate = PyGILState_Ensure(); - PyObject *arglist = Py_BuildValue("(i,i)", pyw->zhandle, rc); - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) - PyErr_Print(); - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -/* Called when an asynchronous call that returns a stat structure - completes and dispatches user provided callback */ -void stat_completion_dispatch(int rc, const struct Stat *stat, const void *data) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) - return; - PyObject *callback = pyw->callback; - gstate = PyGILState_Ensure(); - PyObject *pystat = build_stat(stat); - PyObject *arglist = Py_BuildValue("(i,i,O)", pyw->zhandle,rc, pystat); - Py_DECREF(pystat); - - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) - PyErr_Print(); - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -/* Called when an asynchronous call that returns a stat structure and - some untyped data completes and dispatches user provided - callback (used by aget) */ -void data_completion_dispatch(int rc, const char *value, int value_len, const struct Stat *stat, const void *data) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) - return; - PyObject *callback = pyw->callback; - gstate = PyGILState_Ensure(); - PyObject *pystat = build_stat(stat); - PyObject *arglist = Py_BuildValue("(i,i,s#,O)", pyw->zhandle,rc, value,value_len, pystat); - Py_DECREF(pystat); - - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) - PyErr_Print(); - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -/* Called when an asynchronous call that returns a list of strings - completes and dispatches user provided callback */ -void strings_completion_dispatch(int rc, const struct String_vector *strings, const void *data) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) - return; - PyObject *callback = pyw->callback; - gstate = PyGILState_Ensure(); - PyObject *pystrings = build_string_vector(strings); - if (pystrings) - { - PyObject *arglist = Py_BuildValue("(i,i,O)", pyw->zhandle, rc, pystrings); - if (arglist == NULL || PyObject_CallObject((PyObject*)callback, arglist) == NULL) - PyErr_Print(); - } - else - PyErr_Print(); - Py_DECREF(pystrings); - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -/* Called when an asynchronous call that returns a single string - completes and dispatches user provided callback */ -void string_completion_dispatch(int rc, const char *value, const void *data) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) { - return; - } - PyObject *callback = pyw->callback; - gstate = PyGILState_Ensure(); - PyObject *arglist = Py_BuildValue("(i,i,s)", pyw->zhandle,rc, value); - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) - PyErr_Print(); - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -/* Called when an asynchronous call that returns a list of ACLs - completes and dispatches user provided callback */ -void acl_completion_dispatch(int rc, struct ACL_vector *acl, struct Stat *stat, const void *data) -{ - PyGILState_STATE gstate; - pywatcher_t *pyw = (pywatcher_t*)data; - if (pyw == NULL) { - return; - } - PyObject *callback = pyw->callback; - gstate = PyGILState_Ensure(); - PyObject *pystat = build_stat(stat); - PyObject *pyacls = build_acls(acl); - PyObject *arglist = Py_BuildValue("(i,i,O,O)", pyw->zhandle,rc, pyacls, pystat); - - Py_DECREF(pystat); - Py_DECREF(pyacls); - - if (PyObject_CallObject((PyObject*)callback, arglist) == NULL) { - PyErr_Print(); - } - free_pywatcher(pyw); - PyGILState_Release(gstate); -} - -/* -------------------------------------------------------------------------- */ -/* ZOOKEEPER API IMPLEMENTATION */ -/* -------------------------------------------------------------------------- */ - -static PyObject *pyzookeeper_init(PyObject *self, PyObject *args) -{ - const char *host; - PyObject *watcherfn = Py_None; - int recv_timeout = 10000; - // int clientid = -1; - clientid_t cid; - cid.client_id = -1; - const char *passwd; - int handle = next_zhandle(); - if (handle == -1) { - if (resize_zhandles() == 0) { - return NULL; - } - handle = next_zhandle(); - } - - if (handle == -1) { - PyErr_SetString(ZooKeeperException,"Couldn't find a free zhandle, something is very wrong"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "s|Oi(Ls)", &host, &watcherfn, &recv_timeout, &cid.client_id, &passwd)) - return NULL; - - if (cid.client_id != -1) { - strncpy(cid.passwd, passwd, 16*sizeof(char)); - } - pywatcher_t *pyw = NULL; - if (watcherfn != Py_None) { - pyw = create_pywatcher(handle, watcherfn,1); - if (pyw == NULL) { - return NULL; - } - } - watchers[handle] = pyw; - zhandle_t *zh = zookeeper_init( host, watcherfn != Py_None ? watcher_dispatch : NULL, - recv_timeout, cid.client_id == -1 ? 0 : &cid, - pyw, - 0 ); - - if (zh == NULL) - { - PyErr_SetString( ZooKeeperException, "Could not internally obtain zookeeper handle" ); - return NULL; - } - - zhandles[handle] = zh; - return Py_BuildValue( "i", handle); -} - - -/* -------------------------------------------------------------------------- */ -/* Asynchronous API implementation */ -/* -------------------------------------------------------------------------- */ - -/* Asynchronous node creation, returns integer error code */ -PyObject *pyzoo_acreate(PyObject *self, PyObject *args) -{ - int zkhid; char *path; char *value; int valuelen; - struct ACL_vector acl; int flags = 0; - PyObject *completion_callback = Py_None; - PyObject *pyacls = Py_None; - if (!PyArg_ParseTuple(args, "iss#O|iO", &zkhid, &path, - &value, &valuelen, &pyacls, &flags, - &completion_callback)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - CHECK_ACLS(pyacls); - if (parse_acls(&acl, pyacls) == 0) { - return NULL; - } - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - int err = zoo_acreate( zhandles[zkhid], - path, - value, - valuelen, - pyacls == Py_None ? NULL : &acl, - flags, - string_completion_dispatch, - pyw); - free_acls(&acl); - if (err != ZOK) - { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err); -} - -/* Asynchronous node deletion, returns integer error code */ -PyObject *pyzoo_adelete(PyObject *self, PyObject *args) -{ - int zkhid; char *path; int version = -1; - PyObject *completion_callback = Py_None; - if (!PyArg_ParseTuple(args, "is|iO", &zkhid, &path, &version, &completion_callback)) - return NULL; - CHECK_ZHANDLE(zkhid); - - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - - int err = zoo_adelete( zhandles[zkhid], - path, - version, - void_completion_dispatch, - pyw); - - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err); -} - -/* Asynchronous node existance check, returns integer error code */ -PyObject *pyzoo_aexists(PyObject *self, PyObject *args) -{ - int zkhid; char *path; - PyObject *completion_callback = Py_None; - PyObject *exists_watch = Py_None; - if (!PyArg_ParseTuple(args, "is|OO", &zkhid, &path, - &exists_watch, &completion_callback)) - return NULL; - CHECK_ZHANDLE(zkhid); - void *comp_pyw = NULL; - if (completion_callback != Py_None) { - comp_pyw = create_pywatcher(zkhid, completion_callback, 0); - if (comp_pyw == NULL) { - return NULL; - } - } - void *exist_pyw = NULL; - if (exists_watch != Py_None) { - exist_pyw = create_pywatcher(zkhid, exists_watch, 0); - if (exist_pyw == NULL) { - return NULL; - } - } - - int err = zoo_awexists( zhandles[zkhid], - path, - exists_watch != Py_None ? watcher_dispatch : NULL, - exist_pyw, - stat_completion_dispatch, - comp_pyw); - - if (err != ZOK) - { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err);; -} - -/* Asynchronous node data retrieval, returns integer error code */ -PyObject *pyzoo_aget(PyObject *self, PyObject *args) -{ - int zkhid; char *path; - PyObject *completion_callback = Py_None; - PyObject *get_watch = Py_None; - void *comp_pw = NULL; - void *watch_pw = NULL; - - if (!PyArg_ParseTuple(args, "is|OO", &zkhid, &path, - &get_watch, &completion_callback)) { - return NULL; - } - - CHECK_ZHANDLE(zkhid); - - if (get_watch != Py_None) { - if ((watch_pw = create_pywatcher(zkhid, get_watch, 0)) == NULL) { - return NULL; - } - } - - if (completion_callback != Py_None) { - if ((comp_pw = create_pywatcher(zkhid, completion_callback, 0)) == NULL) { - return NULL; - } - } - - int err = zoo_awget( zhandles[zkhid], - path, - get_watch != Py_None ? watcher_dispatch : NULL, - watch_pw, - data_completion_dispatch, - comp_pw); - - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err); -} - -/* Asynchronous node contents update, returns integer error code */ -PyObject *pyzoo_aset(PyObject *self, PyObject *args) -{ - int zkhid; char *path; char *buffer; int buflen; int version=-1; - PyObject *completion_callback = Py_None; - if (!PyArg_ParseTuple(args, "iss#|iO", &zkhid, &path, &buffer, &buflen, &version, &completion_callback)) - return NULL; - CHECK_ZHANDLE(zkhid); - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - int err = zoo_aset( zhandles[zkhid], - path, - buffer, - buflen, - version, - stat_completion_dispatch, - pyw); - - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err); -} - -/* Asynchronous node child retrieval, returns integer error code */ -PyObject *pyzoo_aget_children(PyObject *self, PyObject *args) -{ - int zkhid; char *path; - PyObject *completion_callback = Py_None; - PyObject *get_watch; - if (!PyArg_ParseTuple(args, "is|OO", &zkhid, &path, - &get_watch, &completion_callback)) - return NULL; - CHECK_ZHANDLE(zkhid); - - void *get_pyw = NULL; - if (get_watch != Py_None) { - get_pyw = create_pywatcher(zkhid, get_watch, 0); - if (get_pyw == NULL) { - return NULL; - } - } - - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - - int err = zoo_awget_children( zhandles[zkhid], - path, - get_watch != Py_None ? watcher_dispatch : NULL, - get_pyw, - strings_completion_dispatch, - pyw); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err);; -} - -/* Asynchronous sync, returns integer error code */ -PyObject *pyzoo_async(PyObject *self, PyObject *args) -{ - int zkhid; char *path; - PyObject *completion_callback = Py_None; - if (!PyArg_ParseTuple(args, "is|O", &zkhid, &path, - &completion_callback)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - - int err = zoo_async( zhandles[zkhid], - path, - string_completion_dispatch, - pyw); - - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err);; -} - -/* Asynchronous node ACL retrieval, returns integer error code */ -PyObject *pyzoo_aget_acl(PyObject *self, PyObject *args) -{ - int zkhid; char *path; - PyObject *completion_callback = Py_None; - if (!PyArg_ParseTuple(args, "is|O", &zkhid, &path, - &completion_callback)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - - int err = zoo_aget_acl( zhandles[zkhid], - path, - acl_completion_dispatch, - pyw); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err);; -} - -/* Asynchronous node ACL update, returns integer error code */ -PyObject *pyzoo_aset_acl(PyObject *self, PyObject *args) -{ - int zkhid; char *path; int version; - PyObject *completion_callback = Py_None, *pyacl; - struct ACL_vector aclv; - if (!PyArg_ParseTuple(args, "isiO|O", &zkhid, &path, &version, - &pyacl, &completion_callback)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - CHECK_ACLS(pyacl); - if (parse_acls(&aclv, pyacl) == 0) { - return NULL; - } - - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - - int err = zoo_aset_acl( zhandles[zkhid], - path, - version, - &aclv, - void_completion_dispatch, - pyw); - free_acls(&aclv); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err);; -} - -/* Asynchronous authorization addition, returns integer error code */ -PyObject *pyzoo_add_auth(PyObject *self, PyObject *args) -{ - int zkhid; - char *scheme, *cert; - int certLen; - PyObject *completion_callback; - - if (!PyArg_ParseTuple(args, "iss#O", &zkhid, &scheme, &cert, &certLen, - &completion_callback)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - - void *pyw = NULL; - if (completion_callback != Py_None) { - pyw = create_pywatcher(zkhid, completion_callback, 0); - if (pyw == NULL) { - return NULL; - } - } - - int err = zoo_add_auth( zhandles[zkhid], - scheme, - cert, - certLen, - void_completion_dispatch, - pyw); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err); -} - -/* -------------------------------------------------------------------------- */ -/* Synchronous API implementation */ -/* -------------------------------------------------------------------------- */ - -/* Synchronous node creation, returns node path string */ -static PyObject *pyzoo_create(PyObject *self, PyObject *args) -{ - char *path; - int zkhid; - char* values; - int valuelen; - PyObject *acl = NULL; - int flags = 0; - char realbuf[256]; - const int maxbuf_len = 256; - if (!PyArg_ParseTuple(args, "iss#O|i",&zkhid, &path, &values, &valuelen,&acl,&flags)) - return NULL; - CHECK_ZHANDLE(zkhid); - struct ACL_vector aclv; - CHECK_ACLS(acl); - if (parse_acls(&aclv,acl) == 0) { - return NULL; - } - zhandle_t *zh = zhandles[zkhid]; - int err = zoo_create(zh, path, values, valuelen, &aclv, flags, realbuf, maxbuf_len); - free_acls(&aclv); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - - return Py_BuildValue("s", realbuf); -} - -/* Synchronous node deletion, returns integer error code */ -static PyObject *pyzoo_delete(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - int version = -1; - if (!PyArg_ParseTuple(args, "is|i",&zkhid,&path,&version)) - return NULL; - CHECK_ZHANDLE(zkhid); - zhandle_t *zh = zhandles[zkhid]; - int err = zoo_delete(zh, path, version); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err); -} - -/* Synchronous node existance check, returns stat if exists, None if - absent */ -static PyObject *pyzoo_exists(PyObject *self, PyObject *args) -{ - int zkhid; char *path; PyObject *watcherfn = Py_None; - struct Stat stat; - if (!PyArg_ParseTuple(args, "is|O", &zkhid, &path, &watcherfn)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - zhandle_t *zh = zhandles[zkhid]; - pywatcher_t *pw = NULL; - void *callback = NULL; - if (watcherfn != Py_None) { - pw = create_pywatcher(zkhid, watcherfn,0); - callback = watcher_dispatch; - if (pw == NULL) { - return NULL; - } - } - int err = zoo_wexists(zh, path, callback, pw, &stat); - if (err != ZOK && err != ZNONODE) { - PyErr_SetString(err_to_exception(err), zerror(err)); - free_pywatcher(pw); - return NULL; - } - if (err == ZNONODE) { - Py_INCREF(Py_None); - return Py_None; // This isn't exceptional - } - return build_stat(&stat); -} - -/* Synchronous node child retrieval, returns list of children's path - as strings */ -static PyObject *pyzoo_get_children(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - PyObject *watcherfn = Py_None; - struct String_vector strings; - if (!PyArg_ParseTuple(args, "is|O", &zkhid, &path, &watcherfn)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - pywatcher_t *pw = NULL; - void *callback = NULL; - if (watcherfn != Py_None) { - pw = create_pywatcher( zkhid, watcherfn, 0 ); - callback = watcher_dispatch; - if (pw == NULL) { - return NULL; - } - } - int err = zoo_wget_children(zhandles[zkhid], path, - callback, - pw, &strings ); - - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - free_pywatcher(pw); - return NULL; - } - - PyObject *ret = build_string_vector(&strings); - deallocate_String_vector(&strings); - return ret; -} - -/* Synchronous node data update, returns integer error code */ -static PyObject *pyzoo_set(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - char *buffer; - int buflen; - int version = -1; - if (!PyArg_ParseTuple(args, "iss#|i", &zkhid, &path, &buffer, &buflen, - &version)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - - int err = zoo_set(zhandles[zkhid], path, buffer, buflen, version); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - - return Py_BuildValue("i", err); -} - -/* Synchronous node data update, returns node's stat data structure */ -static PyObject *pyzoo_set2(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - char *buffer; - int buflen; - int version = -1; - if (!PyArg_ParseTuple(args, "iss#|i", &zkhid, &path, &buffer, &buflen, - &version)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - struct Stat *stat = NULL; - int err = zoo_set2(zhandles[zkhid], path, buffer, buflen, version, stat); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - - return build_stat(stat); -} - -/* As per ZK documentation, datanodes are limited to 1Mb. Why not do a - stat followed by a get, to determine how big the buffer should be? - Because the znode may get updated between calls, so we can't - guarantee a complete get anyhow. */ -#define GET_BUFFER_SIZE 1024*1024 - -/* pyzoo_get has an extra parameter over the java/C equivalents. If - you set the fourth integer parameter buffer_len, we return - min(buffer_len, datalength) bytes. This is set by default to - GET_BUFFER_SIZE */ -static PyObject *pyzoo_get(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - char *buffer; - int buffer_len=GET_BUFFER_SIZE; - struct Stat stat; - PyObject *watcherfn = Py_None; - pywatcher_t *pw = NULL; - if (!PyArg_ParseTuple(args, "is|Oi", &zkhid, &path, &watcherfn, &buffer_len)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - if (watcherfn != Py_None) { - pw = create_pywatcher( zkhid, watcherfn,0 ); - if (pw == NULL) { - return NULL; - } - } - buffer = malloc(sizeof(char)*buffer_len); - if (buffer == NULL) { - free_pywatcher(pw); - PyErr_SetString(PyExc_MemoryError, "buffer could not be allocated in pyzoo_get"); - return NULL; - } - - int err = zoo_wget(zhandles[zkhid], path, - watcherfn != Py_None ? watcher_dispatch : NULL, - pw, buffer, - &buffer_len, &stat); - - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - free_pywatcher(pw); - free(buffer); - return NULL; - } - - PyObject *stat_dict = build_stat( &stat ); - PyObject *ret = Py_BuildValue( "(s#,N)", buffer,buffer_len, stat_dict ); - free(buffer); - - return ret; -} - -/* Synchronous node ACL retrieval, returns list of ACLs */ -PyObject *pyzoo_get_acl(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - struct ACL_vector acl; - struct Stat stat; - if (!PyArg_ParseTuple(args, "is", &zkhid, &path)) - return NULL; - CHECK_ZHANDLE(zkhid); - int err = zoo_get_acl( zhandles[zkhid], path, &acl, &stat ); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - PyObject *pystat = build_stat( &stat ); - PyObject *acls = build_acls( &acl ); - PyObject *ret = Py_BuildValue( "(O,O)", pystat, acls ); - Py_DECREF(pystat); - Py_DECREF(acls); - return ret; -} - -/* Synchronous node ACL update, returns integer error code */ -PyObject *pyzoo_set_acl(PyObject *self, PyObject *args) -{ - int zkhid; - char *path; - int version; - PyObject *pyacls; - struct ACL_vector acl; - if (!PyArg_ParseTuple(args, "isiO", &zkhid, &path, &version, &pyacls)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - if (parse_acls(&acl, pyacls) == 0) { - return NULL; - } - int err = zoo_set_acl(zhandles[zkhid], path, version, &acl ); - free_acls(&acl); - if (err != ZOK) { - PyErr_SetString(err_to_exception(err), zerror(err)); - return NULL; - } - return Py_BuildValue("i", err);; -} - -/* -------------------------------------------------------------------------- */ -/* Session and context methods */ -/* -------------------------------------------------------------------------- */ - -/* Closes a connection, returns integer error code */ -PyObject *pyzoo_close(PyObject *self, PyObject *args) -{ - int zkhid, ret; - if (!PyArg_ParseTuple(args, "i", &zkhid)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - zhandle_t *handle = zhandles[zkhid]; - Py_BEGIN_ALLOW_THREADS - ret = zookeeper_close(handle); - Py_END_ALLOW_THREADS - zhandles[zkhid] = NULL; // The zk C client frees the zhandle - return Py_BuildValue("i", ret); -} - -/* Returns the ID of current client as a tuple (client_id, passwd) */ -PyObject *pyzoo_client_id(PyObject *self, PyObject *args) -{ - int zkhid; - if (!PyArg_ParseTuple(args, "i", &zkhid)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - const clientid_t *cid = zoo_client_id(zhandles[zkhid]); - return Py_BuildValue("(L,s)", cid->client_id, cid->passwd); -} - -/* DO NOT USE - context is used internally. This method is not exposed - in the Python module */ -PyObject *pyzoo_get_context(PyObject *self, PyObject *args) -{ - int zkhid; - if (!PyArg_ParseTuple(args, "i", &zkhid)) - return NULL; - CHECK_ZHANDLE(zkhid); - PyObject *context = NULL; - context = (PyObject*)zoo_get_context(zhandles[zkhid]); - if (context) return context; - Py_INCREF(Py_None); - return Py_None; -} - -/* DO NOT USE - context is used internally. This method is not exposed - in the Python module */ -PyObject *pyzoo_set_context(PyObject *self, PyObject *args) -{ - int zkhid; - PyObject *context; - if (!PyArg_ParseTuple(args, "iO", &zkhid, &context)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - PyObject *py_context = (PyObject*)zoo_get_context(zhandles[zkhid]); - if (py_context != NULL && py_context != Py_None) { - Py_DECREF(py_context); - } - Py_INCREF(context); - zoo_set_context(zhandles[zkhid], (void*)context); - Py_INCREF(Py_None); - return Py_None; -} - - -/* -------------------------------------------------------------------------- */ -/* Miscellaneous methods */ -/* -------------------------------------------------------------------------- */ - -/* Sets the global watcher. Returns None */ -PyObject *pyzoo_set_watcher(PyObject *self, PyObject *args) -{ - int zkhid; - PyObject *watcherfn; - if (!PyArg_ParseTuple(args, "iO", &zkhid, &watcherfn)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - pywatcher_t *pyw = watchers[zkhid]; - if (pyw != NULL) { - free_pywatcher( pyw ); - } - - // Create a *permanent* watcher object, not deallocated when called - pyw = create_pywatcher(zkhid, watcherfn,1); - if (pyw == NULL) { - return NULL; - } - watchers[zkhid] = pyw; - zoo_set_watcher(zhandles[zkhid], watcher_dispatch); - zoo_set_context(zhandles[zkhid], pyw); - Py_INCREF(Py_None); - return Py_None; -} - -/* Returns an integer code representing the current connection - state */ -PyObject *pyzoo_state(PyObject *self, PyObject *args) -{ - int zkhid; - if (!PyArg_ParseTuple(args,"i",&zkhid)) { - return NULL; - } - CHECK_ZHANDLE(zkhid); - int state = zoo_state(zhandles[zkhid]); - return Py_BuildValue("i",state); -} - - -/* Convert an integer error code into a string */ -PyObject *pyzerror(PyObject *self, PyObject *args) -{ - int rc; - if (!PyArg_ParseTuple(args,"i", &rc)) - return NULL; - return Py_BuildValue("s", zerror(rc)); -} - -/* Returns the integer receive timeout for a connection */ -PyObject *pyzoo_recv_timeout(PyObject *self, PyObject *args) -{ - int zkhid; - if (!PyArg_ParseTuple(args,"i",&zkhid)) - return NULL; - CHECK_ZHANDLE(zkhid); - int recv_timeout = zoo_recv_timeout(zhandles[zkhid]); - return Py_BuildValue("i",recv_timeout); -} - -/* Returns True if connection is unrecoverable, False otherwise */ -PyObject *pyis_unrecoverable(PyObject *self, PyObject *args) -{ - int zkhid; - if (!PyArg_ParseTuple(args,"i",&zkhid)) - return NULL; - CHECK_ZHANDLE(zkhid); - int ret = is_unrecoverable(zhandles[zkhid]); - if (ret > 0) - Py_RETURN_TRUE; - Py_RETURN_FALSE; -} - -/* Set the debug level for logging, returns None */ -PyObject *pyzoo_set_debug_level(PyObject *self, PyObject *args) -{ - int loglevel; - if (!PyArg_ParseTuple(args, "i", &loglevel)) - return NULL; - zoo_set_debug_level((ZooLogLevel)loglevel); - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject *log_stream = NULL; - -/* Set the output file-like object for logging output. Returns Py_None */ -PyObject *pyzoo_set_log_stream(PyObject *self, PyObject *args) -{ - PyObject *pystream = NULL; - if (!PyArg_ParseTuple(args,"O",&pystream)) { - PyErr_SetString(PyExc_ValueError, "Must supply a Python object to set_log_stream"); - return NULL; - } - if (!PyFile_Check(pystream)) { - PyErr_SetString(PyExc_ValueError, "Must supply a file object to set_log_stream"); - return NULL; - } - /* Release the previous reference to log_stream that we took */ - if (log_stream != NULL) { - Py_DECREF(log_stream); - } - - log_stream = pystream; - Py_INCREF(log_stream); - zoo_set_log_stream(PyFile_AsFile(log_stream)); - - Py_INCREF(Py_None); - return Py_None; -} - -/* Set the connection order - randomized or in-order. Returns None. */ -PyObject *pyzoo_deterministic_conn_order(PyObject *self, PyObject *args) -{ - int yesOrNo; - if (!PyArg_ParseTuple(args, "i",&yesOrNo)) - return NULL; - zoo_deterministic_conn_order( yesOrNo ); - Py_INCREF(Py_None); - return Py_None; -} - -/* -------------------------------------------------------------------------- */ -/* Module setup */ -/* -------------------------------------------------------------------------- */ - -#include "pyzk_docstrings.h" - -static PyMethodDef ZooKeeperMethods[] = { - {"init", pyzookeeper_init, METH_VARARGS, pyzk_init_doc }, - {"create",pyzoo_create, METH_VARARGS, pyzk_create_doc }, - {"delete",pyzoo_delete, METH_VARARGS, pyzk_delete_doc }, - {"get_children", pyzoo_get_children, METH_VARARGS, pyzk_get_children_doc }, - {"set", pyzoo_set, METH_VARARGS, pyzk_set_doc }, - {"set2", pyzoo_set2, METH_VARARGS, pyzk_set2_doc }, - {"get",pyzoo_get, METH_VARARGS, pyzk_get_doc }, - {"exists",pyzoo_exists, METH_VARARGS, pyzk_exists_doc }, - {"get_acl", pyzoo_get_acl, METH_VARARGS, pyzk_get_acl_doc }, - {"set_acl", pyzoo_set_acl, METH_VARARGS, pyzk_set_acl_doc }, - {"close", pyzoo_close, METH_VARARGS, pyzk_close_doc }, - {"client_id", pyzoo_client_id, METH_VARARGS, pyzk_client_id_doc }, - {"set_watcher", pyzoo_set_watcher, METH_VARARGS }, - {"state", pyzoo_state, METH_VARARGS, pyzk_state_doc }, - {"recv_timeout",pyzoo_recv_timeout, METH_VARARGS }, - {"is_unrecoverable",pyis_unrecoverable, METH_VARARGS, pyzk_is_unrecoverable_doc }, - {"set_debug_level",pyzoo_set_debug_level, METH_VARARGS, pyzk_set_debug_level_doc }, - {"set_log_stream",pyzoo_set_log_stream, METH_VARARGS, pyzk_set_log_stream_doc }, - {"deterministic_conn_order",pyzoo_deterministic_conn_order, METH_VARARGS, pyzk_deterministic_conn_order_doc }, - {"acreate", pyzoo_acreate, METH_VARARGS, pyzk_acreate_doc }, - {"adelete", pyzoo_adelete, METH_VARARGS,pyzk_adelete_doc }, - {"aexists", pyzoo_aexists, METH_VARARGS,pyzk_aexists_doc }, - {"aget", pyzoo_aget, METH_VARARGS, pyzk_aget_doc }, - {"aset", pyzoo_aset, METH_VARARGS, pyzk_aset_doc }, - {"aget_children", pyzoo_aget_children, METH_VARARGS, pyzk_aget_children_doc }, - {"async", pyzoo_async, METH_VARARGS, pyzk_async_doc }, - {"aget_acl", pyzoo_aget_acl, METH_VARARGS, pyzk_aget_acl_doc }, - {"aset_acl", pyzoo_aset_acl, METH_VARARGS, pyzk_aset_acl_doc }, - {"zerror", pyzerror, METH_VARARGS, pyzk_zerror_doc }, - {"add_auth", pyzoo_add_auth, METH_VARARGS, pyzk_add_auth_doc }, - /* DO NOT USE get / set_context. Context is used internally to pass - the python watcher to a dispatch function. If you want context, set - it through set_watcher. */ - // {"get_context", pyzoo_get_context, METH_VARARGS, "" }, - // {"set_context", pyzoo_set_context, METH_VARARGS, "" }, - {NULL, NULL} -}; - -#define ADD_INTCONSTANT(x) PyModule_AddIntConstant(module, #x, ZOO_##x) -#define ADD_INTCONSTANTZ(x) PyModule_AddIntConstant(module, #x, Z##x) - -#define ADD_EXCEPTION(x) x = PyErr_NewException("zookeeper."#x, ZooKeeperException, NULL); \ - Py_INCREF(x); \ - PyModule_AddObject(module, #x, x); - - -PyMODINIT_FUNC initzookeeper(void) { - PyEval_InitThreads(); - PyObject *module = Py_InitModule("zookeeper", ZooKeeperMethods ); - if (init_zhandles(32) == 0) { - return; // TODO: Is there any way to raise an exception here? - } - - ZooKeeperException = PyErr_NewException("zookeeper.ZooKeeperException", - PyExc_Exception, - NULL); - - PyModule_AddObject(module, "ZooKeeperException", ZooKeeperException); - Py_INCREF(ZooKeeperException); - - int size = 10; - char version_str[size]; - snprintf(version_str, size, "%i.%i.%i", ZOO_MAJOR_VERSION, ZOO_MINOR_VERSION, ZOO_PATCH_VERSION); - - PyModule_AddStringConstant(module, "__version__", version_str); - - ADD_INTCONSTANT(PERM_READ); - ADD_INTCONSTANT(PERM_WRITE); - ADD_INTCONSTANT(PERM_CREATE); - ADD_INTCONSTANT(PERM_DELETE); - ADD_INTCONSTANT(PERM_ALL); - ADD_INTCONSTANT(PERM_ADMIN); - - ADD_INTCONSTANT(EPHEMERAL); - ADD_INTCONSTANT(SEQUENCE); - - ADD_INTCONSTANT(EXPIRED_SESSION_STATE); - ADD_INTCONSTANT(AUTH_FAILED_STATE); - ADD_INTCONSTANT(CONNECTING_STATE); - ADD_INTCONSTANT(ASSOCIATING_STATE); - ADD_INTCONSTANT(CONNECTED_STATE); - - ADD_INTCONSTANT(CREATED_EVENT); - ADD_INTCONSTANT(DELETED_EVENT); - ADD_INTCONSTANT(CHANGED_EVENT); - ADD_INTCONSTANT(CHILD_EVENT); - ADD_INTCONSTANT(SESSION_EVENT); - ADD_INTCONSTANT(NOTWATCHING_EVENT); - - ADD_INTCONSTANT(LOG_LEVEL_ERROR); - ADD_INTCONSTANT(LOG_LEVEL_WARN); - ADD_INTCONSTANT(LOG_LEVEL_INFO); - ADD_INTCONSTANT(LOG_LEVEL_DEBUG); - - ADD_INTCONSTANTZ(SYSTEMERROR); - ADD_INTCONSTANTZ(RUNTIMEINCONSISTENCY); - ADD_INTCONSTANTZ(DATAINCONSISTENCY); - ADD_INTCONSTANTZ(CONNECTIONLOSS); - ADD_INTCONSTANTZ(MARSHALLINGERROR); - ADD_INTCONSTANTZ(UNIMPLEMENTED); - ADD_INTCONSTANTZ(OPERATIONTIMEOUT); - ADD_INTCONSTANTZ(BADARGUMENTS); - ADD_INTCONSTANTZ(INVALIDSTATE); - - ADD_EXCEPTION(SystemErrorException); - ADD_EXCEPTION(RuntimeInconsistencyException); - ADD_EXCEPTION(DataInconsistencyException); - ADD_EXCEPTION(ConnectionLossException); - ADD_EXCEPTION(MarshallingErrorException); - ADD_EXCEPTION(UnimplementedException); - ADD_EXCEPTION(OperationTimeoutException); - ADD_EXCEPTION(BadArgumentsException); - ADD_EXCEPTION(InvalidStateException); - - ADD_INTCONSTANTZ(OK); - ADD_INTCONSTANTZ(APIERROR); - ADD_INTCONSTANTZ(NONODE); - ADD_INTCONSTANTZ(NOAUTH); - ADD_INTCONSTANTZ(BADVERSION); - ADD_INTCONSTANTZ(NOCHILDRENFOREPHEMERALS); - ADD_INTCONSTANTZ(NODEEXISTS); - ADD_INTCONSTANTZ(NOTEMPTY); - ADD_INTCONSTANTZ(SESSIONEXPIRED); - ADD_INTCONSTANTZ(INVALIDCALLBACK); - ADD_INTCONSTANTZ(INVALIDACL); - ADD_INTCONSTANTZ(AUTHFAILED); - ADD_INTCONSTANTZ(CLOSING); - ADD_INTCONSTANTZ(NOTHING); - ADD_INTCONSTANTZ(SESSIONMOVED); - - ADD_EXCEPTION(ApiErrorException); - ADD_EXCEPTION(NoNodeException); - ADD_EXCEPTION(NoAuthException); - ADD_EXCEPTION(BadVersionException); - ADD_EXCEPTION(NoChildrenForEphemeralsException); - ADD_EXCEPTION(NodeExistsException); - ADD_EXCEPTION(NotEmptyException); - ADD_EXCEPTION(SessionExpiredException); - ADD_EXCEPTION(InvalidCallbackException); - ADD_EXCEPTION(InvalidACLException); - ADD_EXCEPTION(AuthFailedException); - ADD_EXCEPTION(ClosingException); - ADD_EXCEPTION(NothingException); - ADD_EXCEPTION(SessionMovedException); -} diff --git a/src/contrib/zkpython/src/examples/README b/src/contrib/zkpython/src/examples/README deleted file mode 100644 index 3c5345425a8..00000000000 --- a/src/contrib/zkpython/src/examples/README +++ /dev/null @@ -1,8 +0,0 @@ - -This folder contains sample showing how you can use ZooKeeper from Python. - -You should also check the following projects: - -* http://github.com/phunt/zk-smoketest -* http://github.com/henryr/pyzk-recipes - diff --git a/src/contrib/zkpython/src/examples/watch_znode_for_changes.py b/src/contrib/zkpython/src/examples/watch_znode_for_changes.py deleted file mode 100644 index 07100f0209c..00000000000 --- a/src/contrib/zkpython/src/examples/watch_znode_for_changes.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python2.6 -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" ZNode Change Watcher Skeleton Script - -This script shows you how to write a python program that watches a specific -znode for changes and reacts to them. - -Steps to understand how this script works: - -1. start a standalone ZooKeeper server (by default it listens on localhost:2181) - -Did you know you can deploy "local clusters" by using zkconf[1]? -[1] http://github.com/phunt/zkconf - -2. enter the command line console - -3. create the test node: - [zk: (CONNECTED) 1] create /watch-test dummy-data - Created /watch-test - -4. in another shell start this script in verbose mode - $ python watch_znode_for_changes.py -v - - # you should see a lot of log messages. have a look over them because - # you can easily understand how zookeeper works - -5. update the node data: - - [zk: (CONNECTED) 2] set /watch-test new-data - cZxid = 0xa0000001a - ctime = Fri Jul 09 19:14:45 EEST 2010 - mZxid = 0xa0000001e - mtime = Fri Jul 09 19:18:18 EEST 2010 - pZxid = 0xa0000001a - cversion = 0 - dataVersion = 1 - aclVersion = 0 - ephemeralOwner = 0x0 - dataLength = 8 - numChildren = 0 - - ... and you should see similar log messages: - - 2010-07-09 19:18:18,537:11542(0xb6ea5b70):ZOO_DEBUG@process_completions@1765: Calling a watcher for node [/watch-test], type = -1 event=ZOO_CHANGED_EVENT - 2010-07-09 19:18:18,537 watch_znode_for_changes.py:83 - Running watcher: zh=0 event=3 state=3 path=/watch-test - 2010-07-09 19:18:18,537:11542(0xb6ea5b70):ZOO_DEBUG@zoo_awget@2400: Sending request xid=0x4c374b33 for path [/watch-test] to 127.0.0.1:2181 - 2010-07-09 19:18:18,545:11542(0xb76a6b70):ZOO_DEBUG@zookeeper_process@1980: Queueing asynchronous response - 2010-07-09 19:18:18,545:11542(0xb6ea5b70):ZOO_DEBUG@process_completions@1772: Calling COMPLETION_DATA for xid=0x4c374b33 rc=0 - 2010-07-09 19:18:18,545 watch_znode_for_changes.py:54 - This is where your application does work. - - You can repeat this step multiple times. - -6. that's all. in the end you can delete the node and you should see a ZOO_DELETED_EVENT - -""" - -import logging -import logging.handlers -import signal -import sys -import time -import threading -import zookeeper - -from optparse import OptionParser - -logger = logging.getLogger() - -class MyClass(threading.Thread): - znode = '/watch-test' - - def __init__(self, options, args): - threading.Thread.__init__(self) - - logger.debug('Initializing MyClass thread.') - if options.verbose: - zookeeper.set_debug_level(zookeeper.LOG_LEVEL_DEBUG) - - self.zh = zookeeper.init(options.servers) - if zookeeper.OK != zookeeper.aget(self.zh, self.znode, - self.watcher, self.handler): - logger.critical('Unable to get znode! Exiting.') - sys.exit(1) - - def __del__(self): - zookeeper.close(self.zh) - - def aget(self): - return zookeeper.aget(self.zh, self.znode, self.watcher, self.handler) - - def handler(self, zh, rc, data, stat): - """Handle zookeeper.aget() responses. - - This code handles the zookeeper.aget callback. It does not handle watches. - - Numeric arguments map to constants. See ``DATA`` in ``help(zookeeper)`` - for more information. - - Args: - zh Zookeeper handle that made this request. - rc Return code. - data Data stored in the znode. - - Does not provide a return value. - """ - if zookeeper.OK == rc: - logger.debug('This is where your application does work.') - else: - if zookeeper.NONODE == rc: - # avoid sending too many requests if the node does not yet exists - logger.info('Node not found. Trying again to set the watch.') - time.sleep(1) - - if zookeeper.OK != self.aget(): - logger.critical('Unable to get znode! Exiting.') - sys.exit(1) - - def watcher(self, zh, event, state, path): - """Handle zookeeper.aget() watches. - - This code is called when a znode changes and triggers a data watch. - It is not called to handle the zookeeper.aget call itself. - - Numeric arguments map to constants. See ``DATA`` in ``help(zookeeper)`` - for more information. - - Args: - zh Zookeeper handle that set this watch. - event Event that caused the watch (often called ``type`` elsewhere). - state Connection state. - path Znode that triggered this watch. - - Does not provide a return value. - """ - out = ['Running watcher:', - 'zh=%d' % zh, - 'event=%d' % event, - 'state=%d' % state, - 'path=%s' % path] - logger.debug(' '.join(out)) - if event == zookeeper.CHANGED_EVENT and \ - state == zookeeper.CONNECTED_STATE and \ - self.znode == path: - if zookeeper.OK != self.aget(): - logger.critical('Unable to get znode! Exiting.') - sys.exit(1) - - def run(self): - while True: - time.sleep(86400) - - -def main(argv=None): - # Allow Ctrl-C - signal.signal(signal.SIGINT, signal.SIG_DFL) - - parser = OptionParser() - parser.add_option('-v', '--verbose', - dest='verbose', - default=False, - action='store_true', - help='Verbose logging. (default: %default)') - parser.add_option('-s', '--servers', - dest='servers', - default='localhost:2181', - help='Comma-separated list of host:port pairs. (default: %default)') - - (options, args) = parser.parse_args() - - if options.verbose: - logger.setLevel(logging.DEBUG) - else: - logger.setLevel(logging.INFO) - - formatter = logging.Formatter("%(asctime)s %(filename)s:%(lineno)d - %(message)s") - stream_handler = logging.StreamHandler() - stream_handler.setFormatter(formatter) - logger.addHandler(stream_handler) - - logger.info('Starting Zookeeper python example: %s' % ' '.join(sys.argv)) - - mc = MyClass(options, args) - mc.start() - mc.join() - - -if __name__ == '__main__': - main() diff --git a/src/contrib/zkpython/src/python/setup.py b/src/contrib/zkpython/src/python/setup.py deleted file mode 100755 index 63a046ed59b..00000000000 --- a/src/contrib/zkpython/src/python/setup.py +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from distutils.core import setup, Extension - -zookeeper_basedir = "../../../" - -zookeepermodule = Extension("zookeeper", - sources=["src/c/zookeeper.c"], - include_dirs=[zookeeper_basedir + "/src/c/include", - zookeeper_basedir + "/src/c/generated"], - libraries=["zookeeper_mt"], - library_dirs=[zookeeper_basedir + "/src/c/.libs/", - zookeeper_basedir + "/build/test/test-cppunit/.libs", - "/usr/local/lib" - ]) - -setup( name="ZooKeeper", - version = "0.4", - description = "ZooKeeper Python bindings", - ext_modules=[zookeepermodule] ) diff --git a/src/contrib/zkpython/src/python/zk.py b/src/contrib/zkpython/src/python/zk.py deleted file mode 100755 index abafaa07081..00000000000 --- a/src/contrib/zkpython/src/python/zk.py +++ /dev/null @@ -1,76 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, time, threading - -f = open("out.log","w") -zookeeper.set_log_stream(f) - -connected = False -conn_cv = threading.Condition( ) - -def my_connection_watcher(handle,type,state,path): - global connected, conn_cv - print "Connected, handle is ", handle - conn_cv.acquire() - connected = True - conn_cv.notifyAll() - conn_cv.release() - -conn_cv.acquire() -print "Connecting to localhost:2181 -- " -handle = zookeeper.init("localhost:2181", my_connection_watcher, 10000, 0) -while not connected: - conn_cv.wait() -conn_cv.release() - -def my_getc_watch( handle, type, state, path ): - print "Watch fired -- " - print type, state, path - -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"}; - -try: - zookeeper.create(handle, "/zk-python", "data", [ZOO_OPEN_ACL_UNSAFE], 0) - zookeeper.get_children(handle, "/zk-python", my_getc_watch) - for i in xrange(5): - print "Creating sequence node ", i, " ", zookeeper.create(handle, "/zk-python/sequencenode", "data", [ZOO_OPEN_ACL_UNSAFE], zookeeper.SEQUENCE ) -except: - pass - -def pp_zk(handle,root, indent = 0): - """Pretty print a zookeeper tree, starting at root""" - def make_path(child): - if root == "/": - return "/" + child - return root + "/" + child - children = zookeeper.get_children(handle, root, None) - out = "" - for i in xrange(indent): - out += "\t" - out += "|---"+root + " :: " + zookeeper.get(handle, root, None)[0] - print out - for child in children: - pp_zk(handle,make_path(child),indent+1) - -print "ZNode tree -- " -pp_zk(handle,"/") - -print "Getting ACL / Stat for /zk-python --" -(stat, acl) = zookeeper.get_acl(handle, "/zk-python") -print "Stat:: ", stat -print "Acl:: ", acl - diff --git a/src/contrib/zkpython/src/test/acl_test.py b/src/contrib/zkpython/src/test/acl_test.py deleted file mode 100644 index 2d1426b26fe..00000000000 --- a/src/contrib/zkpython/src/test/acl_test.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading - -ZOO_OPEN_ACL_UNSAFE = {"perms":zookeeper.PERM_ALL, "scheme":"world", "id" :"anyone"} -ZOO_ACL_READ = {"perms":zookeeper.PERM_READ, "scheme": "world", - "id":"anyone"} -class ACLTest(zktestbase.TestBase): - """Test whether basic ACL setting and getting work correctly""" - # to do: startup and teardown via scripts? - def setUp(self): - zktestbase.TestBase.setUp(self) - try: - zookeeper.delete(self.handle, "/zk-python-acltest") - zookeeper.delete(self.handle, "/zk-python-aacltest") - except: - pass - - def test_sync_acl(self): - self.assertEqual(self.connected, True) - ret = zookeeper.create(self.handle, "/zk-python-acltest", "nodecontents", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - acls = zookeeper.get_acl(self.handle, "/zk-python-acltest") - self.assertEqual(acls[1], [ZOO_OPEN_ACL_UNSAFE]) - self.assertRaises(zookeeper.InvalidACLException,zookeeper.set_acl,self.handle, "/zk-python-acltest", -1, ZOO_ACL_READ) - zookeeper.set_acl(self.handle, "/zk-python-acltest", -1, [ZOO_ACL_READ]) - acls = zookeeper.get_acl(self.handle, "/zk-python-acltest") - self.assertEqual(acls[1], [ZOO_ACL_READ]) - - - def test_async_acl(self): - self.cv = threading.Condition() - self.cv = threading.Condition() - def aget_callback(handle, rc, acl, stat): - self.cv.acquire() - self.callback_flag = True - self.rc = rc - self.acl = acl - self.stat = stat - self.cv.notify() - self.cv.release() - - def aset_callback(handle, rc): - self.cv.acquire() - self.callback_flag = True - self.rc = rc - self.cv.notify() - self.cv.release() - - self.assertEqual(self.connected, True, "Not connected!") - ret = zookeeper.create(self.handle, "/zk-python-aacltest", "nodecontents", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - - self.cv.acquire() - zookeeper.aget_acl(self.handle, "/zk-python-aacltest", aget_callback) - self.cv.wait(15) - self.cv.release() - - self.assertEqual(self.callback_flag, True, "aget_acl timed out") - self.assertEqual(self.rc, zookeeper.OK, "aget failed") - self.assertEqual(self.acl, [ZOO_OPEN_ACL_UNSAFE], "Wrong ACL returned from aget") - - self.cv.acquire() - self.callback_flag = False - zookeeper.aset_acl(self.handle, "/zk-python-aacltest", -1, [ZOO_ACL_READ], aset_callback) - self.cv.wait(15) - self.cv.release() - - self.assertEqual(self.callback_flag, True, "aset_acl timed out") - self.assertEqual(self.rc, zookeeper.OK, "aset failed") - acls = zookeeper.get_acl(self.handle, "/zk-python-aacltest") - self.assertEqual(acls[1], [ZOO_ACL_READ], "Wrong ACL returned from get when aset") - - def test_invalid_acl(self): - self.assertRaises(zookeeper.InvalidACLException, - zookeeper.create, - self.handle, - "/zk-python-aclverifytest", - "", - None, - zookeeper.EPHEMERAL) - - def test_invalid_acl2(self): - """Verify all required keys are present in the ACL.""" - invalid_acl = [{"schema": "digest", "id": "zebra"}], - self.assertRaises(zookeeper.InvalidACLException, - zookeeper.create, - self.handle, - "/zk-python-aclverifytest", - "", - invalid_acl, - zookeeper.EPHEMERAL) - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/async_test.py b/src/contrib/zkpython/src/test/async_test.py deleted file mode 100644 index e81343570ea..00000000000 --- a/src/contrib/zkpython/src/test/async_test.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading - -class AsyncTest(zktestbase.TestBase): - """Test whether async works""" - # to do: startup and teardown via scripts? - def setUp( self ): - zktestbase.TestBase.setUp(self) - - def test_async(self): - self.assertEqual(self.connected, True) - ret = zookeeper.async(self.handle, "/") - self.assertEqual(ret, zookeeper.OK, "async failed") - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/callback_test.py b/src/contrib/zkpython/src/test/callback_test.py deleted file mode 100644 index 55d7fe17866..00000000000 --- a/src/contrib/zkpython/src/test/callback_test.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading, gc - -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - -class CallbackTest(zktestbase.TestBase): - """ - Test whether callbacks (watchers/completions) are correctly invoked - """ - # to do: startup and teardown via scripts? - def setUp(self): - zktestbase.TestBase.setUp(self) - self.cv = threading.Condition() - - def create_callback(self, callback): - """ - Returns a callable which signals cv and then calls callback - """ - def wrapper(*args, **kwargs): - self.cv.acquire() - callback(*args, **kwargs) - self.cv.notify() - self.cv.release() - return wrapper - - def test_none_callback(self): - """ - Test that no errors are raised when None is passed as a callback. - """ - self.ensureCreated("/zk-python-none-callback-test","test") - # To do this we need to issue two operations, waiting on the second - # to ensure that the first completes - zookeeper.get(self.handle, "/zk-python-none-callback-test", None) - (d,s) = zookeeper.get(self.handle, "/zk-python-none-callback-test") - self.assertEqual(d, "test") - - def callback_harness(self, trigger, test): - self.callback_flag = False - self.cv.acquire() - trigger() - self.cv.wait(15) - test() - - def test_dispatch_types(self): - """ - Test all the various dispatch mechanisms internal to the module. - """ - def dispatch_callback(*args, **kwargs): - self.callback_flag = True - self.ensureCreated("/zk-python-dispatch-test") - self.callback_harness( lambda: zookeeper.adelete(self.handle, - "/zk-python-dispatch-test", - -1, - self.create_callback(dispatch_callback)), - lambda: self.assertEqual(True, self.callback_flag, "Void dispatch not fired")) - - - self.ensureCreated("/zk-python-dispatch-test") - self.callback_harness( lambda: zookeeper.aexists(self.handle, - "/zk-python-dispatch-test", - None, - self.create_callback(dispatch_callback)), - lambda: self.assertEqual(True, self.callback_flag, "Stat dispatch not fired")) - - self.callback_harness( lambda: zookeeper.aget(self.handle, - "/zk-python-dispatch-test", - None, - self.create_callback(dispatch_callback)), - lambda: self.assertEqual(True, self.callback_flag, "Data dispatch not fired")) - - self.callback_harness( lambda: zookeeper.aget_children(self.handle, - "/", - None, - self.create_callback( dispatch_callback )), - lambda: self.assertEqual(True, self.callback_flag, "Strings dispatch not fired")) - - self.callback_harness( lambda: zookeeper.async(self.handle, - "/", - self.create_callback( dispatch_callback )), - lambda: self.assertEqual(True, self.callback_flag, "String dispatch not fired")) - - self.callback_harness( lambda: zookeeper.aget_acl(self.handle, - "/", - self.create_callback( dispatch_callback )), - lambda: self.assertEqual(True, self.callback_flag, "ACL dispatch not fired")) - - def test_multiple_watchers(self): - """ - Test whether multiple watchers are correctly called - """ - cv1, cv2 = threading.Condition(), threading.Condition() - def watcher1(*args, **kwargs): - cv1.acquire() - self.watcher1 = True - cv1.notify() - cv1.release() - - def watcher2(*args, **kwargs): - cv2.acquire() - self.watcher2 = True - cv2.notify() - cv2.release() - - nodename = "/zk-python-multiple-watcher-test" - self.ensureCreated(nodename, "test") - cv1.acquire() - cv2.acquire() - zookeeper.get(self.handle, nodename, watcher1) - zookeeper.get(self.handle, nodename, watcher2) - zookeeper.set(self.handle, nodename, "test") - cv1.wait(15) - cv2.wait(15) - self.assertTrue(self.watcher1 and self.watcher2, "One or more watchers failed to fire") - - def test_lose_scope(self): - """ - The idea is to test that the reference counting doesn't - fail when we retain no references outside of the module - """ - self.ensureDeleted("/zk-python-lose-scope-test") - self.ensureCreated("/zk-python-lose-scope-test") - def set_watcher(): - def fn(): self.callback_flag = True - self.callback_flag = False - zookeeper.exists(self.handle, "/zk-python-lose-scope-test", - self.create_callback( lambda handle, type, state, path: fn() ) - ) - - set_watcher() - gc.collect() - self.cv.acquire() - zookeeper.set(self.handle, "/zk-python-lose-scope-test", "test") - self.cv.wait(15) - self.assertEqual(self.callback_flag, True) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/clientid_test.py b/src/contrib/zkpython/src/test/clientid_test.py deleted file mode 100755 index 90c8f0acc30..00000000000 --- a/src/contrib/zkpython/src/test/clientid_test.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest, threading - -import zookeeper, zktestbase - -class ClientidTest(zktestbase.TestBase): - """Test whether clientids work""" - def setUp(self): - pass - - def testclientid(self): - cv = threading.Condition() - self.connected = False - def connection_watcher(handle, type, state, path): - cv.acquire() - self.connected = True - cv.notify() - cv.release() - - cv.acquire() - self.handle = zookeeper.init(self.host, connection_watcher,10000,(123456,"mypassword")) - self.assertEqual(self.handle, zookeeper.OK) - cv.wait(15.0) - cv.release() - self.assertEqual(self.connected, True, "Connection timed out to " + self.host) - (cid,passwd) = zookeeper.client_id(self.handle) - self.assertEqual(cid,123456) - self.assertEqual(passwd,"mypassword") - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/close_deadlock_test.py b/src/contrib/zkpython/src/test/close_deadlock_test.py deleted file mode 100644 index 921d2cc4b05..00000000000 --- a/src/contrib/zkpython/src/test/close_deadlock_test.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading -import time - - -class CloseDeadlockTest(zktestbase.TestBase): - """ - This tests for the issue found in - https://issues.apache.org/jira/browse/ZOOKEEPER-763 - - zookeeper.close blocks on waiting for all completions to - finish. Previously it was doing so while holding teh GIL, stopping - any completions from actually continuing. - - This test is a failure if it does not exit within a few seconds. - """ - def deadlock(): - cv = threading.Condition() - - def callback(*args): - cv.acquire() - cv.notifyAll() - cv.release() - time.sleep(1) - - cv.acquire() - zookeeper.aget(handle, "/", None, callback) - cv.wait() - zookeeper.close(handle) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/connection_test.py b/src/contrib/zkpython/src/test/connection_test.py deleted file mode 100755 index 933f7608c2a..00000000000 --- a/src/contrib/zkpython/src/test/connection_test.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest, threading, re - -import zookeeper, zktestbase -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - -class ConnectionTest(zktestbase.TestBase): - """Test whether we can make a connection""" - def setUp(self): - pass - - def testconnection(self): - cv = threading.Condition() - self.connected = False - def connection_watcher(handle, type, state, path): - cv.acquire() - self.connected = True - self.assertEqual(zookeeper.CONNECTED_STATE, state) - self.handle = handle - cv.notify() - cv.release() - - cv.acquire() - ret = zookeeper.init(self.host, connection_watcher) - cv.wait(15.0) - cv.release() - self.assertEqual(self.connected, True, "Connection timed out to " + self.host) - self.assertEqual(zookeeper.CONNECTED_STATE, zookeeper.state(self.handle)) - - self.assertEqual(zookeeper.close(self.handle), zookeeper.OK) - # Trying to close the same handle twice is an error, and the C library will segfault on it - # so make sure this is caught at the Python module layer - self.assertRaises(zookeeper.ZooKeeperException, - zookeeper.close, - self.handle) - - self.assertRaises(zookeeper.ZooKeeperException, - zookeeper.get, - self.handle, - "/") - - def testhandlereuse(self): - """ - Test a) multiple concurrent connections b) reuse of closed handles - """ - cv = threading.Condition() - self.connected = False - def connection_watcher(handle, type, state, path): - cv.acquire() - self.connected = True - self.assertEqual(zookeeper.CONNECTED_STATE, state) - self.handle = handle - cv.notify() - cv.release() - - cv.acquire() - handles = [ zookeeper.init(self.host) for i in xrange(10) ] - ret = zookeeper.init(self.host, connection_watcher) - cv.wait(15.0) - cv.release() - self.assertEqual(self.connected, True, "Connection timed out to " + self.host) - self.assertEqual(True, all( [ zookeeper.state(handle) == zookeeper.CONNECTED_STATE for handle in handles ] ), - "Not all connections succeeded") - oldhandle = handles[3] - zookeeper.close(oldhandle) - newhandle = zookeeper.init(self.host) - - # This assertion tests *internal* behaviour; i.e. that the module - # correctly reuses closed handles. This is therefore implementation - # dependent. - self.assertEqual(newhandle, oldhandle, "Didn't get reused handle") - - def testmanyhandles(self): - """ - Test the ability of the module to support many handles. - """ - # We'd like to do more, but currently the C client doesn't - # work with > 83 handles (fails to create a pipe) on MacOS 10.5.8 - handles = [ zookeeper.init(self.host) for i in xrange(63) ] - - cv = threading.Condition() - self.connected = False - def connection_watcher(handle, type, state, path): - cv.acquire() - self.connected = True - self.assertEqual(zookeeper.CONNECTED_STATE, state) - self.handle = handle - cv.notify() - cv.release() - - cv.acquire() - ret = zookeeper.init(self.host, connection_watcher) - cv.wait(15.0) - cv.release() - self.assertEqual(self.connected, True, "Connection timed out to " + self.host) - - for i,h in enumerate(handles): - path = "/zkpython-test-handles-%s" % str(i) - self.assertEqual(path, zookeeper.create(h, path, "", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL)) - - self.assertEqual(True, all( zookeeper.close(h) == zookeeper.OK for h in handles )) - - def testversionstringexists(self): - self.assertTrue(hasattr(zookeeper, '__version__')) - self.assertTrue(re.match("\d.\d.\d", zookeeper.__version__)) - - - def tearDown(self): - pass - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/create_test.py b/src/contrib/zkpython/src/test/create_test.py deleted file mode 100755 index 8ab80f9565a..00000000000 --- a/src/contrib/zkpython/src/test/create_test.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading - -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - -class CreationTest(zktestbase.TestBase): - """Test whether we can create znodes""" - # to do: startup and teardown via scripts? - def setUp(self): - zktestbase.TestBase.setUp(self) - try: - zookeeper.delete(self.handle, "/zk-python-createtest") - zookeeper.delete(self.handle, "/zk-python-acreatetest") - except: - pass - - def test_sync_create(self): - self.assertEqual(self.connected, True) - ret = zookeeper.create(self.handle, "/zk-python-createtest", "nodecontents", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - self.assertEqual(ret, "/zk-python-createtest") - self.assertRaises(zookeeper.NoChildrenForEphemeralsException, - zookeeper.create, - self.handle, - "/zk-python-createtest/invalid-child", - "", - [ZOO_OPEN_ACL_UNSAFE], - zookeeper.EPHEMERAL) - - def test_sync_create_existing(self): - self.assertEqual(self.connected, True) - ret = zookeeper.create(self.handle, "/zk-python-createtest-existing", "nodecontents", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - self.assertEqual(ret, "/zk-python-createtest-existing") - - self.assertRaises(zookeeper.NodeExistsException, - zookeeper.create, - self.handle, - "/zk-python-createtest-existing", - "nodecontents", - [ZOO_OPEN_ACL_UNSAFE], - zookeeper.EPHEMERAL) - - - def test_exception_paths(self): - """ - Make sure common exceptions due to API misuse are correctly propogated - """ - self.assertRaises(zookeeper.BadArgumentsException, - zookeeper.create, - self.handle, - "/zk-python-badargs-test", - "", - [ZOO_OPEN_ACL_UNSAFE], - -1) - self.assertRaises(zookeeper.InvalidACLException, - zookeeper.create, - self.handle, - "/zk-python-invalidacl-test", - "", - ZOO_OPEN_ACL_UNSAFE) # Error - not a list - - - def test_async_create(self): - self.cv = threading.Condition() - def callback(handle, rc, value): - self.cv.acquire() - self.callback_flag = True - self.rc = rc - self.cv.notify() - self.cv.release() - - self.assertEqual(self.connected, True, "Not connected!") - self.cv.acquire() - - ret = zookeeper.acreate(self.handle, "/zk-python-acreatetest", "nodecontents", - [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL, - callback ) - self.assertEqual(ret, zookeeper.OK, "acreate failed") - while not self.callback_flag: - self.cv.wait(15) - self.cv.release() - - self.assertEqual(self.callback_flag, True, "acreate timed out") - self.assertEqual(self.rc, zookeeper.OK) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/delete_test.py b/src/contrib/zkpython/src/test/delete_test.py deleted file mode 100755 index 913b6a9a94e..00000000000 --- a/src/contrib/zkpython/src/test/delete_test.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading - -class DeletionTest(zktestbase.TestBase): - """Test whether we can delete znodes""" - - def test_sync_delete(self): - ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - self.assertEqual(self.connected, True) - ret = zookeeper.create(self.handle, "/zk-python-deletetest", "nodecontents", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - self.assertEqual(ret, "/zk-python-deletetest") - ret = zookeeper.delete(self.handle,"/zk-python-deletetest") - self.assertEqual(ret, zookeeper.OK) - children = zookeeper.get_children(self.handle, "/") - self.assertEqual(False, "zk-python-deletetest" in children) - - # test exception - self.assertRaises(zookeeper.NoNodeException, - zookeeper.delete, - self.handle, - "/zk-python-deletetest") - - def test_async_delete(self): - ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - self.assertEqual(self.connected, True) - ret = zookeeper.create(self.handle, "/zk-python-adeletetest", "nodecontents", [ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - self.assertEqual(ret, "/zk-python-adeletetest") - - self.cv = threading.Condition() - self.callback_flag = False - self.rc = -1 - def callback(handle, rc): - self.cv.acquire() - self.callback_flag = True - self.cv.notify() - self.rc = rc # don't assert this here, as if the assertion fails, the test will block - self.cv.release() - - self.cv.acquire() - ret = zookeeper.adelete(self.handle,"/zk-python-adeletetest",-1,callback) - self.assertEqual(ret, zookeeper.OK, "adelete failed") - while not self.callback_flag: - self.cv.wait(15) - self.cv.release() - - self.assertEqual(self.callback_flag, True, "adelete timed out") - self.assertEqual(self.rc, zookeeper.OK) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/exists_test.py b/src/contrib/zkpython/src/test/exists_test.py deleted file mode 100755 index ddc6ef3683b..00000000000 --- a/src/contrib/zkpython/src/test/exists_test.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading - -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} -class ExistsTest(zktestbase.TestBase): - def setUp( self ): - zktestbase.TestBase.setUp(self) - try: - zookeeper.create(self.handle, "/zk-python-existstest","existstest", [ZOO_OPEN_ACL_UNSAFE],zookeeper.EPHEMERAL) - zookeeper.create(self.handle, "/zk-python-aexiststest","existstest",[ZOO_OPEN_ACL_UNSAFE],zookeeper.EPHEMERAL) - except: - pass - - def test_sync_exists(self): - self.assertEqual(self.connected, True) - ret = zookeeper.exists(self.handle, "/zk-python-existstest", None) - self.assertNotEqual(ret, None, "/zk-python-existstest does not exist (possibly means creation failure)") - - def test_sync_nexists(self): - self.assertEqual(None, zookeeper.exists(self.handle, "/i-dont-exist", None)) - - - def test_async_exists(self): - self.cv = threading.Condition() - def callback(handle, rc, stat): - self.cv.acquire() - self.callback_flag = True - self.cv.notify() - self.cv.release() - self.rc = rc - - self.assertEqual(self.connected, True) - - self.cv.acquire() - ret = zookeeper.aexists(self.handle, "/zk-python-aexiststest", None, - callback ) - self.assertEqual(ret, zookeeper.OK) - while not self.callback_flag: - self.cv.wait(15) - self.cv.release() - - self.assertEqual(self.callback_flag, True, "aexists timed out") - self.assertEqual(self.rc, zookeeper.OK, "Return code not ok:" + zookeeper.zerror(self.rc)) - - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/get_set_test.py b/src/contrib/zkpython/src/test/get_set_test.py deleted file mode 100755 index 97621249bba..00000000000 --- a/src/contrib/zkpython/src/test/get_set_test.py +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import zookeeper, zktestbase, unittest, threading -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - -class GetSetTest(zktestbase.TestBase): - def setUp( self ): - zktestbase.TestBase.setUp(self) - try: - zookeeper.create(self.handle, "/zk-python-getsettest", "on",[ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - zookeeper.create(self.handle, "/zk-python-agetsettest", - "on",[ZOO_OPEN_ACL_UNSAFE], zookeeper.EPHEMERAL) - except: - pass - - def test_sync_getset(self): - self.assertEqual(self.connected, True, "Not connected!") - (data,stat) = zookeeper.get(self.handle, "/zk-python-getsettest", None) - self.assertEqual(data, "on", "Data is not 'on' as expected: " + data) - ret = zookeeper.set(self.handle, "/zk-python-getsettest", - "off", stat["version"]) - (data,stat) = zookeeper.get(self.handle, "/zk-python-getsettest", None) - self.assertEqual(data, "off", "Data is not 'off' as expected: " + data) - self.assertRaises(zookeeper.BadVersionException, - zookeeper.set, - self.handle, - "/zk-python-getsettest", - "test", - stat["version"]+1) - - def test_stat_deleted_node(self): - """ - Test for a bug that surfaced when trying to build a - stat object from a non-existant node. - - """ - self.ensureDeleted("/zk-python-test-deleteme") - self.assertRaises(zookeeper.NoNodeException, - zookeeper.get, - self.handle, - "/zk-python-test-deleteme") - self.cv = threading.Condition() - def callback(handle, rc, value, stat): - self.cv.acquire() - self.stat = stat - self.rc = rc - self.value = value - self.callback_flag = True - self.cv.notify() - self.cv.release() - self.cv.acquire() - zookeeper.aget(self.handle, "/zk-python-test-deleteme", None, callback) - self.cv.wait(15) - self.assertEqual(self.callback_flag, True, "aget timed out!") - self.assertEqual(self.stat, None, "Stat should be none!") - self.assertEqual(self.value, None, "Value should be none!") - - def test_sync_get_large_datanode(self): - """ - Test that we can retrieve datanode sizes up to - 1Mb with default parameters (depends on ZooKeeper server). - """ - - data = ''.join(["A" for x in xrange(1024*1023)]) - self.ensureDeleted("/zk-python-test-large-datanode") - zookeeper.create(self.handle, "/zk-python-test-large-datanode", data, - [{"perms":0x1f, "scheme":"world", "id" :"anyone"}]) - (ret,stat) = zookeeper.get(self.handle, "/zk-python-test-large-datanode") - self.assertEqual(len(ret), 1024*1023, - "Should have got 1Mb returned, instead got %s" % len(ret)) - (ret,stat) = zookeeper.get(self.handle, "/zk-python-test-large-datanode",None,500) - self.assertEqual(len(ret), 500, - "Should have got 500 bytes returned, instead got %s" % len(ret)) - - - - def test_async_getset(self): - self.cv = threading.Condition() - def get_callback(handle, rc, value, stat): - self.cv.acquire() - self.callback_flag = True - self.rc = rc - self.value = (value,stat) - self.cv.notify() - self.cv.release() - - def set_callback(handle, rc, stat): - self.cv.acquire() - self.callback_flag = True - self.rc = rc - self.value = stat - self.cv.notify() - self.cv.release() - - self.assertEqual(self.connected, True, "Not connected!") - - self.cv.acquire() - self.callback_flag = False - ret = zookeeper.aset(self.handle, "/zk-python-agetsettest", "off", -1, set_callback) - self.assertEqual(ret, zookeeper.OK, "aset failed") - while not self.callback_flag: - self.cv.wait(15) - self.cv.release() - self.assertEqual(self.callback_flag, True, "aset timed out") - - self.cv.acquire() - self.callback_flag = False - ret = zookeeper.aget(self.handle, "/zk-python-agetsettest", None, get_callback) - self.assertEqual(ret, zookeeper.OK, "aget failed") - self.cv.wait(15) - self.cv.release() - self.assertEqual(self.callback_flag, True, "aget timed out") - self.assertEqual(self.value[0], "off", "Data is not 'off' as expected: " + self.value[0]) - - def test_sync_getchildren(self): - self.ensureCreated("/zk-python-getchildrentest", flags=0) - self.ensureCreated("/zk-python-getchildrentest/child") - children = zookeeper.get_children(self.handle, "/zk-python-getchildrentest") - self.assertEqual(len(children), 1, "Expected to find 1 child, got " + str(len(children))) - - def test_async_getchildren(self): - self.ensureCreated("/zk-python-getchildrentest", flags=0) - self.ensureCreated("/zk-python-getchildrentest/child") - - def gc_callback(handle, rc, children): - self.cv.acquire() - self.rc = rc - self.children = children - self.callback_flag = True - self.cv.notify() - self.cv.release() - - self.cv.acquire() - self.callback_flag = False - zookeeper.aget_children(self.handle, "/zk-python-getchildrentest", None, gc_callback) - self.cv.wait(15) - self.assertEqual(self.callback_flag, True, "aget_children timed out") - self.assertEqual(self.rc, zookeeper.OK, "Return code for aget_children was not OK - %s" % zookeeper.zerror(self.rc)) - self.assertEqual(len(self.children), 1, "Expected to find 1 child, got " + str(len(self.children))) - - - def test_async_getchildren_with_watcher(self): - self.ensureCreated("/zk-python-getchildrentest", flags=0) - self.ensureCreated("/zk-python-getchildrentest/child") - - watched = [] - - def watcher(*args): - self.cv.acquire() - watched.append(args) - self.cv.notify() - self.cv.release() - - def children_callback(*args): - self.cv.acquire() - self.cv.notify() - self.cv.release() - - zookeeper.aget_children( - self.handle, "/zk-python-getchildrentest", watcher, children_callback) - - self.cv.acquire() - self.cv.wait() - self.cv.release() - - self.cv.acquire() - self.ensureCreated("/zk-python-getchildrentest/child2") - self.cv.wait(15) - self.assertTrue(watched) - -if __name__ == '__main__': - unittest.main() diff --git a/src/contrib/zkpython/src/test/run_tests.sh b/src/contrib/zkpython/src/test/run_tests.sh deleted file mode 100755 index 18d924085d4..00000000000 --- a/src/contrib/zkpython/src/test/run_tests.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Usage: run_tests.sh testdir [logdir] -# logdir is optional, defaults to cwd - -# get the number of command-line arguments given -ARGC=$# - -# check to make sure enough arguments were given or exit -if [ $ARGC -lt 2 ]; then - export ZKPY_LOG_DIR="." -else - export ZKPY_LOG_DIR=$2 -fi - -# Find the build directory containing zookeeper.so -SO_PATH=`find ../../../build/ -name "zookeeper.so" | head -1` -PYTHONPATH=`dirname $SO_PATH` -LIB_PATH=../../c/.libs/:../../../build/test/test-cppunit/.libs -for test in `ls $1/*_test.py`; -do - echo "Running $test" - LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test -done diff --git a/src/contrib/zkpython/src/test/zkServer.sh b/src/contrib/zkpython/src/test/zkServer.sh deleted file mode 100755 index 51d508f6969..00000000000 --- a/src/contrib/zkpython/src/test/zkServer.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ "x$1" == "x" ] -then - echo "USAGE: $0 startClean|start|stop hostPorts" - exit 2 -fi - -if [ "x$1" == "xstartClean" ] -then - if [ "x${base_dir}" == "x" ] - then - rm -rf /tmp/zkdata - else - rm -rf ${base_dir}/build/tmp - fi -fi - -if [ "x${base_dir}" == "x" ] -then -zk_base="../../../" -else -zk_base="${base_dir}" -fi - -CLASSPATH="$CLASSPATH:${zk_base}/build/classes" -CLASSPATH="$CLASSPATH:${zk_base}/conf" - -for i in "${zk_base}"/build/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" -done - -for i in "${zk_base}"/src/java/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" -done - -# Make sure nothing is left over from before -#fuser -skn tcp 22182/tcp - -case $1 in -start|startClean) - if [ "x${base_dir}" == "x" ] - then - mkdir -p /tmp/zkdata - java -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain 22182 /tmp/zkdata &> /tmp/zk.log & - else - mkdir -p ${base_dir}/build/tmp/zkdata - java -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain 22182 ${base_dir}/build/tmp/zkdata &> ${base_dir}/build/tmp/zk.log & - fi - sleep 5 - ;; -stop) - # Already killed above - ;; -*) - echo "Unknown command " + $1 - exit 2 -esac - diff --git a/src/contrib/zkpython/src/test/zktestbase.py b/src/contrib/zkpython/src/test/zktestbase.py deleted file mode 100755 index 86e6c278f5a..00000000000 --- a/src/contrib/zkpython/src/test/zktestbase.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import unittest, threading, zookeeper -ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"} - -class TestBase(unittest.TestCase): - def __init__(self,methodName='runTest'): - unittest.TestCase.__init__(self,methodName) - self.host = "localhost:22182" - self.connected = False - self.handle = -1 - logdir = os.environ.get("ZKPY_LOG_DIR") - logfile = os.path.join(logdir, self.__class__.__name__ + ".log") - try: - f = open(logfile,"w") - zookeeper.set_log_stream(f) - except IOError: - print "Couldn't open " + logfile + " for writing" - - - def setUp(self): - self.callback_flag = False - self.cv = threading.Condition() - self.connected = False - def connection_watcher(handle, type, state, path): - self.cv.acquire() - self.connected = True - self.cv.notify() - self.cv.release() - - self.cv.acquire() - self.handle = zookeeper.init(self.host, connection_watcher) - self.cv.wait(15.0) - self.cv.release() - - if not self.connected: - raise Exception("Couldn't connect to host -", self.host) - - def newConnection(self): - cv = threading.Condition() - self.pending_connection = False - def connection_watcher(handle, type, state, path): - cv.acquire() - self.pending_connection = True - cv.notify() - cv.release() - - cv.acquire() - handle = zookeeper.init(self.host, connection_watcher) - cv.wait(15.0) - cv.release() - - if not self.pending_connection: - raise Exception("Couldn't connect to host -", self.host) - return handle - - def ensureDeleted(self,path): - self.assertEqual(zookeeper.CONNECTED_STATE, zookeeper.state(self.handle), "Not connected!") - try: - self.assertEqual(zookeeper.OK, zookeeper.delete(self.handle, path)) - except zookeeper.NoNodeException: - pass - - def ensureCreated(self,path,data="",flags=zookeeper.EPHEMERAL): - """ - It's possible not to get the flags you want here if the node already exists - """ - self.assertEqual(zookeeper.CONNECTED_STATE, zookeeper.state(self.handle), "Not connected!") - try: - self.assertEqual(path, zookeeper.create(self.handle, path, data, [ZOO_OPEN_ACL_UNSAFE], flags)) - except zookeeper.NodeExistsException: - pass - - def tearDown(self): - if self.connected: - zookeeper.close(self.handle) diff --git a/src/contrib/zktreeutil/Makefile.am b/src/contrib/zktreeutil/Makefile.am deleted file mode 100644 index 36da1a50d4e..00000000000 --- a/src/contrib/zktreeutil/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -## Process this file with automake to produce Makefile.in - -SUBDIRS = src - diff --git a/src/contrib/zktreeutil/README.txt b/src/contrib/zktreeutil/README.txt deleted file mode 100644 index 43b06fa72c2..00000000000 --- a/src/contrib/zktreeutil/README.txt +++ /dev/null @@ -1,74 +0,0 @@ -========================================== -zktreeutil - Zookeeper Tree Data Utility -Author: Anirban Roy -Organization: Yahoo Inc. -========================================== - -zktreeutil program is intended to manage and manipulate zk-tree data quickly, effi- -ciently and with ease. The utility operates on free-form ZK-tree and hence can be used -for any cluster managed by Zookeeper. Here are the basic functionalities - - -EXPORT: The whole/partial ZK-tree is exported into a XML file. This helps in -capturing a current snapshot of the data for backup/analysis. For a subtree -export, one need to specify the path to the ZK-subtree with proper option. - -IMPORT: The ZK-tree can be imported from XML into ZK cluster. This helps in priming -the new ZK cluster with static configuration. The import can be non-intrusive by -making only the additions in the existing data. The import of subtree is also -possible by optionally providing the path to the ZK-subtree. - -DIFF: Creates a diff between live ZK data vs data saved in XML file. Diff can ignore -some ZK-tree branches (possibly dynamic data) on reading the optional ignore flag -from XML file. Diffing on a ZK-subtree achieved by providing path to ZK-subtree with -diff command. - -UPDATE: Make the incremental changes into the live ZK-tree from saved XML, essentia- -lly after running the diff. - -DUMP: Dumps the ZK-tree on the standard output device reading either from live ZK -server or XML file. Like export, ZK-subtree can be dumped with optionaly -providing the path to the ZK-subtree, and till a certain depth of the (sub)tree. - -The exported ZK data into XML file can be shortened by only keeping the static ZK -nodes which are required to prime a cluster. The dynamic zk nodes (created on-the- -fly) can be ignored by setting a 'ignore' attribute at the root node of the dynamic -subtree (see tests/zk_sample.xml), possibly deleting all inner ZK nodes under that. -Once ignored, the whole subtree is ignored during DIFF, UPDATE and WRITE. - -Pre-requisites --------------- -1. Linux system with 2.6.X kernel. -2. Zookeeper C client library (locally built at ../../c/.libs) >= 3.X.X -3. Development build libraries (rpm packages): - a. boost-devel >= 1.32.0 - b. libxml2-devel >= 2.7.3 - c. log4cxx0100-devel >= 0.10.0 - -Build instructions ------------------- -1. cd into this directory -2. autoreconf -if -3. ./configure -4. make -5. 'zktreeutil' binary created under src directory - -Limitations ------------ -Current version works with text data only, binary data will be supported in future -versions. - -Testing and usage of zktreeutil --------------------------------- -1. Run Zookeeper server locally on port 2181 -2. export LD_LIBRARY_PATH=../../c/.libs/:/usr/local/lib/ -3. ./src/zktreeutil --help # show help -4. ./src/zktreeutil --zookeeper=localhost:2181 --import --xmlfile=tests/zk_sample.xml 2>/dev/null # import sample ZK tree -5. ./src/zktreeutil --zookeeper=localhost:2181 --dump --path=/myapp/version-1.0 2>/dev/null # dump Zk subtree -5. ./src/zktreeutil --zookeeper=localhost:2181 --dump --depth=3 2>/dev/null # dump Zk tree till certain depth -6. ./src/zktreeutil --xmlfile=zk_sample.xml -D 2>/dev/null # dump the xml data -7. Change zk_sample.xml with adding/deleting/chaging some nodes -8. ./src/zktreeutil -z localhost:2181 -F -x zk_sample.xml -p /myapp/version-1.0/configuration 2>/dev/null # take a diff of changes -9. ./src/zktreeutil -z localhost:2181 -E 2>/dev/null > zk_sample2.xml # export the mofied ZK tree -10. ./src/zktreeutil -z localhost:2181 -U -x zk_sample.xml -p /myapp/version-1.0/distributions 2>/dev/null # update with incr. changes -11. ./src/zktreeutil --zookeeper=localhost:2181 --import --force --xmlfile=zk_sample2.xml 2>/dev/null # re-prime the ZK tree - diff --git a/src/contrib/zktreeutil/build.xml b/src/contrib/zktreeutil/build.xml deleted file mode 100644 index 809d134ecd1..00000000000 --- a/src/contrib/zktreeutil/build.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/zktreeutil/configure.ac b/src/contrib/zktreeutil/configure.ac deleted file mode 100644 index b4a82a76af8..00000000000 --- a/src/contrib/zktreeutil/configure.ac +++ /dev/null @@ -1,66 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) - -AC_INIT([zktreeutil], [1.0.0]) -AM_INIT_AUTOMAKE(foreign) - -AC_CONFIG_SRCDIR([src]) -AM_CONFIG_HEADER([config.h]) - -PACKAGE=zktreeutil -VERSION=1.0.0 - -AC_SUBST(PACKAGE) -AC_SUBST(VERSION) -BUILD_PATH="`pwd`" - -# Checks for programs. -AC_LANG_CPLUSPLUS -AC_PROG_CXX - -# Checks for libxm2. -AM_PATH_XML2(2.7.3) -XML2_INCLUDE="/usr/include/libxml2" -AC_SUBST(XML2_INCLUDE) - -# Zookeeper C client -ZOOKEEPER_PATH=${BUILD_PATH}/../../c -AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER="-L${ZOOKEEPER_PATH}/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_PATH}/.libs"]) -if test -z "${ZOOKEEPER}"; then - AC_ERROR("... zookeeper C client not found!") -fi - -AC_SUBST(ZOOKEEPER) -AC_SUBST(ZOOKEEPER_PATH) - -### log4cxx ### - -LOG4CXX_VERSION="0.10.0" -LOG4CXX_INCLUDE="/usr/local/include" -LOG4CXX_LIB_PATH="/usr/local/lib" -AC_CHECK_LIB(log4cxx, main, [LOG4CXX="-L${LOG4CXX_LIB_PATH} -llog4cxx"],,["-L${LOG4CXX_LIB_PATH}"]) -if test -z "${LOG4CXX}"; then - AC_ERROR("... log4cxx not found!") -fi - -AC_SUBST(LOG4CXX) -AC_SUBST(LOG4CXX_VERSION) -AC_SUBST(LOG4CXX_INCLUDE) - -# Checks for header files. -AC_HEADER_DIRENT -AC_HEADER_STDC -AC_CHECK_HEADERS([stdlib.h string.h stdio.h unistd.h boost/shared_ptr.hpp boost/algorithm/string.hpp boost/algorithm/string/split.hpp]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_HEADER_STDBOOL -AC_C_CONST -AC_C_INLINE -AC_TYPE_SIZE_T -AC_C_VOLATILE - -AC_CONFIG_FILES([Makefile]) -AC_CONFIG_FILES([src/Makefile]) -AC_OUTPUT diff --git a/src/contrib/zktreeutil/src/Makefile.am b/src/contrib/zktreeutil/src/Makefile.am deleted file mode 100644 index 641077a88e4..00000000000 --- a/src/contrib/zktreeutil/src/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -AM_CXXFLAGS = -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ - -I$(top_srcdir)/include -I${LOG4CXX_INCLUDE} -I/usr/include \ - -I${XML2_INCLUDE} - -bin_PROGRAMS = zktreeutil - -zktreeutil_SOURCES = ZkAdaptor.cc ZkTreeUtil.cc ZkTreeUtilMain.cc -zktreeutil_LDADD = ${ZOOKEEPER} ${XML_LIBS} ${LOG4CXX} diff --git a/src/contrib/zktreeutil/src/SimpleTree.h b/src/contrib/zktreeutil/src/SimpleTree.h deleted file mode 100644 index 8226f05c9e1..00000000000 --- a/src/contrib/zktreeutil/src/SimpleTree.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __SIMPLE_TREE_H__ -#define __SIMPLE_TREE_H__ - -#include -#include - -namespace zktreeutil -{ - using std::vector; - - /** - * \brief A simple tree data-structure template. - */ - template < class KeyType, class DataType > class SimpleTreeNode - { - private: - /** - * \brief The type representing simple-tree node smart-pointer. - */ - typedef boost::shared_ptr< SimpleTreeNode< KeyType, DataType > > SimpleTreeNodeSptr; - - public: - /** - * \brief Constructor. - * - * @param isRoot the flag indicating whether the node is root. - */ - SimpleTreeNode (bool isRoot=false) : isRoot_(isRoot) - { - } - - /** - * \brief Constructor. - * - * @param key the key stored at the tree node - * @param isRoot the flag indicating whether the node is root - */ - SimpleTreeNode (const KeyType& key, bool isRoot=false) : - isRoot_(isRoot), key_(key) - { - } - - /** - * \brief Constructor. - * - * @param key the key stored at the tree node - * @param val the value stored at the tree node - * @param isRoot the flag indicating whether the node is root - */ - SimpleTreeNode (const KeyType& key, const DataType& val, bool isRoot=false) : - isRoot_(isRoot), key_(key), val_(val) - { - } - - /** - * \brief Destructor. - */ - ~SimpleTreeNode () throw() {} - - /** - * \brief Add a child node to this node. - * - * @param node the child node to be added - */ - void addChild (const SimpleTreeNodeSptr node) { children_.push_back (node); } - - /** - * \brief Sets the key of this node. - * - * @param key the key to be set - */ - void setKey (const KeyType& key) { key_ = key; } - - /** - * \brief Sets the data of this node. - * - * @param val the value to be set - */ - void setData (const DataType& val) { val_ = val; } - - /** - * \brief Gets the key of this node. - * - * @return the key of this node - */ - KeyType getKey () const { return key_; } - - /** - * \brief Gets the data of this node. - * - * @return the value of this node - */ - DataType getData () const { return val_; } - - /** - * \brief Gets the i'th of this node. - * - * @param idx the index of the child node - * @return the child node - */ - SimpleTreeNodeSptr getChild (unsigned idx) const { return children_[idx]; } - - /** - * \brief Gets the number of children of this node. - * - * @return the number of children - */ - unsigned numChildren () const { return children_.size(); } - - /** - * \brief Indicates whether this node is root. - * - * @return 'true' if this node is root, 'false' otherwise - */ - bool isRoot () const { return isRoot_; } - - /** - * \brief Indicates whether this node is leaf node. - * - * @return 'true' if this node is leaf node, 'false' otherwise - */ - bool isLeaf () const { return !numChildren(); } - - private: - bool isRoot_; // Flag indicates if the node is root - KeyType key_; // Key of this node - DataType val_; // Value of this node - vector< SimpleTreeNodeSptr > children_; // List of children of this node - }; -} - -#endif // __SIMPLE_TREE_H__ diff --git a/src/contrib/zktreeutil/src/ZkAdaptor.cc b/src/contrib/zktreeutil/src/ZkAdaptor.cc deleted file mode 100644 index baec8f9b0fa..00000000000 --- a/src/contrib/zktreeutil/src/ZkAdaptor.cc +++ /dev/null @@ -1,513 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ZkAdaptor.h" -#include -#include -#include -#include -#include - -// Logger -static log4cxx::LoggerPtr zkLoggerPtr = log4cxx::Logger::getLogger ("zookeeper.core"); - -namespace zktreeutil -{ - /** - * \brief This class provides logic for checking if a request can be retried. - */ - class RetryHandler - { - public: - RetryHandler(const ZooKeeperConfig &zkConfig) : m_zkConfig(zkConfig) - { - if (zkConfig.getAutoReconnect()) - retries = 2; - else - retries = 0; - } - - /** - * \brief Attempts to fix a side effect of the given RC. - * - * @param rc the ZK error code - * @return whether the error code has been handled and the caller should - * retry an operation the caused this error - */ - bool handleRC(int rc) - { - //check if the given error code is recoverable - if (!retryOnError(rc)) - return false; - - std::cerr << "[zktreeuti] Number of retries left: " << retries << std::endl; - if (retries-- > 0) - return true; - else - return false; - } - - private: - /** - * The ZK config. - */ - const ZooKeeperConfig &m_zkConfig; - - /** - * The number of outstanding retries. - */ - int retries; - - /** - * Checks whether the given error entitles this adapter - * to retry the previous operation. - * - * @param zkErrorCode one of the ZK error code - */ - static bool retryOnError(int zkErrorCode) - { - return (zkErrorCode == ZCONNECTIONLOSS || zkErrorCode == ZOPERATIONTIMEOUT); - } - }; - - - // ======================================================================= - - ZooKeeperAdapter::ZooKeeperAdapter(ZooKeeperConfig config) throw(ZooKeeperException) : - m_zkConfig(config), - mp_zkHandle(NULL) - { - // Enforce setting up appropriate ZK log level - if (zkLoggerPtr->isDebugEnabled() -#ifdef LOG4CXX_TRACE - || zkLoggerPtr->isTraceEnabled() -#endif - ) - { - zoo_set_debug_level( ZOO_LOG_LEVEL_DEBUG ); - } else if (zkLoggerPtr->isInfoEnabled()) { - zoo_set_debug_level( ZOO_LOG_LEVEL_INFO ); - } else if (zkLoggerPtr->isWarnEnabled()) { - zoo_set_debug_level( ZOO_LOG_LEVEL_WARN ); - } else { - zoo_set_debug_level( ZOO_LOG_LEVEL_ERROR ); - } - - // Establish the connection - reconnect(); - } - - ZooKeeperAdapter::~ZooKeeperAdapter() - { - try - { - disconnect(); - } - catch (std::exception &e) - { - std::cerr << "[zktreeutil] An exception while disconnecting from ZK: " - << e.what() - << std::endl; - } - } - - void ZooKeeperAdapter::validatePath(const string &path) throw(ZooKeeperException) - { - if (path.find ("/") != 0) - { - std::ostringstream oss; - oss << "Node path must start with '/' but" "it was '" - << path - << "'"; - throw ZooKeeperException (oss.str()); - } - if (path.length() > 1) - { - if (path.rfind ("/") == path.length() - 1) - { - std::ostringstream oss; - oss << "Node path must not end with '/' but it was '" - << path - << "'"; - throw ZooKeeperException (oss.str()); - } - if (path.find( "//" ) != string::npos) - { - std::ostringstream oss; - oss << "Node path must not contain '//' but it was '" - << path - << "'"; - throw ZooKeeperException (oss.str()); - } - } - } - - void ZooKeeperAdapter::disconnect() - { - if (mp_zkHandle != NULL) - { - zookeeper_close (mp_zkHandle); - mp_zkHandle = NULL; - } - } - - void ZooKeeperAdapter::reconnect() throw(ZooKeeperException) - { - // Clear the connection state - disconnect(); - - // Establish a new connection to ZooKeeper - mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), - NULL, - m_zkConfig.getLeaseTimeout(), - 0, - NULL, - 0); - if (mp_zkHandle == NULL) - { - // Invalid handle returned - std::ostringstream oss; - oss << "Unable to connect to ZK running at '" - << m_zkConfig.getHosts() - << "'"; - throw ZooKeeperException (oss.str()); - } - - // Enter into connect loop - int64_t connWaitTime = m_zkConfig.getConnectTimeout(); - while (1) - { - int state = zoo_state (mp_zkHandle); - if (state == ZOO_CONNECTED_STATE) - { - // connected - std::cerr << "[zktreeutil] Connected! mp_zkHandle: " - << mp_zkHandle - << std::endl; - return; - } - else if ( state && state != ZOO_CONNECTING_STATE) - { - // Not connecting any more... some other issue - std::ostringstream oss; - oss << "Unable to connect to ZK running at '" - << m_zkConfig.getHosts() - << "'; state=" - << state; - throw ZooKeeperException (oss.str()); - } - - // Still connecting, wait and come back - struct timeval now; - gettimeofday( &now, NULL ); - int64_t milliSecs = -(now.tv_sec * 1000LL + now.tv_usec / 1000); - std::cerr << "[zktreeutil] About to wait 1 sec" << std::endl; - sleep (1); - gettimeofday( &now, NULL ); - milliSecs += now.tv_sec * 1000LL + now.tv_usec / 1000; - connWaitTime -= milliSecs; - // Timed out !!! - if (connWaitTime <= 0) - break; - } - - // Timed out while connecting - std::ostringstream oss; - oss << "Timed out while connecting to ZK running at '" - << m_zkConfig.getHosts() - << "'"; - throw ZooKeeperException (oss.str()); - } - - void ZooKeeperAdapter::verifyConnection() throw(ZooKeeperException) - { - // Check connection state - int state = zoo_state (mp_zkHandle); - if (state != ZOO_CONNECTED_STATE) - { - if (m_zkConfig.getAutoReconnect()) - { - // Trying to reconnect - std::cerr << "[zktreeutil] Trying to reconnect..." << std::endl; - reconnect(); - } - else - { - std::ostringstream oss; - oss << "Disconnected from ZK running at '" - << m_zkConfig.getHosts() - << "'; state=" - << state; - throw ZooKeeperException (oss.str()); - } - } - } - - bool ZooKeeperAdapter::createNode(const string &path, - const string &value, - int flags, - bool createAncestors) throw(ZooKeeperException) - { - const int MAX_PATH_LENGTH = 1024; - char realPath[MAX_PATH_LENGTH]; - realPath[0] = 0; - - int rc; - RetryHandler rh(m_zkConfig); - do - { - verifyConnection(); - rc = zoo_create( mp_zkHandle, - path.c_str(), - value.c_str(), - value.length(), - &ZOO_OPEN_ACL_UNSAFE, - flags, - realPath, - MAX_PATH_LENGTH ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) // check return status - { - if (rc == ZNODEEXISTS) - { - //the node already exists - std::cerr << "[zktreeutil] ZK node " << path << " already exists" << std::endl; - return false; - } - else if (rc == ZNONODE && createAncestors) - { - std::cerr << "[zktreeutil] Intermediate ZK node missing in path " << path << std::endl; - //one of the ancestors doesn't exist so lets start from the root - //and make sure the whole path exists, creating missing nodes if - //necessary - for (string::size_type pos = 1; pos != string::npos; ) - { - pos = path.find( "/", pos ); - if (pos != string::npos) - { - try - { - createNode( path.substr( 0, pos ), "", 0, true ); - } - catch (ZooKeeperException &e) - { - throw ZooKeeperException( string("Unable to create " "node ") + path, rc ); - } - pos++; - } - else - { - // No more path components - return createNode( path, value, flags, false ); - } - } - } - - // Unexpected error during create - std::cerr << "[zktreeutil] Error in creating ZK node " << path << std::endl; - throw ZooKeeperException( string("Unable to create node ") + path, rc ); - } - - // Success - std::cerr << "[zktreeutil] " << realPath << " has been created" << std::endl; - return true; - } - - bool ZooKeeperAdapter::deleteNode(const string &path, - bool recursive, - int version) throw(ZooKeeperException) - { - // Validate the zk path - validatePath( path ); - - int rc; - RetryHandler rh(m_zkConfig); - do - { - verifyConnection(); - rc = zoo_delete( mp_zkHandle, path.c_str(), version ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) //check return status - { - if (rc == ZNONODE) - { - std::cerr << "[zktreeutil] ZK Node " - << path - << " does not exist" - << std::endl; - return false; - } - if (rc == ZNOTEMPTY && recursive) - { - std::cerr << "[zktreeutil] ZK Node " - << path - << " not empty; deleting..." - << std::endl; - //get all children and delete them recursively... - vector nodeList = getNodeChildren (path); - for (vector::const_iterator i = nodeList.begin(); - i != nodeList.end(); - ++i) { - deleteNode( *i, true ); - } - //...and finally attempt to delete the node again - return deleteNode( path, false ); - } - - // Unexpected return without success - std::cerr << "[zktreeutil] Unable to delete ZK node " << path << std::endl; - throw ZooKeeperException( string("Unable to delete node ") + path, rc ); - } - - // success - std::cerr << "[zktreeutil] " << path << " has been deleted" << std::endl; - return true; - } - - vector< string > ZooKeeperAdapter::getNodeChildren (const string &path) throw (ZooKeeperException) - { - // Validate the zk path - validatePath( path ); - - String_vector children; - memset( &children, 0, sizeof(children) ); - int rc; - RetryHandler rh(m_zkConfig); - do - { - verifyConnection(); - rc = zoo_get_children( mp_zkHandle, - path.c_str(), - 0, - &children ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) // check return code - { - std::cerr << "[zktreeutil] Error in fetching children of " << path << std::endl; - throw ZooKeeperException( string("Unable to get children of node ") + path, rc ); - } - else - { - vector< string > nodeList; - for (int i = 0; i < children.count; ++i) - { - //convert each child's path from relative to absolute - string absPath(path); - if (path != "/") - { - absPath.append( "/" ); - } - absPath.append( children.data[i] ); - nodeList.push_back( absPath ); - } - - //make sure the order is always deterministic - sort( nodeList.begin(), nodeList.end() ); - return nodeList; - } - } - - bool ZooKeeperAdapter::nodeExists(const string &path) throw(ZooKeeperException) - { - // Validate the zk path - validatePath( path ); - - struct Stat tmpStat; - struct Stat* stat = &tmpStat; - memset( stat, 0, sizeof(Stat) ); - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - rc = zoo_exists( mp_zkHandle, - path.c_str(), - 0, - stat ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) - { - if (rc == ZNONODE) - return false; - // Some error - std::cerr << "[zktreeutil] Error in checking existance of " << path << std::endl; - throw ZooKeeperException( string("Unable to check existence of node ") + path, rc ); - } else { - return true; - } - } - - string ZooKeeperAdapter::getNodeData(const string &path) throw(ZooKeeperException) - { - // Validate the zk path - validatePath( path ); - - const int MAX_DATA_LENGTH = 128 * 1024; - char buffer[MAX_DATA_LENGTH]; - memset( buffer, 0, MAX_DATA_LENGTH ); - struct Stat tmpStat; - struct Stat* stat = &tmpStat; - memset( stat, 0, sizeof(Stat) ); - - int rc; - int len; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - len = MAX_DATA_LENGTH - 1; - rc = zoo_get( mp_zkHandle, - path.c_str(), - 0, - buffer, &len, stat ); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) // checl return code - { - std::cerr << "[zktreeutil] Error in fetching value of " << path << std::endl; - throw ZooKeeperException( string("Unable to get data of node ") + path, rc ); - } - - // return data - return string( buffer, buffer + len ); - } - - void ZooKeeperAdapter::setNodeData(const string &path, - const string &value, - int version) throw(ZooKeeperException) - { - // Validate the zk path - validatePath( path ); - - int rc; - RetryHandler rh(m_zkConfig); - do { - verifyConnection(); - rc = zoo_set( mp_zkHandle, - path.c_str(), - value.c_str(), - value.length(), - version); - } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) // check return code - { - std::cerr << "[zktreeutil] Error in setting value of " << path << std::endl; - throw ZooKeeperException( string("Unable to set data for node ") + path, rc ); - } - // success - } - -} /* end of 'namespace zktreeutil' */ diff --git a/src/contrib/zktreeutil/src/ZkAdaptor.h b/src/contrib/zktreeutil/src/ZkAdaptor.h deleted file mode 100644 index d94b033c51b..00000000000 --- a/src/contrib/zktreeutil/src/ZkAdaptor.h +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ZK_ADAPTER_H__ -#define __ZK_ADAPTER_H__ - -#include -#include - -extern "C" { -#include "zookeeper.h" -} - -namespace zktreeutil -{ - using std::string; - using std::vector; - - /** - * \brief A cluster related exception. - */ - class ZooKeeperException : public std::exception - { - public: - - /** - * \brief Constructor. - * - * @param msg the detailed message associated with this exception - */ - ZooKeeperException(const string& msg) : - m_message(msg), - m_zkErrorCode(0) {} - - /** - * \brief Constructor. - * - * @param msg the detailed message associated with this exception - * @param errorCode the ZK error code associated with this exception - */ - ZooKeeperException(const string &msg, int errorCode) : - m_zkErrorCode(errorCode) - { - char tmp[100]; - sprintf( tmp, " (ZK error code: %d)", errorCode ); - m_message = msg + tmp; - } - - /** - * \brief Destructor. - */ - ~ZooKeeperException() throw() {} - - /** - * \brief Returns detailed description of the exception. - */ - const char *what() const throw() - { - return m_message.c_str(); - } - - /** - * \brief Returns the ZK error code. - */ - int getZKErrorCode() const - { - return m_zkErrorCode; - } - - private: - - /** - * The detailed message associated with this exception. - */ - string m_message; - - /** - * The optional error code received from ZK. - */ - int m_zkErrorCode; - - }; - - /** - * \brief This class encapsulates configuration of a ZK client. - */ - class ZooKeeperConfig - { - public: - - /** - * \brief Constructor. - * - * @param hosts the comma separated list of host and port pairs of ZK nodes - * @param leaseTimeout the lease timeout (heartbeat) - * @param autoReconnect whether to allow for auto-reconnect - * @param connectTimeout the connect timeout, in milliseconds; - */ - ZooKeeperConfig(const string &hosts, - int leaseTimeout, - bool autoReconnect = true, - long long int connectTimeout = 15000) - : m_hosts(hosts), - m_leaseTimeout(leaseTimeout), - m_autoReconnect(autoReconnect), - m_connectTimeout(connectTimeout) {} - - /** - * \brief Returns the list of ZK hosts to connect to. - */ - string getHosts() const { return m_hosts; } - - /** - * \brief Returns the lease timeout. - */ - int getLeaseTimeout() const { return m_leaseTimeout; } - - /** - * \brief Returns whether {@link ZooKeeperAdapter} should attempt - * \brief to automatically reconnect in case of a connection failure. - */ - bool getAutoReconnect() const { return m_autoReconnect; } - - /** - * \brief Gets the connect timeout. - * - * @return the connect timeout - */ - long long int getConnectTimeout() const { return m_connectTimeout; } - - private: - - /** - * The host addresses of ZK nodes. - */ - const string m_hosts; - - /** - * The ZK lease timeout. - */ - const int m_leaseTimeout; - - /** - * True if this adapater should attempt to autoreconnect in case - * the current session has been dropped. - */ - const bool m_autoReconnect; - - /** - * How long to wait, in milliseconds, before a connection - * is established to ZK. - */ - const long long int m_connectTimeout; - }; - - /** - * \brief This is a wrapper around ZK C synchrounous API. - */ - class ZooKeeperAdapter - { - public: - /** - * \brief Constructor. - * Attempts to create a ZK adapter, optionally connecting - * to the ZK. Note, that if the connection is to be established - * and the given listener is NULL, some events may be lost, - * as they may arrive asynchronously before this method finishes. - * - * @param config the ZK configuration - * @throw ZooKeeperException if cannot establish connection to the given ZK - */ - ZooKeeperAdapter(ZooKeeperConfig config) throw(ZooKeeperException); - - /** - * \brief Destructor. - */ - ~ZooKeeperAdapter(); - - /** - * \brief Returns the current config. - */ - const ZooKeeperConfig &getZooKeeperConfig() const { return m_zkConfig; } - - /** - * \brief Restablishes connection to the ZK. - * If this adapter is already connected, the current connection - * will be dropped and a new connection will be established. - * - * @throw ZooKeeperException if cannot establish connection to the ZK - */ - void reconnect() throw(ZooKeeperException); - - /** - * \brief Disconnects from the ZK and unregisters {@link #mp_zkHandle}. - */ - void disconnect(); - - /** - * \brief Creates a new node identified by the given path. - * This method will optionally attempt to create all missing ancestors. - * - * @param path the absolute path name of the node to be created - * @param value the initial value to be associated with the node - * @param flags the ZK flags of the node to be created - * @param createAncestors if true and there are some missing ancestor nodes, - * this method will attempt to create them - * - * @return true if the node has been successfully created; false otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool createNode(const string &path, - const string &value = "", - int flags = 0, - bool createAncestors = true) throw(ZooKeeperException); - - /** - * \brief Deletes a node identified by the given path. - * - * @param path the absolute path name of the node to be deleted - * @param recursive if true this method will attempt to remove - * all children of the given node if any exist - * @param version the expected version of the node. The function will - * fail if the actual version of the node does not match - * the expected version - * - * @return true if the node has been deleted; false otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool deleteNode(const string &path, - bool recursive = false, - int version = -1) throw(ZooKeeperException); - - /** - * \brief Retrieves list of all children of the given node. - * - * @param path the absolute path name of the node for which to get children - * @return the list of absolute paths of child nodes, possibly empty - * @throw ZooKeeperException if the operation has failed - */ - vector getNodeChildren( const string &path) throw(ZooKeeperException); - - /** - * \brief Check the existance of path to a znode. - * - * @param path the absolute path name of the znode - * @return TRUE if the znode exists; FALSE otherwise - * @throw ZooKeeperException if the operation has failed - */ - bool nodeExists(const string &path) throw(ZooKeeperException); - - /** - * \brief Gets the given node's data. - * - * @param path the absolute path name of the node to get data from - * - * @return the node's data - * @throw ZooKeeperException if the operation has failed - */ - string getNodeData(const string &path) throw(ZooKeeperException); - - /** - * \brief Sets the given node's data. - * - * @param path the absolute path name of the node to get data from - * @param value the node's data to be set - * @param version the expected version of the node. The function will - * fail if the actual version of the node does not match - * the expected version - * - * @throw ZooKeeperException if the operation has failed - */ - void setNodeData(const string &path, - const string &value, - int version = -1) throw(ZooKeeperException); - - /** - * \brief Validates the given path to a node in ZK. - * - * @param the path to be validated - * - * @throw ZooKeeperException if the given path is not valid - * (for instance it doesn't start with "/") - */ - static void validatePath(const string &path) throw(ZooKeeperException); - - private: - - /** - * Verifies whether the connection is established, - * optionally auto reconnecting. - * - * @throw ZooKeeperConnection if this client is disconnected - * and auto-reconnect failed or was not allowed - */ - void verifyConnection() throw(ZooKeeperException); - - private: - - /** - * The current ZK configuration. - */ - const ZooKeeperConfig m_zkConfig; - - /** - * The current ZK session. - */ - zhandle_t *mp_zkHandle; - }; - -} /* end of 'namespace zktreeutil' */ - -#endif /* __ZK_ADAPTER_H__ */ diff --git a/src/contrib/zktreeutil/src/ZkTreeUtil.cc b/src/contrib/zktreeutil/src/ZkTreeUtil.cc deleted file mode 100644 index 83f0cbf3f94..00000000000 --- a/src/contrib/zktreeutil/src/ZkTreeUtil.cc +++ /dev/null @@ -1,705 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ZkTreeUtil.h" - -#include -#include -#include -#include -#include - -namespace zktreeutil -{ - using std::map; - using std::pair; - - static ZkTreeNodeSptr loadZkTree_ (ZooKeeperAdapterSptr zkHandle, - const string& path) - { - // Extract the node value - string value = zkHandle->getNodeData(path); - - // Extract nodename from the path - string nodename = "/"; - if (path != "/") - { - vector< string > nodes; - boost::split(nodes, path, boost::is_any_of ("/") ); - nodename = nodes[nodes.size()-1]; - } - - // Create tree-node with name and value - ZkTreeNodeSptr nodeSptr = ZkTreeNodeSptr (new ZkTreeNode (nodename, value)); - std::cerr << "[zktreeutil] loaded nodename: " - << nodename - << " value: " - << value - << std::endl; - - // Load all the children - vector< string > cnodes = zkHandle->getNodeChildren (path); - for (unsigned i = 0; i < cnodes.size(); i++) - nodeSptr->addChild (loadZkTree_ (zkHandle, cnodes[i])); - - // Return the constructed node - return nodeSptr; - } - - static ZkTreeNodeSptr loadZkTreeXml_ (xmlNode* xmlNodePtr) - { - // Null check - if (xmlNodePtr == NULL) - { - std::cerr << "[zktreeutil] empty XML node encountered" << std::endl; - exit (-1); - } - - // Get the node name - xmlChar* name = xmlGetProp (xmlNodePtr, BAD_CAST "name"); - string nameStr = (const char*)name; - std::cerr << "[zktreeutil] node name: " << nameStr; - xmlFree (name); - // Get the node value - string valueStr; - xmlChar* value = xmlGetProp (xmlNodePtr, BAD_CAST "value"); - if (value) - { - valueStr = (const char*)value; - std::cerr << " value: " << valueStr; - } - xmlFree (value); - // Get the ignore flag - bool doIgnore = false; - xmlChar* ignore = xmlGetProp (xmlNodePtr, BAD_CAST "ignore"); - if (ignore) - { - string ignoreStr = (const char*) ignore; - if (ignoreStr == "true" || ignoreStr == "yes" || ignoreStr == "1") - { - doIgnore = true; - std::cerr << " "; - } - } - xmlFree (ignore); - std::cerr << std::endl; - - // Create the zk node - ZkTreeNodeSptr nodeSptr = - ZkTreeNodeSptr (new ZkTreeNode (nameStr, - ZkNodeData (valueStr, doIgnore))); - - // Load the children - for (xmlNode* chldNode = xmlNodePtr->children; - chldNode; - chldNode = chldNode->next) - if (chldNode->type == XML_ELEMENT_NODE) - nodeSptr->addChild (loadZkTreeXml_ (chldNode)); - - // Return the loaded node - return nodeSptr; - } - - static void writeZkTree_ (ZooKeeperAdapterSptr zkHandle, - const ZkTreeNodeSptr zkNodeSptr, - const string& path) - { - // Create the path in zk-tree - zkHandle->createNode(path.c_str(), "", 0, false); - std::cerr << "[zktreeutil] created key: " << path << std::endl; - // Set value for the path - string value = zkNodeSptr->getData().value; - if (value != "") - { - zkHandle->setNodeData (path.c_str(), value.c_str()); - std::cerr << "[zktreeutil] set value: " << std::endl; - } - - // Go deep to write the subtree rooted in the node, if not to be ignored - if (!(zkNodeSptr->getData().ignoreUpdate)) - { - for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) - { - ZkTreeNodeSptr childNodeSptr = zkNodeSptr->getChild (i); - // Add the node name into the path and write in zk-tree - string cpath = ((path != "/")? path : "") - + string("/") - + childNodeSptr->getKey(); - writeZkTree_ (zkHandle, childNodeSptr, cpath); - } - } - - return; - } - - static void addTreeZkAction_ (const ZkTreeNodeSptr zkNodeSptr, - const string& path, - vector< ZkAction >& actions) - { - // Create the key - actions.push_back (ZkAction (ZkAction::CREATE, path)); - - // Set value for the new key - if (zkNodeSptr->getData().value != "") - actions.push_back (ZkAction (ZkAction::VALUE, - path, - zkNodeSptr->getData().value)); - - // Add all the children - for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) - { - ZkTreeNodeSptr childSptr = zkNodeSptr->getChild (i); - string cpath = path + string("/") + childSptr->getKey(); - addTreeZkAction_ (childSptr, cpath, actions); - } - - return; - } - - static xmlNodePtr dumpZkTreeXml_ (const ZkTreeNodeSptr zkNodeSptr) - { - // Create xml node with zknode name and value - string nodename = zkNodeSptr->getKey (); - string value = zkNodeSptr->getData().value; - xmlNodePtr node = xmlNewNode(NULL, BAD_CAST "zknode"); - xmlNewProp (node, BAD_CAST "name", BAD_CAST nodename.c_str()); - if (value.length()) - xmlNewProp (node, BAD_CAST "value", BAD_CAST value.c_str()); - - // Add all the children rotted at this node - for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) - xmlAddChild (node, dumpZkTreeXml_ (zkNodeSptr->getChild (i))); - - // Return xml node - return node; - } - - static void dumpZkTree_ (const ZkTreeNodeSptr zkNodeSptr, - int maxLevel, - int level, - vector< bool >& masks) - { - // Check the max. dlevel to be dumped - if (level > maxLevel) - return; - - - // Create branch - for (int i=0; i < level; i++) - { - if ( i== level-1) std::cout << "| "; - else if (masks[i]) std::cout << " "; - else std::cout << "| "; - } - std::cout << std::endl; - for (int i=0; i < level-1; i++) - { - if (masks[i]) std::cout << " "; - else std::cout << "| "; - } - - // Dump the node name and value - std::cout << "|--[" << zkNodeSptr->getKey(); - if (zkNodeSptr->getData().value != "") - std::cout << " => " << zkNodeSptr->getData().value; - std::cout << "]" << std::endl; - - // Dump all the children - for (unsigned i=0; i < zkNodeSptr->numChildren(); i++) - { - // Add mask for last child - if (i == zkNodeSptr->numChildren()-1) - masks.push_back(true); - else - masks.push_back(false); - dumpZkTree_ (zkNodeSptr->getChild (i), maxLevel, level+1, masks); - } - - masks.pop_back(); - return; - } - - static ZkTreeNodeSptr traverseBranch_ (const ZkTreeNodeSptr& zkRootSptr, - const string& path) - { - // Check if the tree is loaded into memory - if (zkRootSptr == NULL) - { - string errMsg = "[zktreeutil] null root passed for traversing"; - std::cout << errMsg << std::endl; - throw std::logic_error (errMsg); - } - - // Split the path and add intermediate znodes - vector< string > nodes; - boost::split(nodes, path, boost::is_any_of ("/") ); - - // Start traversing the tree - ZkTreeNodeSptr currNodeSptr = zkRootSptr; - for (unsigned znode_idx = 1; znode_idx < nodes.size(); znode_idx++) - { - bool found = false; - for (unsigned i=0; i < currNodeSptr->numChildren(); i++) - { - ZkTreeNodeSptr childNodeSptr = currNodeSptr->getChild(i); - if (childNodeSptr->getKey() == nodes[znode_idx]) - { - // Found! go to the znode - currNodeSptr = childNodeSptr; - found = true; - break; - } - } - if (!found) // No such znode found; return NULL node-ptr - { - string errMsg = string("[zktreeutil] unknown znode during traversal: ") - + nodes[znode_idx]; - std::cout << errMsg << std::endl; - throw std::logic_error (errMsg); - } - } - - return currNodeSptr; - } - - static ZkTreeNodeSptr createAncestors_ (const string& path) - { - // Create the root znode - ZkTreeNodeSptr zkRootSptr = ZkTreeNodeSptr (new ZkTreeNode ("/")); - ZkTreeNodeSptr currNodeSptr = zkRootSptr; - // Split the path and add intermediate znodes - vector< string > nodes; - boost::split(nodes, path, boost::is_any_of ("/") ); - for (unsigned i=1; i < nodes.size()-1; i++) - { - ZkTreeNodeSptr childNodeSptr = ZkTreeNodeSptr (new ZkTreeNode (nodes[i])); - currNodeSptr->addChild (childNodeSptr); - currNodeSptr = childNodeSptr; - } - - //Return the root of the branch - return zkRootSptr; - } - - ZooKeeperAdapterSptr ZkTreeUtil::get_zkHandle (const string& zkHosts) - { - try - { - // Create an instance of ZK adapter. - ZooKeeperConfig config (zkHosts, 10000); - ZooKeeperAdapterSptr zkHandleSptr = - ZooKeeperAdapterSptr (new ZooKeeperAdapter (config)); - return zkHandleSptr; - } - catch (const ZooKeeperException &e) - { - std::cerr << "[zktreeutil] zooKeeper exception caught: " - << e.what() - << std::endl; - throw; - } - catch (std::exception &stde) - { - std::cerr << "[zktreeutil] standard exception caught: " - << stde.what() - << std::endl; - throw; - } - catch (...) - { - std::cerr - << "[zktreeutil] unknown exception while connecting to zookeeper" - << std::endl; - throw; - } - } - - - void ZkTreeUtil::loadZkTree (const string& zkHosts, - const string& path, - bool force) - { - // Check if already loaded - if (loaded_ && !force) - { - std::cerr << "[zktreeutil] zk-tree already loaded into memory" - << std::endl; - return; - } - - // Connect to ZK server - ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); - std::cerr << "[zktreeutil] connected to ZK serverfor reading" - << std::endl; - - // Check the existance of the path to znode - if (!zkHandle->nodeExists (path)) - { - string errMsg = string("[zktreeutil] path does not exists : ") + path; - std::cout << errMsg << std::endl; - throw std::logic_error (errMsg); - } - - // Load the rooted (sub)tree - ZkTreeNodeSptr zkSubrootSptr = loadZkTree_ (zkHandle, path); - - // Create the ancestors before loading the rooted subtree - if (path != "/") - { - zkRootSptr_ = createAncestors_(path); - string ppath = path.substr (0, path.rfind('/')); - ZkTreeNodeSptr parentSptr = traverseBranch_( zkRootSptr_, ppath); - parentSptr->addChild (zkSubrootSptr); - } - else // Loaded entire zk-tree - { - zkRootSptr_ = zkSubrootSptr; - } - - // Set load flag - loaded_ = true; - return; - } - - void ZkTreeUtil::loadZkTreeXml (const string& zkXmlConfig, - bool force) - { - // Check if already loaded - if (loaded_ && !force) - { - std::cerr << "[zktreeutil] zk-tree already loaded into memory" - << std::endl; - return; - } - - // Parse the file and get the DOM - xmlDocPtr docPtr = xmlReadFile(zkXmlConfig.c_str(), NULL, 0); - if (docPtr == NULL) { - std::cerr << "[zktreeutil] could not parse XML file " - << zkXmlConfig - << std::endl; - exit (-1); - } - std::cerr << "[zktreeutil] zk-tree XML parsing successful" - << std::endl; - - // Get the root element node - xmlNodePtr rootPtr = xmlDocGetRootElement(docPtr); - // Create the root zk node - zkRootSptr_ = ZkTreeNodeSptr (new ZkTreeNode ("/")); - // Load the rooted XML tree - for (xmlNode* chldNode = rootPtr->children; - chldNode; - chldNode = chldNode->next) - { - if (chldNode->type == XML_ELEMENT_NODE) - zkRootSptr_->addChild (loadZkTreeXml_ (chldNode)); - } - - // set oad flag - loaded_ = true; - // Cleanup stuff - xmlFreeDoc(docPtr); - xmlCleanupParser(); - return; - } - - void ZkTreeUtil::writeZkTree (const string& zkHosts, - const string& path, - bool force) const - { - // Connect to ZK server - ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); - std::cerr << "[zktreeutil] connected to ZK server for writing" - << std::endl; - - // Go to the rooted subtree - ZkTreeNodeSptr zkRootSptr = traverseBranch_ (zkRootSptr_, path); - - // Cleanup before write if forceful write enabled - if (force) - { - if (path != "/") // remove the subtree rooted at the znode - { - // Delete the subtree rooted at the znode before write - if (zkHandle->nodeExists (path)) - { - std::cerr << "[zktreeutil] deleting subtree rooted at " - << path - << "..." - << std::endl; - zkHandle->deleteNode (path, true); - } - } - else // remove the rooted znodes - { - std::cerr << "[zktreeutil] deleting rooted zk-tree" - << "..." - << std::endl; - // Get the root's children - vector< string > cnodes = zkHandle->getNodeChildren ("/"); - for (unsigned i=0; i < cnodes.size(); i++) - { - if ( cnodes[i] != "/zookeeper") // reserved for zookeeper use - zkHandle->deleteNode(cnodes[i], true); - } - } - } - - // Start tree construction - writeZkTree_ (zkHandle, zkRootSptr, path); - return; - } - - void ZkTreeUtil::dumpZkTree (bool xml, int depth) const - { - if (xml) - { - // Creates a new document, a node and set it as a root node - xmlDocPtr docPtr = xmlNewDoc(BAD_CAST "1.0"); - xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "root"); - xmlDocSetRootElement(docPtr, rootNode); - - // Add all the rooted children - for (unsigned i=0; i < zkRootSptr_->numChildren(); i++) - xmlAddChild (rootNode, dumpZkTreeXml_ (zkRootSptr_->getChild (i))); - - // Dumping document to stdio or file - xmlSaveFormatFileEnc("-", docPtr, "UTF-8", 1); - - // Cleanup stuff - xmlFreeDoc(docPtr); - xmlCleanupParser(); - return; - } - - // Dump text - std::cout << "/" << std::endl; - vector< bool > masks; - for (unsigned i=0; i < zkRootSptr_->numChildren(); i++) - { - if (i == zkRootSptr_->numChildren()-1) - masks.push_back(true); - else - masks.push_back(false); - dumpZkTree_ (zkRootSptr_->getChild (i), depth, 1, masks); - } - - return; - } - - vector< ZkAction > ZkTreeUtil::diffZkTree (const string& zkHosts, - const string& path) const - { - // Action container - vector< ZkAction > actions; - - if (!loaded_) - { - std::cout << "[zktreeutil] zk-tree not loaded for diff" - << std::endl; - exit (-1); - } - - // Load the rooted subtree from zookeeper - ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); - std::cerr << "[zktreeutil] connected to ZK server for reading" - << std::endl; - ZkTreeNodeSptr zkLiveRootSptr = loadZkTree_ (zkHandle, path); - - // Go to the saved rooted subtree - ZkTreeNodeSptr zkLoadedRootSptr = - traverseBranch_ (zkRootSptr_, path); - - // Check the root value first - if (zkLoadedRootSptr->getData().value - != zkLiveRootSptr->getData().value) - { - actions.push_back (ZkAction (ZkAction::VALUE, - path, - zkLoadedRootSptr->getData().value, - zkLiveRootSptr->getData().value)); - } - - // Start traversal from root - vector< string > ppaths; - vector< pair< ZkTreeNodeSptr, ZkTreeNodeSptr > > commonNodes; - ppaths.push_back ((path != "/")? path : ""); - commonNodes.push_back (pair< ZkTreeNodeSptr, ZkTreeNodeSptr > - (zkLoadedRootSptr, zkLiveRootSptr)); - - for (unsigned j=0; j < commonNodes.size(); j++) - { - // Get children of loaded tree - map< string, ZkTreeNodeSptr > loadedChildren; - for (unsigned i=0; i < commonNodes[j].first->numChildren(); i++) - { - ZkTreeNodeSptr childSptr = commonNodes[j].first->getChild (i); - loadedChildren[childSptr->getKey()] = childSptr; - } - // Get children of live tree - map< string, ZkTreeNodeSptr > liveChildren; - for (unsigned i=0; i < commonNodes[j].second->numChildren(); i++) - { - ZkTreeNodeSptr childSptr = commonNodes[j].second->getChild (i); - liveChildren[childSptr->getKey()] = childSptr; - } - - // Start comparing the children - for (map< string, ZkTreeNodeSptr >::const_iterator it = - loadedChildren.begin(); - it != loadedChildren.end(); - it++) - { - bool ignoreKey = it->second->getData().ignoreUpdate; - string loadedVal = it->second->getData().value; - // Path to this node - string path = ppaths[j] + string("/") + it->first; - - map< string, ZkTreeNodeSptr >::const_iterator jt = - liveChildren.find (it->first); - if (jt != liveChildren.end()) - { - // Key is present in live zk-tree - string liveVal = jt->second->getData().value; - // Check value for the key, if not ignored - if (!ignoreKey) - { - if (loadedVal != liveVal) - { - // Value differs, set the new value for the key - actions.push_back (ZkAction (ZkAction::VALUE, - path, - loadedVal, - liveVal)); - } - - // Add node to common nodes - ppaths.push_back (path); - commonNodes.push_back (pair< ZkTreeNodeSptr, ZkTreeNodeSptr > - (it->second, jt->second)); - } - - // Remove the live zk node - liveChildren.erase (it->first); - } - else - { - // Add the subtree rooted to this node, if not ignored - if (!ignoreKey) - addTreeZkAction_ (it->second, path, actions); - } - } - - // Remaining live zk nodes to be deleted - for (map< string, ZkTreeNodeSptr >::const_iterator it = liveChildren.begin(); - it != liveChildren.end(); it++) - { - string path = ppaths[j] + string("/") + it->first; - actions.push_back (ZkAction (ZkAction::DELETE, path)); - } - } - // return the diff actions - return actions; - } - - void ZkTreeUtil::executeZkActions (const string& zkHosts, - const vector< ZkAction >& zkActions, - int execFlags) const - { - // Execute the diff zk actions - if (zkActions.size()) - { - // Connect to Zookeeper for writing - ZooKeeperAdapterSptr zkHandleSptr; - if ((execFlags & EXECUTE) - || (execFlags & INTERACTIVE)) - { - zkHandleSptr = get_zkHandle (zkHosts); - std::cerr << "[zktreeutil] connected to ZK server for writing" - << std::endl; - } - - for (unsigned i=0; i < zkActions.size(); i++) - { - if (zkActions[i].action == ZkAction::CREATE) - { - if (execFlags & PRINT) - std::cout << "CREAT- key:" << zkActions[i].key << std::endl; - if (execFlags & EXECUTE) - { - if (execFlags & INTERACTIVE) - { - string resp; - std::cout << "Execute this action?[yes/no]: "; - std::getline(std::cin, resp); - if (resp != "yes") - continue; - } - zkHandleSptr->createNode(zkActions[i].key.c_str(), "", 0, false); - } - } - else if (zkActions[i].action == ZkAction::DELETE) - { - if (execFlags & PRINT) - std::cout << "DELET- key:" << zkActions[i].key << std::endl; - if (execFlags & EXECUTE) - { - if (execFlags & INTERACTIVE) - { - string resp; - std::cout << "Execute this action?[yes/no]: "; - std::getline(std::cin, resp); - if (resp != "yes") - continue; - } - zkHandleSptr->deleteNode(zkActions[i].key.c_str(), true); - } - } - else if (zkActions[i].action == ZkAction::VALUE) - { - if (execFlags & PRINT) - { - std::cout << "VALUE- key:" - << zkActions[i].key - << " value:" << zkActions[i].newval; - if (zkActions[i].oldval != "") - std::cout << " old_value:" << zkActions[i].oldval; - std::cout << std::endl; - } - if (execFlags & EXECUTE) - { - if (execFlags & INTERACTIVE) - { - string resp; - std::cout << "Execute this action?[yes/no]: "; - std::getline(std::cin, resp); - if (resp != "yes") - continue; - } - zkHandleSptr->setNodeData (zkActions[i].key, zkActions[i].newval); - } - } - } - } - - return; - } - -} - diff --git a/src/contrib/zktreeutil/src/ZkTreeUtil.h b/src/contrib/zktreeutil/src/ZkTreeUtil.h deleted file mode 100644 index 0a9be03f842..00000000000 --- a/src/contrib/zktreeutil/src/ZkTreeUtil.h +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __ZK_TREE_UTIL_H__ -#define __ZK_TREE_UTIL_H__ - -#include -#include -#include "SimpleTree.h" -#include "ZkAdaptor.h" - -namespace zktreeutil -{ - -#define ZKTREEUTIL_INF 1000000000 - /** - * \brief A structure containing ZK node data. - */ - struct ZkNodeData - { - /** - * \brief The value string of the ZK node. - */ - string value; - - /** - * \brief The flag indicating whether children of the - * \brief node shduld be ignored during create/diff/update - */ - bool ignoreUpdate; - - /** - * \brief Constructor. - * - * @param val the value string - * @param ignore the flag indicating ignore any update/diff - */ - ZkNodeData (const string& val, bool ignore=false) - : value (val), ignoreUpdate (ignore) {} - - /** - * \brief Constructor. - * - * @param ignore the flag indicating ignore any update/diff - */ - ZkNodeData (bool ignore=false) - : ignoreUpdate (ignore) {} - }; - - /** - * \brief The type representing a ZK Treenode - */ - typedef SimpleTreeNode< string, ZkNodeData > ZkTreeNode; - - /** - * \brief The type representing a ZK Treenode smart-pointer - */ - typedef boost::shared_ptr< ZkTreeNode > ZkTreeNodeSptr; - - /** - * \brief The type representing a ZK Adapter smart-pointer - */ - typedef boost::shared_ptr< ZooKeeperAdapter > ZooKeeperAdapterSptr; - - /** - * \brief A structure defining a particular action on ZK node; - * \brief the action can be any of - - * \brief CREAT- : creates recussively - * \brief DELET- : deletes recursively - * \brief VALUE- : sets to - */ - struct ZkAction - { - /** - * \brief The action type; any of create/delete/setvalue. - */ - enum ZkActionType - { - NONE, - CREATE, - DELETE, - VALUE, - }; - - /** - * \brief action of this instance - */ - ZkActionType action; - - /** - * \brief ZK node key - */ - string key; - - /** - * \brief value to be set, if action is setvalue - */ - string newval; - - /** - * \brief existing value of the ZK node key - */ - string oldval; - - /** - * \brief Constructor. - */ - ZkAction () - : action (ZkAction::NONE) {} - - /** - * \brief Constructor. - * - * @param act the action to be taken - * @param k the key on which action to be taken - */ - ZkAction (ZkActionType act, const string& k) - : action(act), - key(k) {} - - /** - * \brief Constructor. - * - * @param act the action to be taken - * @param k the key on which action to be taken - * @param v the value of the ZK node key - */ - ZkAction (ZkActionType act, const string& k, const string& v) - : action(act), - key(k), - newval(v) {} - - /** - * \brief Constructor. - * - * @param act the action to be taken - * @param k the key on which action to be taken - * @param nv the new value of the ZK node key - * @param ov the old value of the ZK node key - */ - ZkAction (ZkActionType act, const string& k, const string& nv, const string& ov) - : action (act), - key(k), - newval(nv), - oldval(ov) {} - }; - - /** - * \brief The ZK tree utility class; supports loading ZK tree from ZK server OR - * \brief from saved XML file, saving ZK tree into XML file, dumping the ZK tree - * \brief on standard output, creting a diff between saved ZK tree and live ZK - * \brief tree and incremental update of the live ZK tree. - */ - class ZkTreeUtil - { - public: - /** - * \brief Execution flag on ZkAction - */ - enum ZkActionExecuteFlag - { - NONE = 0, - PRINT = 1, - EXECUTE = 2, - INTERACTIVE = 5, - }; - - public: - /** - * \brief Connects to zookeeper and returns a valid ZK handle - * - * @param zkHosts comma separated list of host:port forming ZK quorum - * @param a valid ZK handle - */ - static ZooKeeperAdapterSptr get_zkHandle (const string& zkHosts); - - - public: - /** - * \brief Constructor. - */ - ZkTreeUtil () : loaded_(false) {} - - /** - * \brief loads the ZK tree from ZK server into memory - * - * @param zkHosts comma separated list of host:port forming ZK quorum - * @param path path to the subtree to be loaded into memory - * @param force forces reloading in case tree already loaded into memory - */ - void loadZkTree (const string& zkHosts, const string& path="/", bool force=false); - - /** - * \brief loads the ZK tree from XML file into memory - * - * @param zkXmlConfig ZK tree XML file - * @param force forces reloading in case tree already loaded into memory - */ - void loadZkTreeXml (const string& zkXmlConfig, bool force=false); - - /** - * \brief writes the in-memory ZK tree on to ZK server - * - * @param zkHosts comma separated list of host:port forming ZK quorum - * @param path path to the subtree to be written to ZK tree - * @param force forces cleanup of the ZK tree on the ZK server before writing - */ - void writeZkTree (const string& zkHosts, const string& path="/", bool force=false) const; - - /** - * \brief dupms the in-memory ZK tree on the standard output device; - * - * @param xml flag indicates whether tree should be dumped in XML format - * @param depth the depth of the tree to be dumped for non-xml dump - */ - void dumpZkTree (bool xml=false, int depth=ZKTREEUTIL_INF) const; - - /** - * \brief returns a list of actions after taking a diff of in-memory - * \brief ZK tree and live ZK tree. - * - * @param zkHosts comma separated list of host:port forming ZK quorum - * @param path path to the subtree in consideration while taking diff with ZK tree - * @return a list of ZKAction instances to be performed on live ZK tree - */ - vector< ZkAction > diffZkTree (const string& zkHosts, const string& path="/") const; - - /** - * \brief performs create/delete/setvalue by executing a set of - * ZkActions on a live ZK tree. - * - * @param zkHosts comma separated list of host:port forming ZK quorum - * @param zkActions set of ZkActions - * @param execFlags flags indicating print/execute/interactive etc - */ - void executeZkActions (const string& zkHosts, - const vector< ZkAction >& zkActions, - int execFlags) const; - - private: - - ZkTreeNodeSptr zkRootSptr_; // ZK tree root node - bool loaded_; // Falg indicating whether ZK tree loaded into memory - }; -} - -#endif // __ZK_TREE_UTIL_H__ diff --git a/src/contrib/zktreeutil/src/ZkTreeUtilMain.cc b/src/contrib/zktreeutil/src/ZkTreeUtilMain.cc deleted file mode 100644 index 8afebf6e2f7..00000000000 --- a/src/contrib/zktreeutil/src/ZkTreeUtilMain.cc +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -#include -#include "ZkTreeUtil.h" - -using namespace zktreeutil; - -// The set of "long" options accepted by this program. -static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"import", no_argument, 0, 'I'}, - {"export", no_argument, 0, 'E'}, - {"update", no_argument, 0, 'U'}, - {"diff", no_argument, 0, 'F'}, - {"dump", no_argument, 0, 'D'}, - {"force", no_argument, 0, 'f'}, - {"xmlfile", required_argument, 0, 'x'}, - {"path", required_argument, 0, 'p'}, - {"depth", required_argument, 0, 'd'}, - {"zookeeper", required_argument, 0, 'z'}, - {0, 0, 0, 0} -}; -static char *short_options = "IEUFDfx:p:d:hz:"; - -static void usage(int argc, char *argv[]) -{ - std::cout << "ZK-tree utility for managing ZK-tree with XML import/export," << std::endl; - std::cout << "viewing diff between live and saved ZK-tree and performing" << std::endl; - std::cout << "incremental update of the same." << std::endl; - std::cout << "Usage: " << argv[0] << " [args-and-values]+" << std::endl; - std::cout - << "\t--import or -I: " - << std::endl - << "\t Imports the zookeeper tree from XML file. Must be specified with" - << std::endl - << "\t --zookeeper AND --xmlfile options. Optionally takes --path for" - << std::endl - << "\t importing subtree" - << std::endl; - std::cout - << "\t--export or -E: " - << std::endl - << "\t Exports the zookeeper tree to XML file. Must be specified with" - << std::endl - << "\t --zookeeper option. Optionally takes --path for exporting subtree" - << std::endl; - std::cout - << "\t--update or -U: " - << std::endl - << "\t Updates zookeeper tree with changes from XML file. Update operation" - << std::endl - << "\t is interactive unless specified with --force option. Must be speci-" - << std::endl - << "\t fied with --zookeeper AND --xmlfile options. Optionally takes --path" - << std::endl - << "\t for updating subtree." - << std::endl; - std::cout - << "\t--diff or -F: " - << std::endl - << "\t Creates a list of diff actions on ZK tree based on XML data. Must" - << std::endl - << "\t be specified with --zookeeper OR --xmlfile options. Optionally takes" - << std::endl - << "\t --path for subtree diff" - << std::endl; - std::cout - << "\t--dump or -D: " - << std::endl - << "\t Dumps the entire ZK (sub)tree to standard output. Must be specified" - << std::endl - << "\t with --zookeeper OR --xmlfile options. Optionally takes --path and" - << std::endl - << "\t --depth for dumping subtree." - << std::endl; - std::cout - << "\t--xmlfile= or -x : " - << std::endl - << "\t Zookeeper tree-data XML file." - << std::endl; - std::cout - << "\t--path= or -p : " - << std::endl - << "\t Path to the zookeeper subtree rootnode." - << std::endl; - std::cout - << "\t--depth= or -d : " - << std::endl - << "\t Depth of the ZK tree to be dumped (ignored for XML dump)." - << std::endl; - std::cout - << "\t--force or -f: Forces cleanup before import; also used for forceful" - << std::endl - << "\t update. Optionally be specified with --import and --update." - << std::endl; - std::cout - << "\t--help or -h: " - << std::endl - << "\t prints this message" - << std::endl; - std::cout - << "\t--zookeeper= or -z : " - << std::endl - << "\t specifies information to connect to zookeeper." - << std::endl; -} - -int main(int argc, char **argv) -{ - if (argc == 1) { - usage(argc, argv); - exit(0); - } - - // Parse the arguments. - int op = 0; - bool force = false; - string zkHosts; - string xmlFile; - string path = "/"; - int depth = 0; - while (1) - { - int c = getopt_long(argc, argv, short_options, long_options, 0); - if (c == -1) - break; - - switch (c) { - case 'I': op = c; - break; - case 'E': op = c; - break; - case 'U': op = c; - break; - case 'F': op = c; - break; - case 'D': op = c; - break; - case 'f': force = true; - break; - case 'x': xmlFile = optarg; - break; - case 'p': path = optarg; - break; - case 'd': depth = atoi (optarg); - break; - case 'z': zkHosts = optarg; - break; - case 'h': usage (argc, argv); - exit(0); - } - } - - ZkTreeUtil zkTreeUtil; - switch (op) - { - case 'I': { - if (zkHosts == "" || xmlFile == "") - { - std::cout << "[zktreeutil] missing params; please see usage" << std::endl; - exit (-1); - } - zkTreeUtil.loadZkTreeXml (xmlFile); - zkTreeUtil.writeZkTree (zkHosts, path, force); - std::cout << "[zktreeutil] import successful!" << std::endl; - break; - } - case 'E': { - if (zkHosts == "") - { - std::cout << "[zktreeutil] missing params; please see usage" << std::endl; - exit (-1); - } - zkTreeUtil.loadZkTree (zkHosts, path); - zkTreeUtil.dumpZkTree (true); - break; - } - case 'U': { - if (zkHosts == "" || xmlFile == "") - { - std::cout << "[zktreeutil] missing params; please see usage" << std::endl; - exit (-1); - } - zkTreeUtil.loadZkTreeXml (xmlFile); - vector< ZkAction > zkActions = zkTreeUtil.diffZkTree (zkHosts, path); - int flags = ZkTreeUtil::EXECUTE; - if (!force) flags |= ZkTreeUtil::INTERACTIVE; - zkTreeUtil.executeZkActions (zkHosts, zkActions, flags); - std::cout << "[zktreeutil] update successful!" << std::endl; - break; - } - case 'F': { - if (zkHosts == "" || xmlFile == "") - { - std::cout << "[zktreeutil] missing params; please see usage" << std::endl; - exit (-1); - } - zkTreeUtil.loadZkTreeXml (xmlFile); - vector< ZkAction > zkActions = zkTreeUtil.diffZkTree (zkHosts, path); - zkTreeUtil.executeZkActions (zkHosts, zkActions, ZkTreeUtil::PRINT); - break; - } - case 'D': { - if (zkHosts != "") - zkTreeUtil.loadZkTree (zkHosts, path); - else if (xmlFile != "") - zkTreeUtil.loadZkTreeXml (xmlFile); - else - { - std::cout << "[zktreeutil] missing params; please see usage" << std::endl; - exit (-1); - } - // Dump the ZK tree - if (depth) zkTreeUtil.dumpZkTree (false, depth); - else zkTreeUtil.dumpZkTree (false); - break; - } - } - - exit(0); -} - diff --git a/src/contrib/zktreeutil/tests/zk_sample.xml b/src/contrib/zktreeutil/tests/zk_sample.xml deleted file mode 100644 index 6e97daa5fcb..00000000000 --- a/src/contrib/zktreeutil/tests/zk_sample.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/zooinspector/NOTICE.txt b/src/contrib/zooinspector/NOTICE.txt deleted file mode 100644 index 059bdc117f9..00000000000 --- a/src/contrib/zooinspector/NOTICE.txt +++ /dev/null @@ -1,3 +0,0 @@ -This contrib module includes icons available under the Eclipse Public Licence Version 1.0 -. from the Eclipse Java Devlopment Platform. -The lib sub-directory includes a binary only jar library developed at http://sourceforge.net/projects/jtoaster/ \ No newline at end of file diff --git a/src/contrib/zooinspector/README.txt b/src/contrib/zooinspector/README.txt deleted file mode 100644 index 4868e3a8e47..00000000000 --- a/src/contrib/zooinspector/README.txt +++ /dev/null @@ -1,94 +0,0 @@ -========================================== -ZooInspector - Browser and Editor for ZooKeeper Instances -Author: Colin Goodheart-Smithe -Date: February 2010 -========================================== - -ZooInspector is a Java Swing based application for browsing and editing ZooKeeper instances. - -Contents --------- - - Features - - Pre-requisites - - Build Instructions - - Using ZooInspector - - Creating and Using Plugins - -Features --------- - Below is a list of features in the current release of ZooInspector. - - Load connection settings from a zookeeper properties file - - Plugable DataEncryptionManagers to specify how data should be encrypted and decrypted in the Zookeeper instance - - Browseable tree view of the ZooKeeper instance - - View the data in a node - - View the ACL's currently applied to a node - - View the metadata for a node (Version, Number of Children, Last modified Tiem, etc.) - - Plugable NodeViewers interface - - Ability to save/load and set default Node Viewers - -Pre-requisites --------------- - - The main zookeeper build script must have been run before building this module - -Build Instructions ------------------- - 1. Open a command line. - 2. cd into this directory - 3. Run command: ant - 4. ZooInspector will be built to ../../../build/contrib/ZooInspector - 5. Copy zookeeper-3.x.x.jar into the lib sub-directory (if you are using zookeeper-3.3.0.jar it will have been - copied to this directory during the build - 6. By default the zookeeper.cmd and zookeeper.sh files expect zookeeper-3.3.0.jar. If you are using another version - you will need to change these files to point to the zookeeper-3.x.x.jar you copied to the lib directory - 7. To run ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux). If you are using - zookeeper-3.3.0.jar and do not require any classpath changes you can run the zookeeper-dev-ZooInspector.jar - directly - -Using ZooInspector ------------------- - To start ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux). If you are using - zookeeper-3.3.0.jar and do not require any classpath changes you can run the zookeeper-dev-ZooInspector.jar - directly. - - Click the play button on the toolbar to bring up the connection dialog. From here you can enter connection - information for your zookeeper instance. You can also load the connection properties from a file. This file can - have the format as a normal zookeeper properties file (i.e. hosts and timeout key-value pairs) and van optional have - an encryptionManager key-value pair to specify the DataEncryptionManager to use for this connection - (DataEncryptionManagers are explained in further detail in the 'Creating and Using Plugins' section below). You can - also set the entered information as the defaults so that when you first start ZooInspector these settings are - automatically loaded into this dialog. Pressing the OK button with connect to your ZooKeeper instance and show the - current node tree on the left of the main panel. - - Clicking a node in the node tree will load the data for that node into the node viewers. Three node viewers are - currently distributed with ZooInspector: - 1. Node Data - This enables you to see the data current stored on that node. This data can be modified and - saved. The data is decrypted and encrypted using the DataEncryptionManager specified on the connection - dialog. - 2. Node Metadata - This enables you to see the metadata associiated with this node. This is Essentially the data - obtained from the Stat object for this node. - 3. Node ACLs - This allows you to see the ACLs currently applied to this node. Currently there is no ability - to change the ACLs on a node, but it is a feature I would like to add. - Other custom Node Viewers can be added, this is explained in the 'Creating and Using Plugins' section below. - - -Creating and Using Plugins --------------------------- - There are two types of plugin which can be used with ZooInspector: - 1. DataEncryptionManager - This specifies how data should be encrypted and decrypted when working with a - zookeeper instance. - 2. ZooInspectorNodeViewer - This is a GUI panel which provides a view of visualisation on a node. - More information on these interfaces can be found in the javadocs for this module. - - To use a plugin in ZooInspector, build the plugin to a jar and copy the jar to the lib sub-directory. Edit the - zooInspector.cmd and/or zooInspector.sh files to include your new jar on the classpath and run ZooInspector. - - For DataEncryptionManagers, click the play button to open the connection dialog and enter the full class name of - your DataEncryptionManager in the 'Data Encryption Manager' field. You can make this Data Encryption Manager the - default by clicking 'Set As Default'. Click the 'OK' button to instantiate and use your plugin. - - For ZooInspectorNodeViewers, Click the 'Change Node Viewers' button on the toolbar (looks like a tree with a pencil) - and enter the full classname for your Node Viewer in the field left of the 'Add' button, then click the 'Add' - button. The Node Viewer will be instantiated and should appear in the list. You can change the order of the Node - viewers by clicking the up and dpwn buttons and delete a Node Viewer by clicking the delete button. You can save - to configuration to a file or set it as the default if necessary. Then click the 'OK' button and your Node Viewer - should appear in the tabs on the right of the main panel. \ No newline at end of file diff --git a/src/contrib/zooinspector/build.xml b/src/contrib/zooinspector/build.xml deleted file mode 100644 index bf757d7638a..00000000000 --- a/src/contrib/zooinspector/build.xml +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tests failed! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/contrib/zooinspector/config/defaultConnectionSettings.cfg b/src/contrib/zooinspector/config/defaultConnectionSettings.cfg deleted file mode 100644 index c22c70c7b79..00000000000 --- a/src/contrib/zooinspector/config/defaultConnectionSettings.cfg +++ /dev/null @@ -1,5 +0,0 @@ -#Default connection for ZooInspector -#Sun Feb 28 14:46:55 GMT 2010 -hosts=localhost\:2181 -encryptionManager=org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager -timeout=5000 diff --git a/src/contrib/zooinspector/config/defaultNodeVeiwers.cfg b/src/contrib/zooinspector/config/defaultNodeVeiwers.cfg deleted file mode 100644 index cc580f62653..00000000000 --- a/src/contrib/zooinspector/config/defaultNodeVeiwers.cfg +++ /dev/null @@ -1,3 +0,0 @@ -org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerData -org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerMetaData -org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerACL diff --git a/src/contrib/zooinspector/icons/edtsrclkup_co.gif b/src/contrib/zooinspector/icons/edtsrclkup_co.gif deleted file mode 100644 index 94eedf6f92944ef56a7165d98ee6482ad24d00f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmZ?wbhEHb6krfwIKsdXQ?+X1(&P7^zE^TizkPY(?k!y(Zmf8Dr0(0Z6O(Jgr_{$z zZA_S45!jz;(U)n_pJO+nzamx|{omhdZ+pPy1w_LK}lAnr-`SJ62+pK*(^ACOa@_X{KW3$$r zUbOlAiXE5N?74dH#gDtszu$lH{mGl3&)@xg`{~!`Z@=#VMPB~6_u~7*S3h2T`1$R} z?`Q9Re)#(P)3@JWfBgRb^LKTLe^s%6bxA;7sb4jaQ5?`-<Y72H(vk7Xs us!1$fvP8{QU92ZrK%7tARasP&f6JDw8m_8J3W|I7DyXXX9C3DJum%7=h^`F) diff --git a/src/contrib/zooinspector/icons/fldr_obj.gif b/src/contrib/zooinspector/icons/fldr_obj.gif deleted file mode 100644 index 51e703b1b9c671baa2be0f6525edb3b69403370d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216 zcmZ?wbhEHb6krfwIKsg2wj(b5!P~sjn^4pBFDXFJ7TPNb)}zFdzZN zpDc_F4AKlbATf}g46LCK)cR5~=VfGBty!EmMJ+qTijl!k<;;`cOWYDXbj&=28jDUI znIPA;;-J&oD09KqYe6rnb~B%5E3$|tF?k!UrOe@6&xDe6Z(8Y4cFg@2oFwXHJRx@P^{Hj-{g3x~&Wh)&R@kLgN4c diff --git a/src/contrib/zooinspector/icons/launch_run.gif b/src/contrib/zooinspector/icons/launch_run.gif deleted file mode 100644 index 57f410224cf0e125fefdfbfd424b34ca32650ac4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 379 zcmZ?wbhEHb6krfwxXQqw(BP;BL>-Qf6T{aP*iM?lCLeYNChZ zME|t4Kq*J#ax2FU508od{@|VOltj+J(UOww!N5}U3iQCI3ZqHwGu%qFO+M$;f|G|KP zbfEZ?g^__lk3k2d733!dwiyo73p{kh9NV1~0xTMO)-@+?HCpL1C+*_|jkQs$L%3SK zWZD#%Uw+AK^#3sVq2$yax7_cq>g;#(C^lABHOjI%u}OEy<@M@F%Q8AK@K5dOm7mJO z;KV30f4Z*Rd;#G}fjokO)8$r)3o+Lza0p5E$no(>iVJg;1ab?@$%#scNeOe;IkR(c Y3G?y_b8(z@@nG_1@(Z}@>d0UX09ibCp#T5? diff --git a/src/contrib/zooinspector/icons/launch_stop.gif b/src/contrib/zooinspector/icons/launch_stop.gif deleted file mode 100644 index fbf1686e4b47fd4e9fe639c6b6d7bb49ca09b706..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 916 zcma)5Ur1A76hFJWvg?r8zLu)g97e2UJ)cYo*m&hPxr`ObGw zHlGlVw3)yJ-2jSHPy|sC-HKbvwFeR=yJC0B4q@ICkR?%Wb+3=dWKoCD>tJZN&Y})vc0? zTOEK!!^&%5<_m>7ZC_W>bffI*0Q3M>?MO!kHZXwP0@Ip=?UVM1>UuGCF{Xf8sWA(3 z)Kjm6bOcEU|GOtg`5x>V>q(97$0M}V=%K74XBDyf1rJb_8f)3XjPuJ9KnQ?UaVC_s zI}OT#>bSLH<}0q*A08y4I)F~U@`hh_BjWqhz;@}spU0B0JaRKb20rPPmvrzs9h@Tm z1Ot1pchVsPFe=U=z6~%Txx#KZA-Ifg}E3bkV~vIf*ZhHQ@NZ<>v6tiSIG- zJpteVwvnz-wlM@ySr)le222^KbHuRX_{y85@o6sN&lpg|KRX{GE~iCqrTN3WZDOeH zD$FI%!P#(rsFZ_rp2k-}g#b=1?~2ho0qqqLoD>;I_~1$d9PEONHF*}VfWSVLrv4Ma zU5C9K?;ejP?{V90ebMO9v2L@mwpEFe)rKmPps{rBguKlfhzc=G1w^LIbre)@Ii`S<%Te>{Bk z12|TpyR{>mrI{2JXHL%C7O09Mu>554H9&1sF`=rV`q@7GGB8G zKfk`Pt2BF0FFPY+r;7~t)M?XXTxEIZ%$>s~>nhH=Wa(0IS4pnbYt~4*N^x%5yop81 WRg`1b?%krUA_op05^-{5um%7kHK6(c diff --git a/src/contrib/zooinspector/icons/refresh.gif b/src/contrib/zooinspector/icons/refresh.gif deleted file mode 100644 index 3ca04d06ff9c811a6f791d029a802a7be4681ff4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327 zcmZ?wbhEHb6krfwxXQrrJWa!4-LeYm0=|77MK@5?WI%v@BcT|Ns9CR0WDZSr{1@R2g(Yrh@#$z?O1g zQh|q#RHw+PiyoXB)1AE)3U1~skT{fJ?U6BUYICAZn$5HYp3GD7dKS#fQdLEHHy z6I^aRo~tUt;P2kV6w~t;=?ud9vy7?cE^q z*1q%4_Md-x_|o&km!6%t_VVPl7w2!kK6mTY)%$O+-hXrZ@w-PaKBPA<&u(2-(6RIc zNcPq9H=oX5zOnP@<=ao*J$&)u*_+SB-Al@Q7r*)NC8Bg+ zz;2_n+r|KMbN2{G6a4_&{kGN)4gvc5juAGR#sRMOu1@w&t^vlzRuN`KMgigX?uQ4k zYgk5@s;SFPcrC52W*VWRCnlyRuKMMRsEVpigru?tONzsU2OVNcZfg!)N^s&3;9>A! zDQIQv=+@exGeO}YGc&uwQjf$B2b)>gB6wUn7Bn7go*uf&AmXGW<1{9LBBLK|is)RRH2yZMG-dG{LyHRvkqv*C;prFW}Cb1QH0&9u|R~HDbFBMu_B(%Cf zXj8fH`cmQLIReYF1^)m4&p+|z+lLr1JVKV69Zf3ff)rJI#S8AmOS&g!objY zXp*F{?P9eo9}@`oK{Iw9&}i^SBU4D}c3FLN?5SiQ`V!5RSYo{04T diff --git a/src/contrib/zooinspector/icons/search_prev.gif b/src/contrib/zooinspector/icons/search_prev.gif deleted file mode 100644 index 07164754e5ca231666d9daf7fa4a9d12e378a52a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323 zcmZ?wbhEHb6krfwxXQrr@9+D6zn=X4cJ<${r+>d*`SKk--`OT*-X9 diff --git a/src/contrib/zooinspector/icons/trash.gif b/src/contrib/zooinspector/icons/trash.gif deleted file mode 100644 index 5f47780f0e61161f1fb21701d68f697fe80cbec9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132 zcmV-~0DJ#ONk%w1VGsZi0Kx|VhLob9uDr9s&%Mjp$JFB5==JpZ{{R30A^8LW000L7 zEC2ui01yBW0009?c&gC8kOf0c0P(oZ7pO)d76c&(n8vM*SB<2ZGNDimwh*AuSJ=4U mZlJf6sN=bQN1>AjWhS3M!*M diff --git a/src/contrib/zooinspector/ivy.xml b/src/contrib/zooinspector/ivy.xml deleted file mode 100644 index bf782090491..00000000000 --- a/src/contrib/zooinspector/ivy.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - ZooInspector - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/contrib/zooinspector/lib/jtoaster-1.0.4.jar b/src/contrib/zooinspector/lib/jtoaster-1.0.4.jar deleted file mode 100644 index aaf3f6ea06f4b66fbe1719f63dd8f68e6ed05193..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14975 zcma)j1CS-#)@IqZZ5v&-ZQHhO+vswaZQHhOqpP~AOVj<{%-p^==700niHyh-=j6)0 zkSq88_P6AvfI(0IAbx#Tnn;)d{ty%Z5C9nwWdT|VSy6f!0a*!A5hZ0h8PSh%0DuRX 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 - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

    Eclipse Public License - v 1.0

    - -

    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

    - -

    1. DEFINITIONS

    - -

    "Contribution" means:

    - -

    a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

    -

    b) in the case of each subsequent Contributor:

    -

    i) changes to the Program, and

    -

    ii) additions to the Program;

    -

    where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

    - -

    "Contributor" means any person or entity that distributes -the Program.

    - -

    "Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

    - -

    "Program" means the Contributions distributed in accordance -with this Agreement.

    - -

    "Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

    - -

    2. GRANT OF RIGHTS

    - -

    a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

    - -

    b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

    - -

    c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

    - -

    d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

    - -

    3. REQUIREMENTS

    - -

    A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

    - -

    a) it complies with the terms and conditions of this -Agreement; and

    - -

    b) its license agreement:

    - -

    i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

    - -

    ii) effectively excludes on behalf of all Contributors -all liability for damages, including direct, indirect, special, -incidental and consequential damages, such as lost profits;

    - -

    iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

    - -

    iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

    - -

    When the Program is made available in source code form:

    - -

    a) it must be made available under this Agreement; and

    - -

    b) a copy of this Agreement must be included with each -copy of the Program.

    - -

    Contributors may not remove or alter any copyright notices contained -within the Program.

    - -

    Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

    - -

    4. COMMERCIAL DISTRIBUTION

    - -

    Commercial distributors of software may accept certain -responsibilities with respect to end users, business partners and the -like. While this license is intended to facilitate the commercial use of -the Program, the Contributor who includes the Program in a commercial -product offering should do so in a manner which does not create -potential liability for other Contributors. Therefore, if a Contributor -includes the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and -indemnify every other Contributor ("Indemnified Contributor") -against any losses, damages and costs (collectively "Losses") -arising from claims, lawsuits and other legal actions brought by a third -party against the Indemnified Contributor to the extent caused by the -acts or omissions of such Commercial Contributor in connection with its -distribution of the Program in a commercial product offering. The -obligations in this section do not apply to any claims or Losses -relating to any actual or alleged intellectual property infringement. In -order to qualify, an Indemnified Contributor must: a) promptly notify -the Commercial Contributor in writing of such claim, and b) allow the -Commercial Contributor to control, and cooperate with the Commercial -Contributor in, the defense and any related settlement negotiations. The -Indemnified Contributor may participate in any such claim at its own -expense.

    - -

    For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

    - -

    5. NO WARRANTY

    - -

    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

    - -

    6. DISCLAIMER OF LIABILITY

    - -

    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

    - -

    7. GENERAL

    - -

    If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

    - -

    If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

    - -

    All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

    - -

    Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

    - -

    This Agreement is governed by the laws of the State of New York and -the intellectual property laws of the United States of America. No party -to this Agreement will bring a legal action under this Agreement more -than one year after the cause of action arose. Each party waives its -rights to a jury trial in any resulting litigation.

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

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

    -
    -

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

    -

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

    - - \ No newline at end of file diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java deleted file mode 100644 index cc2a4bd2e93..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui.nodeviewer; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import javax.swing.BorderFactory; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A node viewer for displaying the ACLs currently applied to the selected node - */ -public class NodeViewerACL extends ZooInspectorNodeViewer { - private ZooInspectorNodeManager zooInspectorManager; - private final JPanel aclDataPanel; - private String selectedNode; - - /** - * - */ - public NodeViewerACL() { - this.setLayout(new BorderLayout()); - this.aclDataPanel = new JPanel(); - this.aclDataPanel.setBackground(Color.WHITE); - JScrollPane scroller = new JScrollPane(this.aclDataPanel); - this.add(scroller, BorderLayout.CENTER); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * getTitle() - */ - @Override - public String getTitle() { - return "Node ACLs"; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * nodeSelectionChanged(java.util.Set) - */ - @Override - public void nodeSelectionChanged(List selectedNodes) { - this.aclDataPanel.removeAll(); - if (selectedNodes.size() > 0) { - this.selectedNode = selectedNodes.get(0); - SwingWorker>, Void> worker = new SwingWorker>, Void>() { - - @Override - protected List> doInBackground() - throws Exception { - return NodeViewerACL.this.zooInspectorManager - .getACLs(NodeViewerACL.this.selectedNode); - } - - @Override - protected void done() { - List> acls = null; - try { - acls = get(); - } catch (InterruptedException e) { - acls = new ArrayList>(); - LoggerFactory.getLogger().error( - "Error retrieving ACL Information for node: " - + NodeViewerACL.this.selectedNode, e); - } catch (ExecutionException e) { - acls = new ArrayList>(); - LoggerFactory.getLogger().error( - "Error retrieving ACL Information for node: " - + NodeViewerACL.this.selectedNode, e); - } - aclDataPanel.setLayout(new GridBagLayout()); - int j = 0; - for (Map data : acls) { - int rowPos = 2 * j + 1; - JPanel aclPanel = new JPanel(); - aclPanel.setBorder(BorderFactory - .createLineBorder(Color.BLACK)); - aclPanel.setBackground(Color.WHITE); - aclPanel.setLayout(new GridBagLayout()); - int i = 0; - for (Map.Entry entry : data.entrySet()) { - int rowPosACL = 2 * i + 1; - JLabel label = new JLabel(entry.getKey()); - JTextField text = new JTextField(entry.getValue()); - text.setEditable(false); - GridBagConstraints c1 = new GridBagConstraints(); - c1.gridx = 1; - c1.gridy = rowPosACL; - c1.gridwidth = 1; - c1.gridheight = 1; - c1.weightx = 0; - c1.weighty = 0; - c1.anchor = GridBagConstraints.NORTHWEST; - c1.fill = GridBagConstraints.BOTH; - c1.insets = new Insets(5, 5, 5, 5); - c1.ipadx = 0; - c1.ipady = 0; - aclPanel.add(label, c1); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 3; - c2.gridy = rowPosACL; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.NORTHWEST; - c2.fill = GridBagConstraints.BOTH; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - aclPanel.add(text, c2); - i++; - } - GridBagConstraints c = new GridBagConstraints(); - c.gridx = 1; - c.gridy = rowPos; - c.gridwidth = 1; - c.gridheight = 1; - c.weightx = 1; - c.weighty = 1; - c.anchor = GridBagConstraints.NORTHWEST; - c.fill = GridBagConstraints.NONE; - c.insets = new Insets(5, 5, 5, 5); - c.ipadx = 0; - c.ipady = 0; - aclDataPanel.add(aclPanel, c); - } - NodeViewerACL.this.aclDataPanel.revalidate(); - NodeViewerACL.this.aclDataPanel.repaint(); - } - }; - worker.execute(); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * setZooInspectorManager - * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) - */ - @Override - public void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - } - -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java deleted file mode 100644 index 9dd0f38f3b6..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui.nodeviewer; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.concurrent.ExecutionException; - -import javax.swing.JButton; -import javax.swing.JOptionPane; -import javax.swing.JScrollPane; -import javax.swing.JTextPane; -import javax.swing.JToolBar; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.inspector.gui.ZooInspectorIconResources; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A node viewer for displaying the data for the currently selected node - */ -public class NodeViewerData extends ZooInspectorNodeViewer { - private ZooInspectorNodeManager zooInspectorManager; - private final JTextPane dataArea; - private final JToolBar toolbar; - private String selectedNode; - - /** - * - */ - public NodeViewerData() { - this.setLayout(new BorderLayout()); - this.dataArea = new JTextPane(); - this.toolbar = new JToolBar(); - this.toolbar.setFloatable(false); - JScrollPane scroller = new JScrollPane(this.dataArea); - scroller - .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - this.add(scroller, BorderLayout.CENTER); - this.add(this.toolbar, BorderLayout.NORTH); - JButton saveButton = new JButton(ZooInspectorIconResources - .getSaveIcon()); - saveButton.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - if (selectedNode != null) { - if (JOptionPane.showConfirmDialog(NodeViewerData.this, - "Are you sure you want to save this node?" - + " (this action cannot be reverted)", - "Confirm Save", JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { - zooInspectorManager.setData(selectedNode, dataArea - .getText()); - } - } - } - }); - this.toolbar.add(saveButton); - - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * getTitle() - */ - @Override - public String getTitle() { - return "Node Data"; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * nodeSelectionChanged(java.util.Set) - */ - @Override - public void nodeSelectionChanged(List selectedNodes) { - if (selectedNodes.size() > 0) { - this.selectedNode = selectedNodes.get(0); - SwingWorker worker = new SwingWorker() { - - @Override - protected String doInBackground() throws Exception { - return NodeViewerData.this.zooInspectorManager - .getData(NodeViewerData.this.selectedNode); - } - - @Override - protected void done() { - String data = ""; - try { - data = get(); - } catch (InterruptedException e) { - LoggerFactory.getLogger().error( - "Error retrieving data for node: " - + NodeViewerData.this.selectedNode, e); - } catch (ExecutionException e) { - LoggerFactory.getLogger().error( - "Error retrieving data for node: " - + NodeViewerData.this.selectedNode, e); - } - NodeViewerData.this.dataArea.setText(data); - } - }; - worker.execute(); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * setZooInspectorManager - * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) - */ - @Override - public void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - } - -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java deleted file mode 100644 index d7e2b43258f..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui.nodeviewer; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.SwingWorker; - -import org.apache.zookeeper.data.Stat; -import org.apache.zookeeper.inspector.logger.LoggerFactory; -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A node viewer for displaying the meta data for the currently selected node. - * The meta data is essentially the information from the {@link Stat} for the - * node - */ -public class NodeViewerMetaData extends ZooInspectorNodeViewer { - private ZooInspectorNodeManager zooInspectorManager; - private final JPanel metaDataPanel; - private String selectedNode; - - /** - * - */ - public NodeViewerMetaData() { - this.setLayout(new BorderLayout()); - this.metaDataPanel = new JPanel(); - this.metaDataPanel.setBackground(Color.WHITE); - JScrollPane scroller = new JScrollPane(this.metaDataPanel); - this.add(scroller, BorderLayout.CENTER); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * getTitle() - */ - @Override - public String getTitle() { - return "Node Metadata"; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * nodeSelectionChanged(java.util.Set) - */ - @Override - public void nodeSelectionChanged(List selectedNodes) { - this.metaDataPanel.removeAll(); - if (selectedNodes.size() > 0) { - this.selectedNode = selectedNodes.get(0); - SwingWorker, Void> worker = new SwingWorker, Void>() { - - @Override - protected Map doInBackground() throws Exception { - return NodeViewerMetaData.this.zooInspectorManager - .getNodeMeta(NodeViewerMetaData.this.selectedNode); - } - - @Override - protected void done() { - Map data = null; - try { - data = get(); - } catch (InterruptedException e) { - data = new HashMap(); - LoggerFactory.getLogger().error( - "Error retrieving meta data for node: " - + NodeViewerMetaData.this.selectedNode, - e); - } catch (ExecutionException e) { - data = new HashMap(); - LoggerFactory.getLogger().error( - "Error retrieving meta data for node: " - + NodeViewerMetaData.this.selectedNode, - e); - } - NodeViewerMetaData.this.metaDataPanel - .setLayout(new GridBagLayout()); - JPanel infoPanel = new JPanel(); - infoPanel.setBackground(Color.WHITE); - infoPanel.setLayout(new GridBagLayout()); - int i = 0; - int rowPos = 0; - for (Map.Entry entry : data.entrySet()) { - rowPos = 2 * i + 1; - JLabel label = new JLabel(entry.getKey()); - JTextField text = new JTextField(entry.getValue()); - text.setEditable(false); - GridBagConstraints c1 = new GridBagConstraints(); - c1.gridx = 0; - c1.gridy = rowPos; - c1.gridwidth = 1; - c1.gridheight = 1; - c1.weightx = 0; - c1.weighty = 0; - c1.anchor = GridBagConstraints.WEST; - c1.fill = GridBagConstraints.HORIZONTAL; - c1.insets = new Insets(5, 5, 5, 5); - c1.ipadx = 0; - c1.ipady = 0; - infoPanel.add(label, c1); - GridBagConstraints c2 = new GridBagConstraints(); - c2.gridx = 2; - c2.gridy = rowPos; - c2.gridwidth = 1; - c2.gridheight = 1; - c2.weightx = 0; - c2.weighty = 0; - c2.anchor = GridBagConstraints.WEST; - c2.fill = GridBagConstraints.HORIZONTAL; - c2.insets = new Insets(5, 5, 5, 5); - c2.ipadx = 0; - c2.ipady = 0; - infoPanel.add(text, c2); - i++; - } - GridBagConstraints c = new GridBagConstraints(); - c.gridx = 1; - c.gridy = rowPos; - c.gridwidth = 1; - c.gridheight = 1; - c.weightx = 1; - c.weighty = 1; - c.anchor = GridBagConstraints.NORTHWEST; - c.fill = GridBagConstraints.NONE; - c.insets = new Insets(5, 5, 5, 5); - c.ipadx = 0; - c.ipady = 0; - NodeViewerMetaData.this.metaDataPanel.add(infoPanel, c); - NodeViewerMetaData.this.metaDataPanel.revalidate(); - NodeViewerMetaData.this.metaDataPanel.repaint(); - } - }; - worker.execute(); - } - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer# - * setZooInspectorManager - * (org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager) - */ - @Override - public void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager) { - this.zooInspectorManager = zooInspectorManager; - } - -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java deleted file mode 100644 index 2185440ec4b..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui.nodeviewer; - -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.io.IOException; -import java.util.List; - -import javax.swing.JPanel; - -import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; - -/** - * A {@link JPanel} for displaying information about the currently selected - * node(s) - */ -public abstract class ZooInspectorNodeViewer extends JPanel implements - Transferable { - /** - * The {@link DataFlavor} used for DnD in the node viewer configuration - * dialog - */ - public static final DataFlavor nodeViewerDataFlavor = new DataFlavor( - ZooInspectorNodeViewer.class, "nodeviewer"); - - /** - * @param zooInspectorManager - */ - public abstract void setZooInspectorManager( - ZooInspectorNodeManager zooInspectorManager); - - /** - * Called whenever the selected nodes in the tree view changes. - * - * @param selectedNodes - * - the nodes currently selected in the tree view - * - */ - public abstract void nodeSelectionChanged(List selectedNodes); - - /** - * @return the title of the node viewer. this will be shown on the tab for - * this node viewer. - */ - public abstract String getTitle(); - - /* - * (non-Javadoc) - * - * @see - * java.awt.datatransfer.Transferable#getTransferData(java.awt.datatransfer - * .DataFlavor) - */ - public Object getTransferData(DataFlavor flavor) - throws UnsupportedFlavorException, IOException { - if (flavor.equals(nodeViewerDataFlavor)) { - return this.getClass().getCanonicalName(); - } else { - return null; - } - } - - /* - * (non-Javadoc) - * - * @see java.awt.datatransfer.Transferable#getTransferDataFlavors() - */ - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] { nodeViewerDataFlavor }; - } - - /* - * (non-Javadoc) - * - * @seejava.awt.datatransfer.Transferable#isDataFlavorSupported(java.awt. - * datatransfer.DataFlavor) - */ - public boolean isDataFlavorSupported(DataFlavor flavor) { - return flavor.equals(nodeViewerDataFlavor); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((getTitle() == null) ? 0 : getTitle().hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ZooInspectorNodeViewer other = (ZooInspectorNodeViewer) obj; - if (getClass().getCanonicalName() != other.getClass() - .getCanonicalName()) { - return false; - } - if (getTitle() == null) { - if (other.getTitle() != null) - return false; - } else if (!getTitle().equals(other.getTitle())) - return false; - return true; - } -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java deleted file mode 100644 index cbe9c20fb1a..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.logger; - -import org.apache.log4j.Logger; - -/** - * Provides a {@link Logger} for use across the entire application - * - */ -public class LoggerFactory -{ - private static final Logger logger = Logger.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$ - - /** - * @return {@link Logger} for ZooInspector - */ - public static Logger getLogger() - { - return logger; - } - -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java deleted file mode 100644 index f174f8a29c2..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/NodeListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.manager; - -import java.util.Map; - -/** - * A Listener for Events on zookeeper nodes - */ -public interface NodeListener { - /** - * @param nodePath - * - the path of the node - * @param eventType - * - the event type - * @param eventInfo - * - a {@link Map} containing any other information about this - * event - */ - public void processEvent(String nodePath, String eventType, - Map eventInfo); -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java deleted file mode 100644 index 9ebbd952041..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/Pair.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.manager; - -/** - * A utility class for storing a pair of objects - * - * @param - * @param - */ -public class Pair { - private K key; - private V value; - - /** - * @param key - * @param value - */ - public Pair(K key, V value) { - this.key = key; - this.value = value; - } - - /** - * - */ - public Pair() { - // Do Nothing - } - - /** - * @return key - */ - public K getKey() { - return key; - } - - /** - * @param key - */ - public void setKey(K key) { - this.key = key; - } - - /** - * @return value - */ - public V getValue() { - return value; - } - - /** - * @param value - */ - public void setValue(V value) { - this.value = value; - } - - @Override - public String toString() { - return "Pair [" + key + ", " + value + "]"; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Pair other = (Pair) obj; - if (key == null) { - if (other.key != null) - return false; - } else if (!key.equals(other.key)) - return false; - if (value == null) { - if (other.value != null) - return false; - } else if (!value.equals(other.value)) - return false; - return true; - } - -} diff --git a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java b/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java deleted file mode 100644 index e9c7c1d99db..00000000000 --- a/src/contrib/zooinspector/src/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.manager; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import javax.swing.JComboBox; -import javax.swing.JTextField; - -/** - * A Manager for all interactions between the application and the Zookeeper - * instance - */ -public interface ZooInspectorManager extends ZooInspectorNodeManager, - ZooInspectorNodeTreeManager { - - /** - * @param connectionProps - * @return true if successfully connected - */ - public boolean connect(Properties connectionProps); - - /** - * @return true if successfully disconnected - */ - public boolean disconnect(); - - /** - * @return a {@link Pair} containing the following: - *

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

- - - -
- - - - - - - - - - - - -
-